MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
Object Relationship Mapping
如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml
文件中
1
2
3
4
5
|
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>
|
创建MyBatis配置文件
在resources目录下创建配置文件db/mybatis/mybatis-config.xml
这个路径可以改,不是固定的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
<?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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="org.h2.Driver"/> <!-- 指定你的数据库驱动 -->
<property name="url" value="jdbc:h2:file:./target/test"/> <!-- 指定你的数据库连接字符串 -->
<property name="username" value="root"/> <!-- 数据库用户名 -->
<property name="password" value="Jxi1Oxc92qSj"/> <!-- 数据库密码 -->
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="db/mybatis/mybatis-mapper.xml"/> <!-- sql映射文件 -->
</mappers>
</configuration>
|
配置日志框架
在mybatis-config.xml
添加settings
标签,并设置日志框架为 LOG4J
注意<settings>
标签必须放在<configuration>
标签之后,也就是<environments>
标签的上面
1
2
3
4
5
|
<configuration>
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
</configuration>
|
引入LOG4J
1
2
3
4
5
6
|
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
|
配置LOG4J
,在resources
目录下新建log4j.properties
,并写入如下配置,具体配置可以去LOG4J官网 查看
1
2
3
4
5
6
7
8
|
# 全局日志配置,设置日志级别为DEBUG级别
log4j.rootLogger=DEBUG, stdout
# 为指定的 MyBatis Mapper 设置日志
log4j.logger.db.mybatis.mybatis-mapper=TRACE
# 控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
|
创建Mapper(Sql关系映射) db/mybatis/mybatis-mapper.xml
,这个文件位置也是可以改的
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<?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.github.wjinlei.mapper"> <!-- 命名空间,用于定位,这个名字随便取 -->
<!-- 简单的SELECT语句
id 用于表示这个sql配置,通过命名空间+id可以唯一的定位到一个sql配置
resultType 表示返回值类型
-->
<select id="selectUsers" resultType="HashMap">
SELECT * FROM User
</select>
</mapper>
|
获取 SqlSessionFactory 对象
1
2
3
|
String resource = "db/mybatis/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
|
简单使用
1
2
3
4
5
6
|
@Test
public void selectTest() {
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
System.out.println(sqlSession.selectList("com.github.wjinlei.mapper.selectUsers"));
}
}
|
resultType
指定了返回的结果集类型,我们可以直接给定一个类,它会将结果直接映射到这个类的对象上,这就是ORM(对象关系映射),我们只需要把resultType改成对应的类的全限定类名即可$
用于区分一个内部类,因为我们的User
是个内部类
1
2
3
|
<select id="selectUsers" resultType="com.github.hcsp.sql.Sql$User">
SELECT * FROM User
</select>
|
<typeAliases>
可以给类的全限定类名设置一个别名,它在mybatis-config.xml
文件中配置,且必须在<settings>
标签的后面
1
2
3
|
<typeAliases>
<typeAlias alias="User" type="com.github.hcsp.sql.Sql$User"/>
</typeAliases>
|
设置了类名后,上面的调用就不需要写全限定类名了
1
2
3
|
<select id="selectUsers" resultType="User">
SELECT * FROM User
</select>
|
总是使用#{}
而不要使用${}
,${}
不会预编译SQL,容易引起SQL注入
xml 指定一个名为id的参数#{id}
1
2
3
|
<select id="selectUserById" parameterType="int" resultType="User">
SELECT * FROM User WHERE id = #{id}
</select>
|
java 传递参数的第一种方法给它一个map,里面的key是参数的名字id,因为上面xml指定了参数#{id}
1
2
3
4
5
6
7
8
9
|
@Test
public void selectTest() {
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
HashMap<String, Integer> param = new HashMap<>();
param.put("id", 1);
Sql.User user = sqlSession.selectOne("com.github.wjinlei.mapper.selectUserById", param);
System.out.println(user);
}
}
|
java 第二种方法,直接传递User对象,设置id为1,它内部实现原理是调用对象的getId
方法,如果没有就看有没有id字段
1
2
3
4
5
6
7
8
9
|
@Test
public void selectTest() {
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
Sql.User user = new Sql.User();
user.id = 1;
Sql.User result = sqlSession.selectOne("com.github.wjinlei.mapper.selectUserById", user);
System.out.println(result);
}
}
|
动态SQL是MyBatis的灵魂,详细的可以参考官方文档的动态SQL章节
1
2
3
4
5
6
7
|
<!-- 如果传递了name参数,则拼接 WHERE name LIKE #{name} 否则就不拼接 -->
<select id="selectUserByName" resultType="User">
SELECT * FROM User
<if test="name != null">
WHERE name LIKE #{name}
</if>
</select>
|
1
2
3
4
5
6
7
8
9
10
11
12
|
@Test
public void selectTest() {
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
HashMap<String, String> param = new HashMap<>();
param.put("name", "wangwu");
Sql.User user = sqlSession.selectOne("com.github.wjinlei.mapper.selectUserByName", param);
System.out.println(user);
List<Sql.User> users = sqlSession.selectList("com.github.wjinlei.mapper.selectUserByName");
System.out.println(users);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<!-- 类似选择,
当 name == wangwu 时拼接,WHERE name = 'wangwu',
当 name == 'lisi' 时执行,WHERE name = 'lisi',
否则执行 WHERE name = 'zhangsan' -->
<select id="chooseUserByName" resultType="User">
SELECT * FROM User
<choose>
<when test="name == 'wangwu'">
WHERE name = 'wangwu'
</when>
<when test="name == 'lisi'">
WHERE name = 'lisi'
</when>
<otherwise>
WHERE name = 'zhangsan'
</otherwise>
</choose>
</select>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@Test
public void selectTest() {
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
HashMap<String, String> param1 = new HashMap<>();
param1.put("name", "wangwu");
Sql.User wangWu = sqlSession.selectOne("com.github.wjinlei.mapper.chooseUserByName", param1);
System.out.println(wangWu);
HashMap<String, String> param2 = new HashMap<>();
param2.put("name", "lisi");
Sql.User liSi = sqlSession.selectOne("com.github.wjinlei.mapper.chooseUserByName", param2);
System.out.println(liSi);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
|
<!-- 循环拼接 -->
<select id="foreachUserByIds" resultType="User">
SELECT * FROM User WHERE id in
<!-- index 是当前迭代的序号,item 的值是本次迭代获取到的元素 -->
<!-- collection指定从哪个参数中获取循环数据 open表示要拼接的开始字符,close表示结束字符,separator表示分隔符 -->
<!-- 为什么open,close,separator要这么设置呢? 因为SQL语句子查询语法就是这样的 -->
<!-- SELECT * FROM User WHERE id in (1,4,8,5) -->
<foreach item="item" index="index" collection="ids"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
|
1
2
3
4
5
6
7
8
9
|
@Test
public void selectTest() {
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
HashMap<String, Object> param = new HashMap<>();
param.put("ids", Arrays.asList(1, 4, 5, 9, 3, 6));
List<Sql.User> users = sqlSession.selectList("com.github.wjinlei.mapper.foreachUserByIds", param);
System.out.println(users);
}
}
|
注意有些情况下你可能不需要 open 或 close 属性
1
2
3
4
5
6
7
8
9
|
<insert id="batchInsertUsers">
INSERT INTO User (name, tel, address) VALUE
<foreach item="user" index="index" collection="users" separator=",">
<!-- foreach标签体中的item为一个整体 -->
<!-- 对于这个SQL而言 INSERT INTO User (name, tel, address) VALUE (?, ?, ?), (?, ?, ?) ... 我们不需要open和close -->
<!-- 因为加上了open和close会变成这样 INSERT INTO User (name, tel, address) VALUE ((?, ?, ?)), ((?, ?, ?)) ...-->
(#{user.name}, #{user.tel}, #{user.address})
</foreach>
</insert>
|
1
2
3
4
5
6
7
8
9
|
<select id="selectUserByName" resultType="User">
SELECT * FROM User
<!-- where 只会在有if条件被满足时才会插入 "WHERE" 子句 -->
<!-- 而且,若子句的开头为 "AND" 或 "OR",where 元素也会将它们去除 -->
<where>
<if test="name == 'wangwu'">WHERE name = 'wangwu'</if>
<if test="name == 'lisi'">WHERE name = 'lisi'</if>
</where>
</select>
|
1
2
3
4
5
6
7
8
9
10
11
|
<update id="updateUser">
UPDATE User
<!-- 和where标签类似,只会在有if条件被满足时才会插入 "SET" 子句-->
<!-- set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号 -->
<set>
<if test="name != null">name=#{name},</if>
<if test="tel != null">tel=#{tel},</if>
<if test="address != null">address=#{address},</if>
</set>
WHERE id = #{id}
</update>
|
如果结果resutType的类包含了类成员变量,resultMap可以给它们都映射上值
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
|
<!-- resultType 换成 resultMap, 指定resultMap的id, 如下面id为Order的resultMap -->
<select id="getInnerJoinOrders" resultMap="Order">
SELECT o.id AS o_id,
u.id AS u_id,
u.name AS u_name,
u.tel AS u_tel,
u.address AS u_address,
g.id AS g_id,
g.name AS g_name,
g.price as goods_price,
(o.goods_num * o.goods_price) AS total_price
FROM "ORDER" o
INNER JOIN USER u ON u.id = o.user_id
INNER JOIN GOODS g ON g.id = o.goods_id
</select>
<!-- type 指定你要返回的resultType类 -->
<resultMap id="Order" type="com.github.hcsp.mybatis.entity.Order">
<result property="id" column="o_id"/> <!-- 把上面select到的o_id映射到这个Order类里面的id属性 -->
<result property="totalPrice" column="total_price"/> <!-- 把上面select到的total_price映射到这个Order类里面的totalPrice属性 -->
<association property="user" javaType="User"> <!-- 把User类映射到这个Order类里面的user属性 -->
<result property="id" column="u_id"/> <!-- 把上面select到的u_id映射到这个User类里面的id属性 -->
<result property="name" column="u_name"/> <!-- 把上面select到的u_name映射到这个User类里面的name属性 -->
<result property="tel" column="u_tel"/> <!-- 把上面select到的u_tel映射到这个User类里面的tel属性 -->
<result property="address" column="u_address"/> <!-- 把上面select到的u_address映射到这个User类里面的address属性 -->
</association>
<association property="goods" javaType="Goods"> <!-- 把Goods类映射到这个Order类里面的goods属性 -->
<result property="id" column="g_id"/> <!-- 把上面select到的g_id映射到这个Goods类里面的id属性 -->
<result property="name" column="g_name"/> <!-- 把上面select到的g_name映射到这个Goods类里面的name属性 -->
<result property="price" column="goods_price"/> <!-- 把上面select到的goods_price映射到这个Goods类里面的price属性 -->
</association>
</resultMap>
|
1
2
3
4
5
6
7
|
/** 一个订单 */
public class Order {
private Integer id;
private User user;
private Goods goods;
private BigDecimal totalPrice;
}
|
1
2
3
4
5
6
7
8
|
package com.github.hcsp.mybatis.entity;
/** 一个用户 */
public class User {
private Integer id;
private String name;
private String tel;
private String address;
}
|
1
2
3
4
5
6
|
/** 一件商品 */
public class Goods {
private Integer id;
private String name;
private BigDecimal price;
}
|
这种模式适合sql比较简单
的情况,复杂的sql还是老老实实写xml
文件吧
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
/**
* 删除一个用户。
*
* @param id 待删除的用户ID
*/
public void deleteUserById(Integer id) {
try (SqlSession sqlSession = sqlSessionFactory.openSession(true)) {
DeleteUserByIdMapper mapper = sqlSession.getMapper(DeleteUserByIdMapper.class);
mapper.deleteUserById(id);
}
}
interface DeleteUserByIdMapper {
@Delete("DELETE FROM user WHERE ID = #{id}")
void deleteUserById(@Param("id") Integer id);
}
|
还要在mybatis-config.xml
中配置<mapper>
1
2
3
4
|
<mappers>
<!-- 指定你那个接口的全限定名称 -->
<mapper class="com.github.hcsp.mybatis.UserDao$DeleteUserByIdMapper" />
</mappers>
|