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 }(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 <!–settings配置LOG4J输出日志 –>
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>(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>(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 }(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(9)测试结果

4.其他CRUD操作
(1)insert操作
在Mapper文件中添加响应的SQL配置,以及使用MySQL中的1469784函数得到增加的数据的主键值
<insert id="addUser" parameterType="cn.mybatis.po.User">
<!--
现在需要得到刚刚插入的记录中的主键值,只适用于自增主键的情况
1469784
keyProperty:将查询到的主键值设置到parameterType指定对象中的那个属性
order:指定相对于insert的顺序
resultType:指定映射结果的结果类型
-->
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
SELECT 1469784
</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 }(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 }(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 }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 }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 } 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接口,按照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 }③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 }④查询结果展示

⑤删除结果展示
