MyBatis

MyBatis是什么

 MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架,它是一个轻量级ORM应用框架。MyBatis使用简单的XML或注解用于配置和原始映射,将接口和java的POJOs(普通的java对象)映射成数据中的记录。它保留了SQL语句并且使SQL语句和应用程序分离。
 MyBatis是一个半自动的框架,之所以称它为半自动,是用为它需要手动配置POJO,SQL和映射关系,不像Hibernate只需要提供POJO和映射关系即可。
 Mybatis需要提供的映射文件包含了一下三个部分:sql,映射规则,pojo。在Mybatis里面你需要自己编写sql,虽然比Hibernate配置多,但是Mybatis可以配置动态sql,解决了hibernate表名根据时间变化,不同条件下不一样的问题,同时你也可以对sql进行优化,通过配置决定你的sql映射规则,也能支持存储过程,所以对于一些复杂和需要优化性能的sql查询它就更加方便。Mybatis几乎可以做到jdbc所有能做到的事情。

MyBatis什么时候使用

 MyBaties的SQL语句是由你编写的,所以Mybaties更有利于SQL的优化。如果项目的数据量比较大,并且效率要求比较高,则建议使用MyBatis框架。

Mybatis如何使用

导入jar包

 运用框架首先不必多说的就是要先导入框架所依赖jar包,点击链接下载mybatis的jar包。以为是数据持久层框架所以还需导入访问数据库所依赖的jar包。比如mysql.jar

创建数据库对象

 建立关系型映射之前,我们先需要建立表。

1
2
3
4
5
6
7
8
CREATE TABLE `students` (
`student_id` int(11) NOT NULL AUTO_INCREMENT,
`student_name` varchar(20) NOT NULL,
`student_sex` char(1) DEFAULT NULL,
`student_birthday` date DEFAULT NULL,
`group_id` varchar(20) DEFAULT NULL,
PRIMARY KEY (`student_id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;

建立POJO

 建立对象关系型映射之前,当然对象是必不可少的。需要注意的是,写对象属性的时候,属性名中不可以有下划线。并且最好用引用类型,不要用基本数据类型,这样可以提高代码效率。(因为代码比较简单,就不做注释了。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/**
* @author haozai
* @date 2018年12月5日
*/
package com.aowin.testmybatis.student.pojo;

import java.io.Serializable;
import java.sql.Date;

/**
* 学生基本信息
* @author haozai
* @date 2018年12月5日 上午9:40:49
*/
public class Students implements Serializable {

private Integer studentid;
private String studentname;
private String studentsex;
private Date studentbirthday;
private String groupid;

public Students() {

}

public int getStudentid() {
return studentid;
}

public void setStudentid(Integer studentid) {
this.studentid = studentid;
}

public String getStudentname() {
return studentname;
}

public void setStudentname(String studentname) {
this.studentname = studentname;
}

public String getStudentsex() {
return studentsex;
}

public void setStudentsex(String studentsex) {
this.studentsex = studentsex;
}

public Date getStudentbirthday() {
return studentbirthday;
}

public void setStudentbirthday(Date studentbirthday) {
this.studentbirthday = studentbirthday;
}

public String getGroupid() {
return groupid;
}

public void setGroupid(String groupid) {
this.groupid = groupid;
}

@Override
public String toString() {
return "Students [studentid=" + studentid + ", studentname=" + studentname + ", studentsex=" + studentsex
+ ", studentbirthday=" + studentbirthday + ", groupid=" + groupid + "]";
}

}

创建映射文件

 在POJO同目录下创建一个和POJO名相同的xml文件——Students.xml,写映射文件前,需要先导入DTD约束。(因为篇幅比较大,这里我们只介绍DML语句,DQL语句的介绍请点击这里)

DML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?xml version="1.0" encoding="UTF-8" ?>   
<!DOCTYPE mapper
PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
"http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">

<!-- mapper 为根元素节点, 一个namespace对应一个dao -->
<mapper namespace="com.aowin.testmybatis.student.pojo.Students">

<insert
<!-- 1. id (必须配置)
id是命名空间中的唯一标识符,可被用来代表这条语句。
一个命名空间(namespace) 对应一个dao接口,
这个id也应该对应dao里面的某个方法(相当于方法的实现),因此id 应该与方法名一致 -->


<!-- 2. parameterType (可选配置, 默认为mybatis自动选择处理)
将要传入语句的参数的完全限定类名或别名, 如果不配置,mybatis会通过ParameterHandler 根据参数类型默认选择合适的typeHandler进行处理
parameterType 主要指定参数类型,可以是int, short, long, string等类型,也可以是复杂类型(如对象) -->


<!-- 3. flushCache (可选配置,默认配置为true)
将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:true(对应插入、更新和删除语句) -->


<!-- 4. statementType (可选配置,默认配置为PREPARED)
STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 -->


<!-- 5. keyProperty (可选配置, 默认为unset)
(仅对 insert 和 update 有用)唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey 子元素设置它的键值,默认:unset。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。 -->


<!-- 6. keyColumn (可选配置)
(仅对 insert 和 update 有用)通过生成的键值设置表中的列名,这个设置仅在某些数据库(像 PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。 -->


<!-- 7. useGeneratedKeys (可选配置, 默认为false)
(仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系数据库管理系统的自动递增字段),默认值:false。 -->


<!-- 8. timeout (可选配置, 默认为unset, 依赖驱动)
这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。 -->
>

<update>

<delete>
<!--update和delete标签和insert标签相似,有着相同的属性-->
</mapper>

 上面的代码只是简单的介绍了一下子标签的属性。真正使用的时候,还需要编写自己所需要的SQL语句。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?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="com.aowin.testmybatis.student.pojo.Students" >

<!-- 添加学生信息的执行语句 -->
<insert id="insertstudent" parameterType="com.aowin.testmybatis.student.pojo.Students"
useGeneratedKeys="true" keyProperty="studentid">
<!-- userGeneratedKeys:启动主键 keyProperty:主键属性 -->
<![CDATA[
insert into students (student_name,student_sex,student_birthday,group_id)
values(#{studentname},#{studentsex},#{studentbirthday},#{groupid})
]]>
</insert>

<!-- 修改学生信息 -->
<update id="updatestudent" parameterType="com.aowin.testmybatis.student.pojo.Students">
update students set student_name=#{studentname},student_sex=#{studentsex}
,student_birthday=#{studentbirthday},group_id=#{groupid}
where student_id=#{studentid}
</update>

<!-- 删除学生信息 -->
<delete id="deletestudent" parameterType="int">
delete from students
where student_id=#{studentid}
</delete>

</mapper>

其中将SQL语句用<![CDATA[]]>标签包裹,是为了防止它自己转义(如:>,<等)。在这里也可以编写对应的实体!
这里的parameterType设置成com.aowin.testmybatis.student.pojo.Students。不过,建议大家还是使用typeAlias进行配置吧。唯一需要说明的就是元素里面的useGeneratedKeys和keyProperties属性,这两个属性是用来获取数据库中的主键的。
在数据库里面经常性的会给数据库表设置一个自增长的列作为主键,如果我们操作数据库后希望能够获取这个主键该怎么弄呢?正如上面所述,如果是支持自增长的数据库,如mysql数据库,那么只需要设置useGeneratedKeys和keyProperties属性便可以了,但是对于不支持自增长的数据库(如oracle)该怎么办呢?
mybatis里面在元素下面提供了子元素用于帮助解决这个问题。来看下配置:

1
2
3
4
5
6
7
8
9
10
<selectKey
<!-- selectKey 语句结果应该被设置的目标属性。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。 -->
keyProperty="id"
<!-- 结果的类型。MyBatis 通常可以推算出来,但是为了更加确定写上也不会有什么问题。MyBatis 允许任何简单类型用作主键的类型,包括字符串。如果希望作用于多个生成的列,则可以使用一个包含期望属性的 Object 或一个 Map。 -->
resultType="int"
<!-- 这可以被设置为 BEFORE 或 AFTER。如果设置为 BEFORE,那么它会首先选择主键,设置 keyProperty 然后执行插入语句。如果设置为 AFTER,那么先执行插入语句,然后是 selectKey 元素 - 这和像 Oracle 的数据库相似,在插入语句内部可能有嵌入索引调用。 -->
order="BEFORE"
<!-- 与前面相同,MyBatis 支持 STATEMENT,PREPARED 和 CALLABLE 语句的映射类型,分别代表 PreparedStatement 和 CallableStatement 类型。 -->
statementType="PREPARED">
</selectKey>

针对不能使用自增长特性的数据库,可以使用下面的配置来实现相同的功能:

1
2
3
4
5
6
7
8
9
<insert id="insertUser" parameterType="com.dy.entity.User">
<!-- oracle等不支持id自增长的,可根据其id生成策略,先获取id -->

<selectKey resultType="int" order="BEFORE" keyProperty="id">
select seq_user_id.nextval as id from dual
</selectKey>
insert into students (student_id,student_name,student_sex,student_birthday,group_id)
values(#{id},#{studentname},#{studentsex},#{studentbirthday},#{groupid})
</insert>

接下来我们讲讲查询语句!

动态SQL

写完DML语句,可能很多人就会想到假如我同时要插入多条信息,或者删除多条信息,该怎么写SQL语句呢?别慌,该是动态SQL登场的时候了!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?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="com.aowin.testmybatis.student.pojo.Students" >
<!-- 动态SQL -->
<update id="updatestudentdy" parameterType="com.aowin.testmybatis.student.pojo.Students">
update students
<set>
<if test="studentname!=null and studentname!=''">
student_name=#{studentname},
</if>
<if test="studentsex!=null and studentsex!=''">
student_sex=#{studentsex},
</if>
<if test="studentbirthday!=null">
student_birthday=#{studentbirthday},
</if>
<if test="groupid!=null and groupid!=''">
group_id=#{groupid}
</if>
</set>
<where>
student_id=#{studentid}
</where>
</update>

<update id="updatestudentdy2" parameterType="com.aowin.testmybatis.student.pojo.Students">
update students
<trim prefix="set" suffix="where student_id=#{studnetid}" suffixOverrides=",">
<if test="studentname!=null and studentname!=''">
student_name=#{studentname},
</if>
<if test="studentsex!=null and studentsex!=''">
student_sex=#{studentsex},
</if>
<if test="studentbirthday!=null">
student_birthday=#{studentbirthday},
</if>
<if test="groupid!=null and groupid!=''">
group_id=#{groupid}
</if>
</trim>
</update>

</mapper>

这个自己拼接sql语句有些类似,在这里我们介绍一下几个标签:if,choose (when, otherwise),trim (where, set),foreach。
if:if标签一般用于非空验证,如上例,若id为空,if标签里的代码,将不会执行,反之,则会执行。
choose(when,otherwise):相当于switch(case,default) ,如上例,若title 为空,when标签里的代码,将不会执行,默认执行otherwise标签里面的代码。
trim (prefix, suffix,prefixoverride):

  • prefix:前缀
  • suffix:后缀
  • prefixoverride=“AND|OR”:去掉最前面的and或者or;
  • suffixoverride=“AND|OR”:去掉最后面的and或者or;

批量处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?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="com.aowin.testmybatis.student.pojo.Students" >
<!-- 批量处理添加,传进来的是List -->
<insert id="insertstudents" useGeneratedKeys="true" keyProperty="studentid">
insert into students (student_name,student_sex,student_birthday,group_id) values
<foreach collection="list" item="item" separator=",">
(#{item.studentname},#{item.studentsex},#{item.studentbirthday},#{item.groupid})
</foreach>
</insert>

<!-- 批量删除 传进来的是list集合-->
<delete id="deletestudents" >
delete from students where student_id in
<foreach collection="list" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</delete>

<!-- 批量删除 传进来的是数组-->
<delete id="deletestudents2">
delete from students where student_id in
<foreach collection="array" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</delete>
</mapper>

这里我们需要介绍一下foreach标签:
foreach:可迭代任何对象(如列表、集合等)和任何的字典或者数组对象传递给foreach作为集合参数,当使用可迭代对象或者数组时,index是当前迭代的次数,item的值是本次迭代获取的元素。当使用字典(或者Map.Entry对象的集合)时,index是键,item是值。

  • foreach元素的属性主要有 item,index,collection,open,separator,close。
  • collection标签可以填(’list’,’array’,’map’)。
  • item表示集合中每一个元素进行迭代时的别名;
  • index指 定一个名字,用于表示在迭代过程中,每次迭代到的位置;
  • open表示该语句以什么开始,
  • separator表示在每次进行迭代之间以什么符号作为分隔符;
  • close表示以什么结束。
    当有了动态的SQL之后,我们有时间还可能会做批量修改和批量添加的操作。

创建配置文件

所有的配置文件建议都放在resource目录下!

创建DB配置文件

因为要访问数据库需要配置数据库连接的基本信息,我们这里以mysql为例

1
2
3
4
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/testmybatis?useUnicode=true&characterEncoding=utf8
username=root
password=123

创建mybatisconfig.xml

配置文件主要分四块:

  • 添加数据库、日志配置文件
  • 类型别名设定
  • 搭建数据库连接池
  • 加载映射文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
<!-- 获取数据库连接基本信息,添加数据库配置文件的路径 -->
<properties resource="database.properties"></properties>

<!-- pojo的类型进行别名设置 -->
<typeAliases>
<typeAlias type="com.aowin.testmybatis.student.pojo.Students" alias="Students"/>
</typeAliases>

<!-- 搭建数据库连接池 -->
<environments default="mysql">
<environment id="mysql"><!-- 数据库类型 -->
<transactionManager type="JDBC" /><!-- 事务驱动,一般固定是JDBC -->
<dataSource type="POOLED"><!-- 连接池 -->
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
<!-- 配置最大连接数 -->
<property name="poolMaximumActiveConnections" value="80" />
<!-- 配置核心连接数 -->
<property name="poolMaximumIdleConnections" value="20" />
</dataSource>
</environment>
</environments>

<!-- 加载映射文件 -->
<mappers>
<mapper resource="com/aowin/testmybatis/student/pojo/Students.xml"></mapper>
</mappers>

</configuration>

DAO层的编写

这样我们所有的配置文件都写好了,只需要修改DAO层了。在这里我们就不将所有配置的方法都实现了,例举个别的方法,程序里面会写上比较详细的注释。
程序里面包含了一个工具类:MybatisSqlSessionFactory,在这里就不做详细讲解,它的作用就是得到SqlSession这个核心对象,以及关闭SqlSession。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
public class StudentDAOImpl implements StudentDAO {

private Logger log = Logger.getLogger(this.getClass());
private SqlSession ss;

public StudentDAOImpl() {

}

@Override
public boolean insertStduent(Students s) {
log.info("调用业务逻辑方法:insertStduent(Students s)");
boolean result = false;
SqlSession ss = MybatisSqlSessionFactory.getSqlSession();
try {
int i = ss.insert("insertstudent", s);
if (i > 0) {
result = true;
log.info("添加学生信息成功" + s);
}
} catch (Exception e) {
log.warn("添加学生信息失败:" + e);
} finally {
if (ss != null)
ss.close();
}

return result;
}

@Override
public boolean deleteStudent(Integer studentid) {
boolean result = false;
SqlSession ss = MybatisSqlSessionFactory.getSqlSession(false);
try {
int i = ss.delete("deletestudent");
if (i > 0) {
ss.commit();
result = true;
log.info("学生信息删除成功!");
} else {
ss.rollback();
log.info("学生信息删除失败!");
}
} catch (Exception e) {
log.warn("学生信息删除失败:" + e);
}

return result;
}

@Override
public boolean updateStudnet(Students s) {
log.info("调用业务逻辑方法:updateStudnet(Students s)");
boolean result = false;
SqlSession ss = MybatisSqlSessionFactory.getSqlSession();
try {
int i = ss.update("updatestudentdy", s);
if (i > 0) {
log.info("修改学生信息成功" + s);
result = true;
}
} catch (Exception e) {
log.info("修改学生信息失败:" + e);
}
MybatisSqlSessionFactory.closeSqlSession();
return result;
}

@Override
public int insertStudents(List<Students> list) {
log.info("调用业务逻辑方法:insertStduent(Students s)");
int i = 0;
SqlSession ss = MybatisSqlSessionFactory.getSqlSession(false);
try {
i = ss.insert("insertstudents", list);
if (i == list.size()) {
ss.commit();
log.info("添加学生信息成功" + list);
} else {
ss.rollback();
log.info("添加学生信息失败,数据回滚!");
}
} catch (Exception e) {
log.warn("添加学生信息失败:" + e);
} finally {
if (ss != null)
ss.close();
}

return i;
}

@Override
public int deleteStudents(List<Integer> list) {
log.info("调用业务逻辑方法:deleteStudents(List<Integer> list)");
int result = 0;
SqlSession ss = MybatisSqlSessionFactory.getSqlSession(false);
try {
int i = ss.delete("deletestudents", list);
if (i == list.size()) {
ss.commit();
result = i;
log.info(i + "行学生信息成功被删除!");
} else {
ss.rollback();
log.info("学生信息删除失败,数据回滚!");
}
} catch (Exception e) {
log.warn("学生信息删除失败:" + e);
}
MybatisSqlSessionFactory.closeSqlSession();
return result;
}
}

-------------本文结束感谢您的阅读-------------