我們在實際開發中,越簡單越好,所以都是采用不寫Dao實現類的方式。不管是使用xml還是直接配置。
但是MyBatis是支持寫Dao實現類的
注意sqlSession是這里面的一個靈魂,有很多執行api
目錄結構:
方法:
/** * 用戶的持久層接口 */ public interface IUserDao { List<User> findAll(); }
實現類:
public class UserDaoImpl implements IUserDao { private SqlSessionFactory factory; //覆蓋掉默認構造函數,這樣就有了工廠,可以進一步創建對象 public UserDaoImpl(SqlSessionFactory factory){ this.factory = factory; } @Override public List<User> findAll() { //1.使用工廠創建SqlSession對象 SqlSession sqlSession = factory.openSession(); //2.使用sqlSession執行查詢所有方法(此處需要的參數:(String statement)從配置文件中獲取) namespace + id List<User> userList = sqlSession.selectList("com.toov5.dao.IUserDao.findAll"); //使用完后關閉掉 sqlSession.close(); return userList; } }
實體類:
public class User implements Serializable { private Integer id; private String username; private Date birthday; private String sex; private String address; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", birthday=" + birthday + ", sex='" + sex + '\'' + ", address='" + address + '\'' + '}'; } }
全局配置文件:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 配置環境 --> <environments default="mysql"> <!-- 配置mysql的環境--> <environment id="mysql"> <!-- 配置事務的類型--> <transactionManager type="JDBC"></transactionManager> <!-- 配置數據源(也叫連接池) --> <dataSource type="POOLED"> <!-- 配置連接數據庫的4個基本信息 --> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <!-- 指定映射配置文件的位置,映射配置文件指的是每個dao獨立的配置文件 --> <mappers> <!--resource目錄下 對應要創建相應的包 --> <mapper resource="com/toov5/dao/IUserDao.xml"/> </mappers> </configuration>
映射文件:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.toov5.dao.IUserDao"> <!--查詢所有--> <select id="findAll" resultType="com.toov5.entity.User"> select * from user; </select> </mapper>
測試類:
/** * MyBatis測試類 */ public class MyBatisTest { public static void main(String[] args) throws IOException { //1.讀取配置文件 InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); //2. 創建SqlSessonFactory工廠 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); //如何解析,如何封裝已經底層幫助實現了。細節封裝了。 SqlSessionFactory factory = builder.build(in); // SqlSessionFactory 是個接口,需要找實現。這個工廠是用來創建對象的,創建過程省略了 //3. 使用工廠生產SqlSession對象 SqlSession sqlSession = factory.openSession(); //不需要代理類了,SqlSession 是有查詢方法的.使用工廠創建Dao對象,傳入工廠 IUserDao userDao = new UserDaoImpl(factory); //5.使用代理對象執行方法 List<User> userList = userDao.findAll(); userList.stream().forEach( user ->{ System.out.println(user); } ); //6.釋放資源 in.close(); } }
Mybatis在用動態代理dao方式時候,也是找到對應的sql語句的。
執行結果,一模一樣。
引申
補充,對於絕對路徑和相對路徑,絕對路徑不准確,采用相對路徑。
相對路徑有兩種方案可以獲得。 1. 類加載器,只能讀取類路徑的配置文件 2. 使用ServletContext對象的getRealPath()
創建工廠,采用了構建這模式。隱藏了創建細節。使用戶直接調用方法就可以拿到對象了
使用工廠模式生產SqlSession。降低類間的依賴,解耦合。
創建dao接口實現類使用了代理模式。不修改源碼基礎上對已有的方法增強。
MyBatis原理分析:
MyBatis在使用代理Dao的方式實現增刪改查時候,做的什么事呢?
兩件事:
一: 創建對象
二: 在代理對象中調用selectList
從一開始就需要解析配置文件,
進而做如下操作:
1. 根據配置文件的信息創建Connection對象,注冊驅動,獲取連接
2. 獲取預處理對象PreparedStatement,此時需要SQL語句。 com.prepareStatement(sql)
3. 執行查詢 ResultSet resultSet = preparedStatement.executeQuery();
4.遍歷結果用於封裝
List<E> list = new ArrayList();
while(resultSet.next()){
E element = xxx; // 可以通過反射后去 class.forName("配置的全限定類名").newInstance(); 使用反射封裝
// 進行封裝。把每個rs的內容都添加到element中,把element加入到list中
list.add(element);
}
於是我們就可以把表的列名看成是實體類的屬性名稱,就可以使用反射的方式來根據名稱獲取每個屬性。
5.返回結果
綜上所述: 需要提供方法兩個信息
1. 連接信息
2. 映射信息,包含兩部分: 執行的sql語句; 封裝結果的實體類全限定類名。這兩個信息作為屬性定義對象。
String key : nameSpace+id
Mapper value : String的sql語句 方法的全類名
通過方法:
session.getMapper(IUserDao.class) 實現了代理對象的創建
根據dao接口的字節碼創建到的代理對象
public <T> getMapper(Class<T> daoInterfaceClass){ }
使用的的參數是 : 類加載器(與被代理對象使用相同的類加載器), Class數組:代理對象要實現接口的字節碼數組, 如何代理(Handler)
Proxy.newProxyInstance()
此方法的封裝:
s1.類加載器: 它使用的和被代理對象是相同的類加載器
s2.代理對象要實現的接口: 和被代理對象實現相同的接口
s3.如何代理: 它就是增強的方法,我們需要自己來提供
此處是一個InvocationHandler的接口,我們需要寫一個該接口的實現類
調用該類的selectList方法
自定義MyBatis能通過入門案例看到的類:
class Resources
class SqlSessionFactoryBuilder
interface SqlSessionFactory
此時的pom:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.test</groupId> <artifactId>mybatis</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!--<dependency>--> <!--<groupId>org.mybatis</groupId>--> <!--<artifactId>mybatis</artifactId>--> <!--<version>3.4.5</version>--> <!--</dependency>--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.12</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.6.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> </project>
構建者構建工廠,工廠生產SqlSession。SqlSession可以做的事情很多。
總結:讀取配置文件io流,解析出我們要的信息,交給構建者,構建者使用工具類,構建了工廠對象,工廠的openSession方法提供了sqlSession對象,sqlSession去干活。
下圖信息量很大: