Mybatis入门---dao开发和mapper代理开发

Posted mvcq

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis入门---dao开发和mapper代理开发相关的知识,希望对你有一定的参考价值。

在说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代理开发的主要内容,如果未能解决你的问题,请参考以下文章

mybatis入门-mapper代理原理

mybatis框架入门

Mybatis笔记 - Mapper动态代理开发方式

mybatis入门:jdbc的缺点

Mybatis框架 第一天

MyBatis开发Dao的原始Dao开发和Mapper动态代理开发