Mybatis简单入门

 

1、Mybatis概述  

  MyBatis 是支持普通 SQL 查询(相比较于Hibernate的封装,Mybatis是半自动化的JDBC封装,一个特点就是Mybatis执行的SQL查询语句需要自己在配置文件中写),存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的 JDBC 代码和参数的手工设置以及对结果集的检索。MyBatis 可以使用简单的XML 或注解用于配置和原始映射,将接口和 Java 的 POJO(Plain Old Java Objects,普通的Java 对象)映射成数据库中的记录。

2、Mybatis原理解析

  下面以Mybatis简单的执行流程

  1、加载mybatis全局配置文件(数据源、mapper映射文件等),解析配置文件,MyBatis基于XML配置文件生成Configuration,和一个个MappedStatement(包括了参数映射配置、动态SQL语句、结果映射配置),其对应着<select | update | delete | insert>标签项。

  2、SqlSessionFactoryBuilder通过Configuration对象生成SqlSessionFactory,用来开启SqlSession。

  3、SqlSession对象完成和数据库的交互:

  a、用户程序调用mybatis接口层api(即Mapper接口中的方法)

  b、SqlSession通过调用api的Statement ID找到对应的MappedStatement对象

  c、通过Executor(负责动态SQL的生成和查询缓存的维护)将MappedStatement对象进行解析,sql参数转化、动态sql拼接,生成jdbc Statement对象

  d、JDBC执行sql。

  e、借助MappedStatement中的结果映射关系,将返回结果转化成HashMap、JavaBean等存储结构并返回。

   下面是Mybatis的框架原理图

   

3、Mybatis简单实例

   (1)导入相关jar包以及Mybatis运行环境核心jar包和连接数据库的包

  

  (2)创建一张简单的数据表

  

  (3)创建Java对象(PO类型)

 1 package cn.mybatis.po;
 2 
 3 public class User {
 4     private int id;
 5     private String username;
 6     private String password;
 7     private String address;
 8     private String sex;
 9 
10     public int getId() {
11         return id;
12     }
13 
14     public String getUsername() {
15         return username;
16     }
17 
18     public String getPassword() {
19         return password;
20     }
21 
22     public String getAddress() {
23         return address;
24     }
25 
26     public String getSex() {
27         return sex;
28     }
29 
30     public void setId(int id) {
31         this.id = id;
32     }
33 
34     public void setUsername(String username) {
35         this.username = username;
36     }
37 
38     public void setPassword(String password) {
39         this.password = password;
40     }
41 
42     public void setAddress(String address) {
43         this.address = address;
44     }
45 
46     public void setSex(String sex) {
47         this.sex = sex;
48     }
49 
50     @Override
51     public String toString() {
52         return "User{" +
53                 "id=" + id +
54                 ", username='" + username + '\'' +
55                 ", password='" + password + '\'' +
56                 ", address='" + address + '\'' +
57                 ", sex='" + sex + '\'' +
58                 '}';
59     }
60 }
User实体类

   (4)创建Mybatis核心配置文件(SqlMapConfig.xml)

  在核心配置文件配置连接数据库的相关信息,(如果是和Spring整合,则可以放在Spring配置文件中进行对数据库的配置)

 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     <!--加载资源文件-->
 7 <!--    <properties resource="jdbc.properties"></properties>
 8     &lt;!&ndash;settings配置LOG4J输出日志 &ndash;&gt;
 9     <settings>
10         <setting name="logImpl" value="LOG4J"/>
11     </settings>-->
12     <!--typeAliases配置包的别名-->
13 
14     <!--environments配置了数据库连接,配置了driver、url、username、password属性-->
15     <environments default="development">
16         <environment id="development">
17             <transactionManager type="JDBC">
18                 <!--<property name="" value="" />-->
19             </transactionManager>
20             <dataSource type="POOLED">
21                 <property name="driver" value="com.mysql.jdbc.Driver" />
22                 <property name="url" value="jdbc:mysql:///mybatis01" />
23                 <property name="username" value="root" />
24                 <property name="password" value="123" />
25             </dataSource>
26         </environment>
27     </environments>
28     <!--配置一个SQL语句和映射的配置文件-->
29     <mappers>
30         <mapper resource="UserMapper.xml" />
31     </mappers>
32 </configuration>
Mybatis核心配置文件

   (5)创建一个Mapper.xml文件,对应编写所需要的Sql查询操作

 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指定了命名空间-->
 6 <mapper namespace="test">
 7     <!--定义一个SELECT查询-->
 8     <!--parameterType:指定输入参数的类型-->
 9     <!--#{}表示占位符-->
10     <!--#{id}:其中的id表示的就是接受的输入参数,
11                 参数名称就是id,
12         这里指出:如果输入参数是简单类型,#{}中的参数名可以任意设置(value或者其他名称)-->
13     <!--resultType:指定输出类型(即指定输出结果所映射的Java对象类型)-->
14     <select id="findUserById" parameterType="int" resultType="cn.mybatis.po.User">
15         SELECT * FROM t_user WHERE id = #{id}
16     </select>
17 </mapper>
UserMapper配置文件

   (7)创建测试程序,对刚刚编写的select查询进行测试

 1 package cn.mybatis.first;
 2 
 3 import cn.mybatis.po.User;
 4 import org.apache.ibatis.io.Resources;
 5 import org.apache.ibatis.session.SqlSession;
 6 import org.apache.ibatis.session.SqlSessionFactory;
 7 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
 8 
 9 import java.io.IOException;
10 import java.io.InputStream;
11 
12 public class Test {
13 
14     public User findUserById() throws IOException {
15         //得到mybatis配置文件
16         String resource = "SqlMapConfig.xml";
17         //得到配置文件的文件流信息
18         InputStream inputStream = Resources.getResourceAsStream(resource);
19         //创建会话工厂 传入mybatis的配置文件信息
20         SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
21         //通过会话工厂得到SqlSession
22         SqlSession sqlSession = sqlSessionFactory.openSession();
23         //通过sqlSession来操作数据库
24         //第一个参数就是映射文件中statment的id:namespace +statment的id
25         //第二个参数就是制定映射文件中的parameterType类型的参数
26         User user = sqlSession.selectOne("test.findUserById",1);
27         //System.out.println(user);
28 
29         //释放会话资源
30 
31         try {
32             sqlSession.close();
33         } catch (Exception e) {
34             e.printStackTrace();
35         }
36         return user;
37     }
38 
39     public static void main(String[] args) {
40         // TODO Auto-generated method stub
41         Test test = new Test();
42 
43         try {
44             System.out.println(test.findUserById());
45         } catch (IOException e) {
46             e.printStackTrace();
47         }
48 
49     }
50 }
Test测试程序

   (8)加入Log4j日志文件

1 ### direct log messages to stdout ###
2 
3 log4j.rootLogger=DEBUG, stdout
4 
5 log4j.appender.stdout=org.apache.log4j.ConsoleAppender
6 log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
7 log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
Log4j.properties 

   (9)测试结果

  

 4.其他CRUD操作

  (1)insert操作

  在Mapper文件中添加响应的SQL配置,以及使用MySQL中的LAST_INSERT_ID()函数得到增加的数据的主键值

    <insert id="addUser" parameterType="cn.mybatis.po.User">
        <!--
        现在需要得到刚刚插入的记录中的主键值,只适用于自增主键的情况
        LAST_INSERT_ID()
        keyProperty:将查询到的主键值设置到parameterType指定对象中的那个属性
        order:指定相对于insert的顺序
        resultType:指定映射结果的结果类型
        -->
        <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
            SELECT LAST_INSERT_ID()
        </selectKey>
        INSERT INTO t_user(id,username,password,address,sex) VALUES(#{id},#{username},#{password},#{address}, #{sex});
    </insert>

  插入数据的日志信息,没有使用sqlSession.commit();之前的日志情况

  

  从上面的图中可以看出,没有添加commit的时候,事务进行了回滚,所以要想添加数据,需要自己手动提交(在没有整合Spring之前)

  

  附上insertUser的函数

 1     public void inserUser()  throws IOException {
 2         //得到配置文件的文件流信息
 3         InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
 4         SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
 5         SqlSession sqlSession = sqlSessionFactory.openSession();
 6 
 7         User user = new User("World","1234","武汉市","男");
 8         sqlSession.insert("test.addUser",user);
 9         System.out.println(user.getId());
10         sqlSession.commit();
11         //释放会话资源
12         try {
13             sqlSession.close();
14         } catch (Exception e) {
15             e.printStackTrace();
16         }
17     }
inserUser函数

  (2)模糊查询

  首先配置Mapper文件,${}和#{}的简单区别如下:

 1     <!--
 2         模糊查询可能会查询多条记录
 3         resultType:指定的就是查询结果对应的单条记录类型
 4         ${}:表示将输入的参数不加任何的修饰,直接作为字符串拼接在SQL中
 5         但是这样直接拼接,容易导致SQL注入的隐患
 6         ${value}中的value表示接受的输入参数,注意如果输入参数是简单类型,其中的形参只能用value
 7     -->
 8     <select id="findUserByUsername" parameterType="java.lang.String" resultType="cn.mybatis.po.User">
 9         SELECT * FROM t_user WHERE username LIKE '%${value}%'
10     </select>  

  

  使用查询的时候碰到一个小错误,由于之前测试的insert方法,其中在User实体类中添加了有参构造函数,所以出现了下面的错误,分析原因就是:使用Mybatis查询的时候需要在实体类中加入无参构造方法(当然如果实体类本身没有构造函数,就会是默认的无参构造函数)

   

   附上findByUsername的函数实现

 1     public void findUserByUsername()  throws IOException {
 2         //得到配置文件的文件流信息
 3         InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
 4         SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
 5         SqlSession sqlSession = sqlSessionFactory.openSession();
 6 
 7         List<User> userList = sqlSession.selectList("test.findUserByUsername","u");
 8         System.out.println(userList);
 9         //释放会话资源
10         try {
11             sqlSession.close();
12         } catch (Exception e) {
13             e.printStackTrace();
14         }
15     }
findByUsername函数实现

   (3)删除操作

  首先在Mapper中配置删除的操作

1     <delete id="deleteUser" parameterType="java.lang.Integer">
2         DELETE FROM t_user WHERE id = #{value}
3     </delete>

  运行测试程序,同insert中一样,需要手动提交事务,如下面所示

  

  最终结果:

  数据表中删除了编号为10的数据记录

  

  

5.细节整理

  (1)关于示例程序中一些相关类的理解

   a)SqlSessionFactoryBuilder

   用来创建SqlSessionFactory。因为SqlSessionFactory使用了单例模式,所以不需要使用单例模式来管理SqlSessionFactoryBuilder,只需要在创建SqlSessionFactory时 候使用一次就可以

   b)SqlSessionFactory

   会话工厂,用来创建SqlSession。可以使用单例模式来管理SqlSessionFactory这个会话工厂,工厂创建之后,就一直使用一个实例。

   c)SqlSession

   面向程序员的接口,提供了操作数据库的方法。SqlSession是线程不安全的(原因:在SqlSession实现类中除了接口中的操作数据库的方法之外,还有数据域的属性,比如说一些提交的数据等等,所以在多线程并发请求的时候,会导致线程不安全),所以我们可以将SqlSession使用在方法体里面,这样每个线程都有自己的方法,就不会冲突

  (2)Mybatis中mapper映射文件

  如同解释Mybatis执行原理的时候一样,Mapper映射文件中配置的Sql语句,实际上在执行的时候都被封装称为一个个MapperStatment对象,即Mapper映射文件是按照statment来管理不同的Sql。在编写程序的时候,我们在使用SqlSession其中的操作数据库的方法(selectOne,selectList等等)的时候,传入的参数除了实参(id,模糊查询的字符串等等)之外,还需要传入的就是相应的Sql位置,而Sql是被Statment管理,所以就是传入namespace+statmentId

  (3)占位符

   #{id}:其中的id表示的就是接受的输入参数,参数名称就是id,这里指出:如果输入参数是简单类型,#{}中的参数名可以任意设置(value或者其他名称)

   ${value}:表示将输入的参数不加任何的修饰,直接作为字符串拼接在SQL中但是这样直接拼接,容易导致SQL注入的隐患${value}中的value表示接受的输入参数,注意如果输入参数是简单类型,其中的形参只能用value

  (4)别名定义

      ①单个别名的定义

<typeAliases>
        <!--针对单个别名的定义-->
        <typeAlias type="cn.mybatis.po.User" alias="user"></typeAlias>
    </typeAliases>

       定义别名后的使用

  <select id="findUserByIdTest" parameterType="int" resultType="user">
        SELECT * FROM t_user WHERE id = #{id}
    </select>

      ②批量别名的定义

    <typeAliases>
        <!--批量别名定义:Mybatis在定义别名的时候会自动扫描包中的po类,自动的将别名定义为类名(首字母大写或者小写都可以)-->
        <package name="cn.mybatis.po"></package>
    </typeAliases>

  (5)在SqlMapConfig.xml中加载Mapper映射文件的时候,除了通过resource的方式,还可以使用mapper接口加载的方式来实现

      ①首先先注意一点:

      在配置mybatis-config.xml时,其中的节点是有顺序的,配置顺序依次为:

properties/settings/typeAliases/typeHandlers/objectFactory/objectWrapperFactory/plugins/environments/databaseIdProvider/mappers

      ②使用mapper加载的方式,要将mapper接口和mapper配置文件放在同一目录下面,并且文件名称一致,而且要遵循mapper代理的方式进行开发

    <mappers>
        <mapper class="cn.mybatis.mapper.UserMapper"></mapper>
    </mappers>

      

 

6.Mybatis开发dao方法简介

  (1)使用dao接口+实现类的方式

   a)首先编写接口,如同一般编写模式方式进行编写

 1 package cn.mybatis.dao;
 2 
 3 import cn.mybatis.po.User;
 4 
 5 /**
 6  * 原始Dao方式开发:dao接口+dao实现类的方式
 7  */
 8 public interface UserDao {
 9 
10     //根据id查询信息
11     public User findUserById(int id) throws Exception;
12     //添加信息
13     public void insertUser(User user) throws Exception;
14     //删除信息
15     public void deleteUser(int id) throws Exception;
16 }
dao接口

   b)然后编写接口实现

 1 package cn.mybatis.dao.daoImpl;
 2 
 3 import cn.mybatis.dao.UserDao;
 4 import cn.mybatis.po.User;
 5 import org.apache.ibatis.session.SqlSession;
 6 import org.apache.ibatis.session.SqlSessionFactory;
 7 import org.junit.Test;
 8 
 9 public class UserDaoImpl implements UserDao {
10 
11     //使用构造方法注入SqlSessionFactory
12     private SqlSessionFactory sqlSessionFactory;
13 
14     public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
15         this.sqlSessionFactory = sqlSessionFactory;
16     }
17 
18     @Override
19     @Test
20     public User findUserById(int id) throws Exception {
21         SqlSession sqlSession = sqlSessionFactory.openSession();
22 
23         User user = sqlSession.selectOne("test.findUserById",id);
24 
25         sqlSession.close();
26         return user;
27     }
28 
29     @Override
30     public void insertUser(User user) throws Exception {
31         SqlSession sqlSession = sqlSessionFactory.openSession();
32 //        User user1 = new User("test1","123","洪山区","男");
33         sqlSession.insert("test.findUserById",user);
34         sqlSession.commit();
35         sqlSession.close();
36     }
37 
38     @Override
39     public void deleteUser(int id) throws Exception {
40         SqlSession sqlSession = sqlSessionFactory.openSession();
41 
42         sqlSession.delete("test.findUserById",id);
43         sqlSession.commit();
44         sqlSession.close();
45     }
46 }
dao接口实现类

   c)Mapper配置文件和SqlConfig配置文件不变

   d)使用Junit进行测试

 1 package cn.mybatis.testdao;
 2 
 3 import cn.mybatis.dao.UserDao;
 4 import cn.mybatis.dao.daoImpl.UserDaoImpl;
 5 import cn.mybatis.po.User;
 6 import org.apache.ibatis.io.Resources;
 7 import org.apache.ibatis.session.SqlSessionFactory;
 8 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
 9 import org.junit.After;
10 import org.junit.Before;
11 import org.junit.Test;
12 
13 import java.io.InputStream;
14 
15 public class UserDaoImplTest {
16 
17     private SqlSessionFactory sqlSessionFactory;
18 
19     @Before
20     public void setUp() throws Exception {
21         InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
22         sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
23     }
24 
25     @Test
26     public void testFindUserById() throws Exception{
27         //创建UserDao的对象
28         UserDao userDao = new UserDaoImpl(sqlSessionFactory);
29 
30         //调用UserDao的方法
31         User user = userDao.findUserById(1);
32 
33         System.out.println(user );
34     }
35 }
Junit测试

      e)测试结果

  

   f)原始dao方法的问题

    ①dao接口实现中存在大量的模板方法(即很多重复性的代码 )

    ②调用SqlSession方法的时候将statmentid硬编码了

    ③条用SqlSession方法的时候传入的参数,由于使用泛型,所以在编译阶段不会报错(即使传入参数错误)

  (2)使用Mapper代理的方法(即只需要Mapper接口)

  (a)使用mapper方式的规范

    ①在使用mapper代理的方式中,namespace的值应该是mapper接口的路径

    ②在mapper.java接口文件中的接口方法名称和mapper.xml中的statment的id一致

    ③在mapper.java接口文件中的接口方法的输入参数和mapper.xml中的statment的parameterType一致

    ④在mapper.java接口文件中的接口方法的返回值类型和mapper.xml中的statment的resultType一致

  (b)查询、删除操作实例

    ①编写mapper.xml配置文件,其中包含select和delete的sql配置

<?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指定了命名空间-->
<!--在使用mapper代理的方式中,namespace的值应该是mapper接口的路径-->
<mapper namespace="cn.mybatis.mapper.UserMapper">
    <select id="findUserById" parameterType="int" resultType="cn.mybatis.po.User">
        SELECT * FROM t_user WHERE id = #{id}
    </select>

    <delete id="deleteUser" parameterType="java.lang.Integer">
        DELETE FROM t_user WHERE id = #{value}
    </delete>
</mapper>
mapper.xml配置文件

    ②编写mapper接口,按照mapper代理的方式开发规范来编写mapper的接口

 1 package cn.mybatis.testmapper;
 2 
 3 import cn.mybatis.mapper.UserMapper;
 4 import cn.mybatis.po.User;
 5 import org.apache.ibatis.io.Resources;
 6 import org.apache.ibatis.session.SqlSession;
 7 import org.apache.ibatis.session.SqlSessionFactory;
 8 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
 9 import org.junit.After;
10 import org.junit.Before;
11 import org.junit.Test;
12 
13 import java.io.InputStream;
14 
15 
16 public class UserMapperTest {
17 
18     private SqlSessionFactory sqlSessionFactory;
19 
20     @Before
21     public void setUp() throws Exception {
22         InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
23         sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
24     }
25 
26     @Test
27     public void testFindUserById() throws Exception{
28 
29         SqlSession sqlSession = sqlSessionFactory.openSession();
30         //得到UserMapper的代理对象
31         UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
32 
33         User user = userMapper.findUserById(9);
34 
35         System.out.println(user);
36     }
37 
38     @Test
39     public void testDeleteUser() throws Exception {
40         SqlSession sqlSession = sqlSessionFactory.openSession();
41         UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
42 
43         userMapper.deleteUser(9);
44         sqlSession.commit();
45     }
46 
47     @After
48     public void tearDown() throws Exception {
49     }
50 }
mapper接口

    ③Junit测试

 1 package cn.mybatis.testmapper;
 2 
 3 import cn.mybatis.mapper.UserMapper;
 4 import cn.mybatis.po.User;
 5 import org.apache.ibatis.io.Resources;
 6 import org.apache.ibatis.session.SqlSession;
 7 import org.apache.ibatis.session.SqlSessionFactory;
 8 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
 9 import org.junit.After;
10 import org.junit.Before;
11 import org.junit.Test;
12 
13 import java.io.InputStream;
14 
15 
16 public class UserMapperTest {
17 
18     private SqlSessionFactory sqlSessionFactory;
19 
20     @Before
21     public void setUp() throws Exception {
22         InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
23         sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
24     }
25 
26     @Test
27     public void testFindUserById() throws Exception{
28 
29         SqlSession sqlSession = sqlSessionFactory.openSession();
30         //得到UserMapper的代理对象
31         UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
32 
33         User user = userMapper.findUserById(8);
34 
35         System.out.println(user);
36     }
37 
38     @Test
39     public void testDeleteUser() throws Exception {
40         SqlSession sqlSession = sqlSessionFactory.openSession();
41         UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
42 
43         userMapper.deleteUser(8);
44     }
45 
46     @After
47     public void tearDown() throws Exception {
48     }
49 }
Junit测试

    ④查询结果展示

    

    ⑤删除结果展示

    

  

 

posted @ 2019-02-04 06:51  夏末秋涼  阅读(1072)  评论(2编辑  收藏  举报