mybatis入門案例分析
一、設計模式分析
public class MybatisTest {
public static void main(String[] args) throws Exception{
//1.讀取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.創建SqlSessionFactory工廠
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.使用工廠生產SqlSession對象
SqlSession session = factory.openSession();
//4.使用SqlSession創建Dao的代理對象
IUserDao userDao = session.getMapper(IUserDao.class);
//5.使用代理對象執行方法
List<User> users = userDao.findAll();
for(User user : users) {
System.out.println(user);
}
//6.釋放資源
session.close();
in.close();
}
}
1.讀取配置文件
在讀取文件時,通常有兩種方法,一種是採用絕對路徑,另一種是採用相對路徑。如果採用絕對路徑,其缺點爲不易遷移和部署,在開發時如果我們路徑爲“D:\SqlMapConfig.xml”,部署到服務器時,可能服務器上沒有D盤。相對路徑的缺點就在於,如果項目爲Web工程,部署之後src目錄就不存在了。因此在讀取配置文件時,只有兩種方法:
1.使用類加載器:只能讀取類路徑的配置文件。
2.使用SeverletContext對象的getPath()方法。
2.創建工廠
在創建工廠時,mybatis使用了構建者模式,builder就是構建者,把對象的創建細節隱藏,使用戶直接調用方法便可獲得對象。
3.生產SqlSession對象
生產SqlSession對象時使用了工廠模式,可以降低類間的依賴關係,便於之後對項目進行修改。
4.創建代理對象
創建DAO接口實現類的代理對象實現了使用了代理模式,這樣就不需要自己寫DAO實現類。
二、mybatis執行查詢所有的分析
1.普通的java實現數據庫查詢
1.註冊驅動,獲取connection對象
2.創建數據庫的執行sql的預處理對象
3.執行sql語句
4.封裝查詢結果
5.釋放資源
public class JDBCTest {
public static void main(String[] args) {
JDBCTest test=new JDBCTest();
test.firstJDBC();
}
public void firstJDBC(){
Connection connection=null;
PrepareStatement prepareStatement=null;
ResultSet resultSet=null;
try {
//1.register,註冊驅動
DriverManager.registerDriver(new Driver());
//2.得到數據連接。url格式:jdbc:mysql://主機IP:端口號/數據庫名?user=用戶名&password=密碼
//因爲MySQL安裝時,端口號默認設置的是3306,所以都是3306
String url="jdbc:mysql://localhost:3306/sport?user=root&password=12345678";
connection=DriverManager.getConnection(url);
//3.得到數據庫的執行sql對象
String sql="select * from player"; //SQL查詢語句
prepareStatement=connection.prepareStatement();
//4.執行語句
resultSet=prepareStatement.executeQuery();
while(resultSet.next()){
//取出查詢的信息
String name=resultSet.getString("player_name");
int age=resultSet.getInt("player_age");
int score=resultSet.getInt("player_score");
System.out.println("name="+name+",age="+age+",score="+score);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
//5.關閉資源
if(connection!=null){
try {
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(prepareStatement!=null){
try {
prepareStatement.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
2.mybatis中如何實現查詢
<!-- mybatis的主配置文件 -->
<configuration>
<!-- 配置環境 -->
<environments default="mysql">
<!-- 配置mysql的環境 -->
<environment id="mysql">
<!-- 配置事務的類型 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置數據源(連接池) -->
<dataSource type="POOLED">
<!-- 配置連接數據庫的四個基本信息 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="12345678"/>
</dataSource>
</environment>
</environments>
<!-- 指定映射配置文件的位置,映射配置文件指的是每個dao獨立的配置文件 -->
<mappers>
<mapper resource="dao/IUserDao.xml" />
</mappers>
</configuration>
1.創建connection對象。通過主被配置文件SqlMapConfig.xml,我們可以獲得連接數據庫的信息(驅動、路徑、用戶名和密碼)以及映射配置文件的位置。通過連接數據庫的信息,我們可以構建connection對象。
<mapper namespace="dao.IUserDao">
<!-- 配置查詢所有 -->
<select id="findAll" resultType="domain.User">
select * from user;
</select>
</mapper>
2.創建prepareStatement對象。通過映射配置文件的信息,我們可以獲得執行的SQL語句和封裝的實體類全限定類名,就可以創建prepareStatement對象。mybatis通過dom4j技術來解析xml文件獲取上述信息。
3.存儲解析結果。在獲取相關信息中後,mybatis需要將執行的SQL語句和封裝結果的實體類全限定類名組合起來定義成一個Mapper對象,每一個Mapper對象對應一個完整String類型的id(namespace+“.”+id)。例如本項目中的SQL語句爲“select * from user;”,封裝結果的實體類全限定類名爲“domain.user”,完整的id爲"dao.IUserDao.findAll",這些都是String類型的字段。
4.利用反射技術封裝。創建prePareStatement對象之後,通過resultSet = prepareStatement.executeQuery();語句我們可以獲得查詢結果集,在對查詢結果集進行封裝時。我們通過完整的id(即封裝結果的全限定類名),利用反射技術(Class.forName("domain.User").getDeclaredConstructor().newInstance();)即可進行封裝。由於實體類的屬性和數據庫表中的列名一致,可以把表的列名當作是實體類的屬性名稱,然後利用反射技術來根據名稱獲取每個屬性,並把值賦進去。反射參考鏈接。
三、創建代理對象的分析
//MybatisTest.class
IUserDao userDao = session.getMapper(IUserDao.class);
//DefaultSqlSession.class
public <T> T getMapper(Class<T> type) {
return this.configuration.getMapper(type, this);
}
//Configuration.class
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return this.mapperRegistry.getMapper(type, sqlSession);
}
//MapperRegistry.class
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}
//MapperProxyFactory.class
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
//Proxy.class
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
InvocationHandler h) {
Objects.requireNonNull(h);
final Class<?> caller = System.getSecurityManager() == null
? null
: Reflection.getCallerClass();
/*
* Look up or generate the designated proxy class and its constructor.
*/
Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
return newProxyInstance(caller, cons, h);
}
上述是MybatisTest類中,getMapper()的調用層級。可以看到最終調用的方法是public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h),這個方法有三個參數,分別是:
1.類加載器loader:使用和被代理對象相同的類加載器
2.代理對象要實現的接口字節碼數組interfaces:和被代理對象實現的是相同的接口
3.代理方式h:就是增強的方法,實際上就是一個InvocationHandler接口的實現類,在實現類中調用selectList方法(查詢所有方法)。
mybatis入門案例,可以參考我的上一篇博客:mybatis入門實例