1. 程式人生 > >Mybatis入門---dao開發和mapper代理開發

Mybatis入門---dao開發和mapper代理開發

不能 模糊查詢 stream 根據id獲取 java代碼 tid aps 維護 nal

在說mabatis之前,先說說單獨使用jdbc編程的缺陷。

jdbc編程的大概流程大家都很清楚,基本分為以下幾步:

  1. 加載數據驅動
  2. 創建並獲取數據庫連接
  3. 創建jdbc statement對象
  4. 設置sql語句,並設置sql語句中的參數
  5. 通過statement執行sql並獲取結果
  6. 對執行的結果進行解析處理
  7. 釋放資源
技術分享圖片
 1     public static void main(String[] args) {
 2         Connection connection = null;
 3         PreparedStatement preparedStatement = null
; 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 }
View Code

問題總結如下:

  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代理開發