Mybatis入門---dao開發和mapper代理開發
在說mabatis之前,先說說單獨使用jdbc編程的缺陷。
jdbc編程的大概流程大家都很清楚,基本分為以下幾步:
- 加載數據驅動
- 創建並獲取數據庫連接
- 創建jdbc statement對象
- 設置sql語句,並設置sql語句中的參數
- 通過statement執行sql並獲取結果
- 對執行的結果進行解析處理
- 釋放資源
1 public static void main(String[] args) { 2 Connection connection = null; 3 PreparedStatement preparedStatement = nullView Code; 4 ResultSet resultSet = null; 5 6 try { 7 //加載數據庫驅動 8 Class.forName("com.mysql.jdbc.Driver"); 9 // 通過驅動管理類獲取數據庫鏈接 10 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "mysql");11 // 定義sql語句 ?表示占位符 12 String sql = "select * from user where username = ?"; 13 // 獲取預處理statement 14 preparedStatement = connection.prepareStatement(sql); 15 // 設置參數 16 preparedStatement.setString(1, "王五"); 17 // 向數據庫發出sql執行查詢,查詢出結果集18 resultSet = preparedStatement.executeQuery(); 19 // 遍歷查詢結果集 20 while(resultSet.next()){ 21 System.out.println(resultSet.getString("id")+" "+resultSet.getString("username")); 22 } 23 } catch (Exception e) { 24 e.printStackTrace(); 25 }finally{ 26 if(resultSet!=null){ 27 try { 28 resultSet.close(); 29 } catch (SQLException e) { 30 e.printStackTrace(); 31 } 32 } 33 if(preparedStatement!=null){ 34 try { 35 preparedStatement.close(); 36 } catch (SQLException e) { 37 e.printStackTrace(); 38 } 39 } 40 if(connection!=null){ 41 try { 42 connection.close(); 43 } catch (SQLException e) { 44 e.printStackTrace(); 45 } 46 } 47 48 } 49 50 }
問題總結如下:
1、 數據庫鏈接創建、釋放頻繁造成系統資源浪費從而影響系統性能。
2、 Sql語句在代碼中硬編碼(參數寫死),造成代碼不易維護,實際應用sql變化的可能較大,sql變動需要改變java代碼。
3、 使用preparedStatement向占有位符號傳參數存在硬編碼,因為sql語句的where條件不一定,可能多也可能少,修改sql還要修改代碼,系統不易維護。
4、 對結果集解析存在硬編碼(查詢列名),sql變化導致解析代碼變化,系統不易維護,如果能將數據庫記錄封裝成pojo對象解析比較方便。
由於這些問題的存在,mybatis對其進行了相應的處理:
1、在SqlMapConfig.xml中配置數據鏈接池,使用連接池管理數據庫鏈接。
2、將Sql語句配置在XXXXmapper.xml文件中與java代碼分離。
3、Mybatis自動將java對象映射至sql語句,通過statement中的parameterType定義輸入參數的類型。
4、Mybatis自動將sql執行結果映射至java對象,通過statement中的resultType定義輸出結果的類型。
它對jdbc的操作數據庫的過程進行封裝,使開發者只需要關註 SQL 本身,而不需要花費精力去處理例如註冊驅動、創建connection、創建statement、手動設置參數、結果集檢索等jdbc繁雜的過程代碼。Mybatis通過xml或註解的方式將要執行的各種statement(statement、preparedStatemnt、CallableStatement)配置起來,並通過java對象和statement中的sql進行映射生成最終執行的sql語句,最後由mybatis框架執行sql並將結果映射成java對象並返回。
mybatis框架基本內容
1. mybatis架構圖如下
2. mybatis配置
SqlMapConfig.xml,此文件作為mybatis的全局配置文件,配置了mybatis的運行環境等信息。mapper.xml文件即sql映射文件,文件中配置了操作數據庫的sql語句。此文件需要在SqlMapConfig.xml中加載。通過mybatis環境等配置信息構造SqlSessionFactory即會話工廠
由會話工廠創建sqlSession即會話,操作數據庫需要通過sqlSession進行。
mybatis底層自定義了Executor執行器接口操作數據庫,Executor接口有兩個實現,一個是基本執行器、一個是緩存執行器。
Mapped Statement也是mybatis一個底層封裝對象,它包裝了mybatis配置信息及sql映射信息等。mapper.xml文件中一個sql對應一個Mapped Statement對象,sql的id即是Mapped statement的id。
Mapped Statement對sql執行輸入參數進行定義,包括HashMap、基本類型、pojo,Executor通過Mapped Statement在執行sql前將輸入的java對象映射至sql中,輸入參數映射就是jdbc編程中對preparedStatement設置參數。
Mapped Statement對sql執行輸出結果進行定義,包括HashMap、基本類型、pojo,Executor通過Mapped Statement在執行sql後將輸出結果映射至java對象中,輸出結果映射過程相當於jdbc編程中對結果的解析處理過程。
3. mybatis的dao開發必備的內容
3.1 mybatis核心包、依賴包、數據驅動包。
3.2 日誌配置文件
3.3 SqlMapConfig.xml(名字隨意)作為全局配置文件
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE configuration 3 PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-config.dtd"> 5 <configuration> 6 <properties resource="jdbc.properties"></properties> 7 <environments default="development"> 8 <environment id="development"> 9 <transactionManager type="JDBC" /> 10 <dataSource type="POOLED"> 11 <property name="driver" value="${jdbc.driver}" /> 12 <property name="url" value="${jdbc.url}" /> 13 <property name="username" value="${jdbc.user}" /> 14 <property name="password" value="${jdbc.password}" /> 15 </dataSource> 16 </environment> 17 </environments> 18 <mappers> 19 <mapper resource="sqlMap/UserMapper.xml"/> 20 </mappers> 21 </configuration>View Code
3.4 pojo類
1 public class User { 2 3 private int id; 4 private String username; 5 private String address; 6 private Date birthday; 7 }View Code
3.5 映射文件(需要在全局配置文件SqlMapConfgi.xml進行加載,mappers的內容)
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE mapper 3 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 5 <mapper namespace="test"> 6 <!-- 根據id獲取用戶信息 --> 7 <select id="findUserById" parameterType="int" resultType="priv.cq.mybatis.po.User"> 8 select * from user where id=#{id} 9 </select> 10 </mapper>View Code
3.6 Dao接口
1 public interface IUserDao { 2 // 根據用戶id查詢用戶 3 public User findUserById(int id) throws Exception; 4 }View Code
3.7 Dao接口實現類
1 public class UserDaoImpl implements IUserDao{ 2 3 private SqlSessionFactory sqlSessionFactory; 4 5 public UserDaoImpl(SqlSessionFactory sqlSessionFactory) { 6 this.sqlSessionFactory = sqlSessionFactory; 7 } 8 @Override 9 public User findUserById(int id) throws Exception { 10 SqlSession session = sqlSessionFactory.openSession(); 11 User user = session.selectOne("test.findUserById", id); 12 session.close(); 13 return user; 14 } 15 }View Code
3.8 測試
1 public class IUserDaoTest { 2 private SqlSessionFactory sqlSessionFactory; 3 4 @Before 5 public void setUp() throws Exception { 6 String resource = "SqlMapConfig.xml"; 7 InputStream inputStream = Resources.getResourceAsStream(resource); 8 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 9 } 10 11 @Test 12 public void testFindUserById() throws Exception { 13 IUserDao userDao = new UserDaoImpl(sqlSessionFactory); 14 User user = userDao.findUserById(32); 15 System.out.println(user); 16 } 17 }View Code
註意的地方:
1. SqlSession
SqlSession中封裝了對數據庫的操作,如:查詢、插入、更新、刪除等。通過SqlSessionFactory創建SqlSession,而SqlSessionFactory是通過SqlSessionFactoryBuilder進行創建。同時SqlSession是一個面向用戶的接口, sqlSession中定義了數據庫操作,默認使用DefaultSqlSession實現類。
執行過程如下:
1) 加載數據源等配置信息(Environment environment = configuration.getEnvironment();)
2) 創建數據庫鏈接
3)創建事務對象
4)創建Executor,SqlSession所有操作都是通過Executor完成,mybatis源碼如下:
每個線程都應該有它自己的SqlSession實例。SqlSession的實例不能共享使用,它也是線程不安全的。因此最佳的範圍是請求或方法範圍。絕對不能將SqlSession實例的引用放在一個類的靜態字段或實例字段中。
2. sqlSessionFactory
SqlSessionFactory是一個接口,接口中定義了openSession的不同重載方法,SqlSessionFactory的最佳使用範圍是整個應用運行期間,一旦創建後可以重復使用,通常以單例模式管理SqlSessionFactory。
3. sqlSessionFactoryBuilder
sqlSessionFactoryBuilder用於創建SqlSessionFacoty,SqlSessionFacoty一旦創建完成就不需要SqlSessionFactoryBuilder了,因為SqlSession是通過SqlSessionFactory生產,所以可以將SqlSessionFactoryBuilder當成一個工具類使用,最佳使用範圍是方法範圍即方法體內局部變量。
Dao接口存在的不足之處:
1. Dao方法體存在重復代碼:通過SqlSessionFactory創建SqlSession,調用SqlSession的數據庫操作方法
本例中只有一個方法,如果有各種查詢、刪除、查找等方法,將會存在上述的冗余。
2. 調用sqlSession的數據庫操作方法需要指定statement的id,這裏存在硬編碼,不得於開發維護。
4. mybatis的mapper動態代理開發
4.1 Mapper接口
相當於Dao接口,由Mybatis框架根據接口定義創建接口的動態代理對象,代理對象的方法體同上邊Dao接口實現類方法。
Mapper接口開發需要遵循以下規範:
1)Mapper.xml文件中的namespace與mapper接口的類路徑相同。
2)Mapper接口方法名和Mapper.xml中定義的每個statement的id相同
3) Mapper接口方法的輸入參數類型和mapper.xml中定義的每個sql 的parameterType的類型相同
4) Mapper接口方法的輸出參數類型和mapper.xml中定義的每個sql的resultType的類型相同
1 public interface UserMapper { 2 // 根據用戶id查詢用戶 3 public User findUserById(int id) throws Exception; 4 // 根據用戶姓名模糊查詢用戶 5 public List<User> findUserByName(String name) throws Exception; 6 // 插入用戶 7 public void insertUser(User user) throws Exception; 8 // 刪除用戶 9 public void deleteUser(int i) throws Exception; 10 // 更新用戶 11 public void updateUser(User user) throws Exception; 12 }View Code
4.2 映射文件 mapper.xml
定義mapper映射文件UserMapper.xml,需要修改namespace的值為 UserMapper接口路徑。將UserMapper.xml放在classpath 下mapper目錄下。
1 <mapper namespace="priv.cq.mybatis.mapper.UserMapper"> 2 <select id="findUserById" parameterType="int" resultType="user"> 3 select * from user where id=#{id} 4 </select> 5 6 <select id="findUserByName" parameterType="java.lang.String" resultType="user"> 7 select * from user where username like ‘%${value}%‘ 8 </select> 9 10 <insert id="insertUser" parameterType="user"> 11 <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer"> 12 select LAST_INSERT_ID() 13 </selectKey> 14 insert into user(username,birthday,address)value(#{username},#{birthday},#{address}) 15 </insert> 16 17 <delete id="deleteUser" parameterType="int"> 18 delete from user where id=#{id} 19 </delete> 20 21 <update id="updateUser" parameterType="user"> 22 update user set username=#{username},birthday=#{birthday},address=#{address} where id=#{id} 23 </update> 24 </mapper>View Code
4.3 修改SqlMapConfig.xml文件
1 <mappers> 2 <mapper resource="mapper/UserMapper.xml"/> 3 </mappers>View Code
4.4 測試
1 public class IUserMapperTest { 2 3 private SqlSessionFactory sqlSessionFactory; 4 5 @Before 6 public void setUp() throws Exception { 7 String resource = "SqlMapConfig.xml"; 8 InputStream inputStream = Resources.getResourceAsStream(resource); 9 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 10 } 11 12 // 根據id查找用戶 13 @Test 14 public void testFindUserById() throws Exception { 15 // 獲取session 16 SqlSession sqlSession = sqlSessionFactory.openSession(); 17 // 獲取mapper接口的代理對象 18 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); 19 // 調用代理對象方法 20 User user = userMapper.findUserById(1); 21 System.out.println(user); 22 // 關閉sqlSession 23 sqlSession.close(); 24 } 25 26 // 根據用戶姓名模糊查詢用戶 27 @Test 28 public void testFindUserByName() throws Exception { 29 SqlSession sqlSession = sqlSessionFactory.openSession(); 30 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); 31 List<User> list = userMapper.findUserByName("曹乾"); 32 System.out.println(list); 33 sqlSession.close(); 34 } 35 36 // 插入用戶 37 @Test 38 public void testInsertUser() throws Exception { 39 SqlSession sqlSession = sqlSessionFactory.openSession(); 40 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); 41 42 User user = new User(); 43 user.setUsername("小王"); 44 user.setBirthday(new Date()); 45 user.setAddress("北京"); 46 47 userMapper.insertUser(user); 48 sqlSession.commit(); 49 sqlSession.close(); 50 } 51 52 // 根據id刪除用戶 53 @Test 54 public void testDeleteUser() throws Exception { 55 SqlSession sqlSession = sqlSessionFactory.openSession(); 56 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); 57 userMapper.deleteUser(39); 58 sqlSession.commit(); 59 sqlSession.close(); 60 } 61 62 // 根據id更新用戶(此id必須存在) 63 @Test 64 public void testUpdateUser() throws Exception { 65 SqlSession sqlSession = sqlSessionFactory.openSession(); 66 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); 67 68 User user = new User(); 69 user.setId(31); 70 user.setAddress("孫悟空"); 71 user.setBirthday(new Date()); 72 user.setUsername("花果山"); 73 74 userMapper.updateUser(user); 75 sqlSession.commit(); 76 sqlSession.close(); 77 } 78 }View Code
mapper動態代理中,動態代理對象調用sqlSession.selectOne()和sqlSession.selectList()是根據mapper接口方法的返回值決定,如果返回list則調用selectList方法,如果返回單個對象則調用selectOne方法。
工程結構如下:
兩種開發方法就簡單地介紹到此,只是一個簡單的demo,但大體流程已經很清楚了,在下次將把mybatis更高級的內容做一整理,比如mybatis的高級映射、緩存以及和spring的整合。
Mybatis入門---dao開發和mapper代理開發