Mybatis再学习

Mybatis工具类(线程安全版本)

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
package com.gem.util;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.Reader;

/**
* @author zhang
* @date 2020/9/22 9:34
*/
public class MybatisUtil {

private static SqlSessionFactory sqlSessionFactory = null;
private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal();

static {
try {
Reader reader = Resources.getResourceAsReader("sqlMapConfig.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
}catch (Exception e){
e.printStackTrace();
}
}

/**
* 获取SqlSessionFactory
* @return
*/
public static SqlSessionFactory getSqlSessionFactory(){
return sqlSessionFactory;
}


/**
* 新建一个Sqlsession对象,并且把他放入线程变量中
*/
private static void newSqlSession(){
SqlSession sqlSession = sqlSessionFactory.openSession(true);
threadLocal.set(sqlSession);
}

/**
* 获取sqlSession
* @return
*/
public static SqlSession getSqlSession(){
SqlSession sqlSession = threadLocal.get();
if (sqlSession ==null){
newSqlSession();
sqlSession = threadLocal.get();
}
return sqlSession;
}

/**
* 关闭sqlSession对象,并且从线程变量中删除
*/
public static void closeSqlSession(){
//获取线程变量中的SqlSession对象
SqlSession sqlSession = threadLocal.get();
//判断
if (sqlSession != null){
sqlSession.close();
threadLocal.set(null);
}
}

}

配置别名

给整个包下的所有类配置别名,默认首字符大小写都可以

1
2
3
4
<!--别名-->
<typeAliases>
<package name="com.gem.entity"/>
</typeAliases>

配置类型处理器

实体类存在枚举类型

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* @author zhang
* @date 2020/9/21 16:50
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private Integer id;
private String name;
private LocalDate birthday;
private Gender gender;
}
1
2
3
4
5
6
7
/**
* @author zhang
* @date 2020/9/21 16:50
*/
public enum Gender {
MALE,FEMALE;
}

数据库存的是枚举的下标0,1 ,直接查询会报错

这是因为mybatis提供了两种枚举处理器,默认情况下是EnumTypeHandler(字符串),我们需要修改默认的枚举处理器,使用下标来实现

1
2
3
4
<!--枚举下标-->
<typeHandlers>
<typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="com.gem.entity.Gender"></typeHandler>
</typeHandlers>

引入外部配置文件

我们可以使用properties标签来引入外部文件,将数据库的配置文件分离开来。

1
2
<!--引入外部配置文件 -->
<properties resource="db.properties"/>

使用log4j实现日志功能

导入log4j依赖

1
2
3
4
5
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

导入log4j配置文件

1
2
3
4
5
6
7
8
# \u5168\u5C40\u65E5\u5FD7\u914D\u7F6E
log4j.rootLogger=WARN, stdout
# \u63A7\u5236\u53F0\u8F93\u51FA
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
# MyBatis \u65E5\u5FD7\u914D\u7F6E
log4j.logger.com.gem.mapper=DEBUG

使用p6spy探测sql语句

上面我们可以看出使用Log4j输出的日志信息并没有完整的sql语句,想要查看完整的sql语句 ,我们需要使用第三方工具p6spy

导入依赖

1
2
3
4
5
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.9.1</version>
</dependency>

替换mysql驱动

1
2
3
<!--使用p6spy探测SQL语句,替换原有驱动-->
<!--<property name="driver" value="com.mysql.cj.jdbc.Driver"/>-->
<property name="driver" value="com.p6spy.engine.spy.P6SpyDriver"/>

修改url

1
2
<!--并且修改url-->
<property name="url" value="jdbc:p6spy:mysql:///study?serverTimezone=GMT%2B8"/>

P6Spy是一个可以用来在应用程序中拦截和修改数据操作语句的开源框架。 通过P6Spy我们可以对SQL语句进行拦截,相当于一个SQL语句的记录器,这样我们可以用它来作相关的分析,比如性能分析。

P6SPY提供了如下几个功能:

  • 记录SQL语句的执行时间戳。

  • 记录SQL语句类型

  • 记录SQL填入参数的和没有填入参数的SQL语句

  • 根据配置的时间控制SQL语句的执行时间,对超出时间的SQL语句输出到日志文件中

#{} 和 ${}

需求:通过姓名模糊查询

1
List<Student> selectStudentsByName(String name);

两种实现方式

1
2
3
<select id="selectStudentsByName" resultType="com.gem.entity.Student">
select * from mybatis_student where name like concat('%',#{name},'%');
</select>
1
2
3
<select id="selectStudentsByName" resultType="com.gem.entity.Student">
select * from mybatis_student where name like '%$(name)%';
</select>

虽然两种方法都可以实现,但是${}是不可取的

#{}是预编译处理,$ {}是字符串替换。使用 #{} 可以有效的防止SQL注入,提高系统安全性。

当然在在某些特殊的场合下只能使用${},不能用#{}。例如:在使用排序时ORDER BY ${id},如果使用#{id},则会被解析成ORDER BY “id”,这显然是一种错误的写法。

动态SQL

通过mybatis提供的各种标签方法实现动态拼接sql。

if

需求:更新用户信息

1
int updateStudent(Student student);

正常写法

1
2
3
4
<update id="updateStudent">
update mybatis_student set name = #{name},birthday = #{birthday},gender = #{gender}
where id = #{id}
</update>

如果我们不想修改name属性并没有设置name属性,这时候就会把数据库更新为null,这就不符合要求了。这时候就需要使用if标签进行判断了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!--更新-->
<!--动态SQL语句-->
<update id="updateStudent">
update mybatis_student set id=#{id}
<if test="name !=null and name!=''">
,name=#{name}
</if>
<if test="birthday !=null and birthday!=''">
,birthday=#{birthday}
</if>
<if test="gender !=null and gender!=''">
,gender=#{gender}
</if>
<where>
id=#{id}
</where>
</update>

where

需求:按多个要求查找

1
List<Student> selectStudentsByConditions(@Param("name") String name, @Param("gender")Gender gender);

这个正常实现

1
2
3
4
5
6
7
8
9
10
<select id="selectStudentsByConditions" resultType="com.gem.entity.Student">
select *
from mybatis_student where
<if test="name != null and name != ''">
and name like concat('%',#{name},'%')
</if>
<if test="gender != null">
and gender = #{gender}
</if>
</select>

传入两个参数没有问题,但是如果我们只传入最后一个参数就会解析有问题,多出一个and这时候就需要使用where标签

1
2
3
4
5
6
7
8
9
10
11
12
<select id="selectStudentsByConditions" resultType="com.gem.entity.Student">
select *
from mybatis_student
<where>
<if test="name != null and name != ''">
and name like concat('%',#{name},'%')
</if>
<if test="gender != null">
and gender = #{gender}
</if>
</where>
</select>

where标签会自动帮我们去除第一个and,保证sql语句的正确解析。

foreach

向sql传递数组或者list,mybatis使用foreach解析

需求:传入一个list,查找id在其中的student

1
List<Student> selectStudentsByIds(@Param("ids") List<Long> ids);
1
2
3
4
5
6
7
8
9
10
<select id="selectStudentsByIds" resultType="Student">
select * from mybatis_student
<where>
<if test="ids != null and ids.size > 0">
<foreach collection="ids" open="id in (" close=")" item="id" separator=",">
#{id}
</foreach>
</if>
</where>
</select>
  • collection 表示迭代的集合或者数组
  • open 表示迭代开始的内容
  • close 表示迭代结束的内容
  • item 表示迭代的每一个项
  • separator 表示迭代间隔符

SQL片段

Sql中可将重复的sql提取出来,使用时用include引用即可,最终达到sql重用的目的,如下:

原本sql

1
2
3
4
5
6
7
8
9
10
11
12
<select id="selectStudentsByConditions" resultType="com.gem.entity.Student">
select *
from mybatis_student
<where>
<if test="name != null and name != ''">
and name like concat('%',#{name},'%')
</if>
<if test="gender != null">
and gender = #{gender}
</if>
</where>
</select>

我们可以将where条件抽取成sql片段

1
2
3
4
5
6
7
8
9
10
11
<!--定义SQL片段-->
<sql id="query_student_condition">
<where>
<if test="name!=null and name!=''">
and name=concat('%',#{name},'%')
</if>
<if test="gender!=null">
and gender=#{gender}
</if>
</where>
</sql>

使用include引入

1
2
3
4
5
6
7
<select id="selectStudentsByConditions" resultType="Student">
select * from mybatis_student

<!--引用SQL片段-->
<!--注意:如果想要引用其他xml文件中的SQL片段,必须指定全名(命名空间+sqlid)-->
<include refid="query_student_condition"></include>
</select>

关联查询

一对一

husband和wife实体

1
2
3
4
5
6
7
@Data
public class Husband {
private Integer id;
private String name;

private Wife wife;
}
1
2
3
4
5
@Data
public class Wife {
private Integer id;
private String name;
}

需求:级联查询,如果通过姓名查询丈夫,如果丈夫有妻子则一起查出

1
Husband selectHusbandByName(String name);

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
<resultMap id="husbandAndWife" type="Husband">
<id property="id" column="id"></id>
<result property="name" column="name"></result>

<!--关联绑定-->
<association property="wife" javaType="Wife">
<id property="id" column="wid"/>
<result property="name" column="wname"/>
</association>
</resultMap>
<select id="selectHusbandByName" resultMap="husbandAndWife">
select h.id,h.name,w.id wid,w.name wname from mybatis_husband h left join mybatis_wife w on h.wife_id = w.id where h.name = #{name}
</select>

一对一使用association标签,property表示实体类字段,column表述数据库字段,使用javaType属性

一对多

顾客和订单实体

1
2
3
4
5
6
7
@Data
public class Customer {
private Long id;
private String name;
// 体现一对多关系
private List<Order> orders;
}
1
2
3
4
5
6
@Data
public class Order {
private Long id;
private String orderno;
private Double price;
}

需求:根据客户名查询客户,如果该客户有订单,则级联查询该客户的订单信息

1
Customer selectCustomerAndOrders(String name);

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<mapper namespace="com.gem.mapper.CustomerMapper">
<resultMap id="customerAndOrdersMap" type="Customer">
<id property="id" column="id"/>
<result property="name" column="name"/>
<!--一对多关系绑定-->
<collection property="orders" ofType="Order">
<id property="id" column="oid"/>
<result property="orderno" column="orderno"/>
<result property="price" column="price"/>
</collection>
</resultMap>

<select id="selectCustomerAndOrders" resultMap="customerAndOrdersMap">
select c.id, c.name, o.id oid, o.orderno, o.price
from hbm_customer c
left join hbm_order o
on o.customer_id = c.id
where c.name = #{name}
</select>

一对多使用collection标签,使用ofType表示多端类型

多对多

用户和角色实体

1
2
3
4
5
6
7
8
@Data
public class User {
private Long id;
private String username;
private String password;
// 多对多
private List<Role> roles;
}
1
2
3
4
5
6
7
@Data
public class Role {
private Long id;
private String roleName;
// 多对多
private List<User> users;
}

需求:根据姓名查询用户信息,并且级联查询出用户的角色

1
User selectUserAndRoles(String name);

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<mapper namespace="com.gem.mapper.UserMapper">
<resultMap id="userAndRolesMap" type="User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<collection property="roles" ofType="Role">
<id property="id" column="rid"/>
<result property="roleName" column="roleName"/>
</collection>
</resultMap>

<select id="selectUserAndRoles" resultMap="userAndRolesMap">
select u.id, u.username, u.password, r.id rid, r.roleName
from mybatis_user u
join mybatis_ur ur on ur.user_id = u.id
join mybatis_role r on ur.role_id = r.id
where u.username = #{username}
</select>

需要关联一张中间表,多对多和一对多一致。

获取自增id的值

实体对应上面的客户表

1
int insertCustomer(Customer customer);
1
2
3
<insert id="insertCustomer" useGeneratedKeys="true" keyProperty="id">
insert into hbm_customer(name) value (#{name})
</insert>

useGeneratedKeys属性设置为true,keyProperty设置为id

测试类

1
2
3
4
5
6
7
@Test
public void insertCustomer() {
Customer customer = new Customer();
customer.setName("张三");
mapper.insertCustomer(customer);
System.out.println("id为---->" + customer.getId());
}

输出结果

我们还可以通过<selectKey>实现

1
2
3
4
5
6
<insert id="insertCustomer" parameterType="Customer" useGeneratedKeys="true" keyProperty="id">
<selectKey keyProperty="id" resultType="int" order="AFTER">
select last_insert_id();
</selectKey>
insert into hbm_customer(name) value (#{name})
</insert>

延迟加载

打开延迟加载开关

设置项 描述 允许值 默认值
lazyLoadingEnabled 全局性设置懒加载。如果设为‘false’,则所有相关联的都会被初始化加载。 true | false false
aggressiveLazyLoading 当设置为‘true’的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载。 true | false true

在mybatis核心配置文件中配置:

1
2
3
4
5
6
7
<!--延迟加载-->
<settings>
<!--开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--关闭积极加载-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>

实现

需求:根据姓名查询丈夫信息,如果该丈夫有妻子,则延迟加载妻子的信息

Mapper接口中的两个方法

1
2
3
Husband selectHusbandAndWifeLazy(String name);

Wife selectWifeById(Long id);

xml的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<resultMap id="lazyHusbandAndWife" type="Husband">
<id property="id" column="id"/>
<result property="name" column="name"/>
<!--
association,collection中使用select标签调用其他查询来实现延迟加载
-->
<association property="wife" javaType="Wife" select="selectWifeById" column="id">
</association>
</resultMap>
<select id="selectHusbandAndWifeLazy" resultMap="lazyHusbandAndWife">
select *
from mybatis_husband where name = #{name};
</select>
<select id="selectWifeById" resultType="com.gem.entity.Wife">
select * from mybatis_wife where husband_id = #{id}
</select>

于正常级联查询不同,将原本一个sql分为两个单独sql,但是不同的是查询丈夫的时候,并没有返回丈夫的实体,而是返回了一个resultMap,association标签也并没有去绑定实体,而是去引用另一个sql,association本身提供懒加载功能

测试

我们只打印丈夫的name

1
System.out.println(husband.getName());

sql语句并没有执行查询妻子的部分

尝试输出妻子的信息

1
System.out.println(husband.getWife());

两句sql都有执行

小结

​ 当需要查询关联信息时再去数据库查询,默认不去关联查询,提高数据库性能。

​ 只有使用resultMap支持延迟加载设置。

场合:

​ 当只有部分记录需要关联查询其它信息时,此时可按需延迟加载,需要关联查询时再向数据库发出sql,以提高数据库性能。

​ 当全部需要关联查询信息时,此时不用延迟加载,直接将关联查询信息全部返回即可,可使用resultType或resultMap完成映射。

查询缓存

Mybatis一级缓存的作用域是同一个SqlSession,在同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。Mybatis默认开启一级缓存。

Mybatis二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace,不同的sqlSession两次执行相同namespace下的sql语句且向sql中传递参数也相同即最终执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。Mybatis默认没有开启二级缓存需要在setting全局参数中配置开启二级缓存

一级缓存

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 测试一级缓存 默认开启
*/
@Test
public void testFirstLevelCache(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student s1 = mapper.selectStudentById(1);
System.out.println(s1);
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>");
Student s2 = mapper.selectStudentById(1);
System.out.println(s2);
System.out.println(s1 == s2);
}

二级缓存

二级缓存区域是根据mapper的namespace划分的,相同namespace的mapper查询数据放在同一个区域,如果使用mapper代理方法每个mapper的namespace都不同,此时可以理解为二级缓存区域是根据mapper划分。

每次查询会先从缓存区域找,如果找不到从数据库查询,查询到数据将数据写入缓存。

Mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象

sqlSession执行insert、update、delete等操作commit提交后会清空缓存区域。

开启二级缓存

描述 允许值 默认值
cacheEnabled 对在此配置文件下的所有cache 进行全局性开/关设置。 true false true
  • 在核心配置文件SqlMapConfig.xml中加入
1
2
3
4
<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
  • 要在你的Mapper映射文件中添加一行:<cache /> ,表示此mapper开启二级缓存
  • 二级缓存需要查询结果映射的实体对象实现java.io.Serializable接口实现序列化和反序列化操作,注意如果存在父类、成员pojo都需要实现序列化接口。

测试就可以实现了

使用ehcache实现二级缓存

  • 引入maven依赖
1
2
3
4
5
6
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
<scope>compile</scope>
</dependency>
  • 导入ehcahe的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
<ehcache>
<!--二级缓存存储路径-->
<diskStore path="D:\a_demo\ehcache"/>

<!--
maxElementsInMemory 内存中存储缓存对象的最大值
maxElementsOnDisk 磁盘中存储缓存对象的最大值
eternal 缓存对象是否是永久的
overflowToDisk 缓存对象是否允许被写到磁盘中
timeToIdleSeconds 缓存对象最大空闲时间,超时将被销毁
timeToLiveSeconds 缓存对象最长存活时间,超时将被销毁
-->
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="600"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>

<!--可以配置多套缓存配置-->
<!-- <cache name="myCache1">
maxElementsInMemory="1000"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="120"
timeToLiveSeconds="600">
</cache>-->
</ehcache>
  • 在需要开启二级缓存的mapper文件中添加
1
2
<!--使用ehcache缓存-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
  • 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void testSecondLevelCache(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student s1 = mapper.selectStudentById(1);
System.out.println(s1);
MybatisUtil.closeSqlSession();

System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

sqlSession = MybatisUtil.getSqlSession();
mapper = sqlSession.getMapper(StudentMapper.class);
Student s2 = mapper.selectStudentById(1);
System.out.println(s2);
System.out.println(s1 == s2);
MybatisUtil.closeSqlSession();
}

在第一次查询完毕过后,关闭sqlsession再次查询

pagehelper分页插件

  • 导入maven依赖
1
2
3
4
5
6
<dependency>
<scope>compile</scope>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
  • 在mybatis核心配置文件中添加插件
1
2
3
4
5
6
7
8
<!--mybatis插件配置-->
<plugins>
<!--pagehelper分页插件-->
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 设置数据库类型 Oracle,Mysql,MariaDB,SQLite,Hsqldb,PostgreSQL六种数据库-->
<property name="helperDialect" value="mysql"/>
</plugin>
</plugins>
  • mapper定义查询全部的方法
1
2
3
public interface ProvinceMapper {
List<Province> selectAllProvinces();
}
1
2
3
4
<select id="selectAllProvinces" resultType="Province">
select *
from province
</select>
  • service层
1
2
3
4
5
6
7
8
9
10
public PageInfo<Province> selectAllProvincesByPage(int pageNow,int pageSize){
SqlSession sqlSession = MybatisUtil.getSqlSession();
ProvinceMapper mapper = sqlSession.getMapper(ProvinceMapper.class);

//设置分页参数
PageHelper.startPage(pageNow,pageSize,true);
List<Province> provinces = mapper.selectAllProvinces();
PageInfo<Province> pageInfo = new PageInfo<>(provinces);
return pageInfo;
}

定义方法返回pageInfo对象,设置分页参数,pageInfo构造方法传入全部数据的集合即可

  • 测试
1
2
3
4
5
6
7
8
9
@Test
public void selectAllProvincesByPage() {
PageInfo<Province> pageInfo = provinceService.selectAllProvincesByPage(1, 5);
System.out.println("总页码" + pageInfo.getPages());
System.out.println("每页显示条数"+pageInfo.getPageSize());
pageInfo.getList().forEach(System.out::println);
System.out.println("是否有下一页" + pageInfo.isHasNextPage());
System.out.println("是否是第一页" + pageInfo.isIsFirstPage());
}

逆向工程

通用mapper

  • 导入Maven依赖
1
2
3
4
5
6
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>4.1.5</version>
<scope>compile</scope>
</dependency>
  • 实体类配置
1
2
3
4
5
6
7
8
9
10
11
@Data
@Table(name = "gem_person")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private String phone;
private String qq;
private String address;
}

需要在实体类上使用table注解指定数据库,使用id注解标明主键,自增字段需要加GeneratedValue,类似于Hibernate

  • 编写mapper接口
1
2
3
4
5
6
/**
* @author zhang
* @date 2020/9/24 10:23
*/
public interface PersonMapper extends Mapper<Person> {
}
  • 测试
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
@Test
public void test() throws IOException {
InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession = sqlSessionFactory.openSession();
MapperHelper mapperHelper = new MapperHelper();
mapperHelper.registerMapper(PersonMapper.class);
mapperHelper.processConfiguration(sqlSession.getConfiguration());
PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
//mapper.selectAll().forEach(System.out::println);
//System.out.println(mapper.selectByPrimaryKey(1L));

Person person = new Person();
person.setName("张三");
//System.out.println(mapper.select(person));

//System.out.println(mapper.selectOne(person));

//统计个数
//System.out.println(mapper.selectCount(null));

//条件组合工具
Example example = new Example(Person.class);
//条件,一个create中可以组合无数个条件,但这些条件都是and关系
Example.Criteria criteria = example.createCriteria();
criteria.andLike("name","%张%");
//System.out.println(mapper.selectByExample(example));
Example.Criteria criteria1 = example.createCriteria();
criteria1.andEqualTo("name","王五");
example.or(criteria1);
//System.out.println(mapper.selectByExample(example));

//排序
example.setOrderByClause("id desc");
System.out.println(mapper.selectByExample(example));

}

在传统的Mybatis写法中,DAO接口需要与Mapper文件关联,即需要编写SQL来实现DAO接口中的方法。而在通用Mapper中,DAO只需要继承一个通用接口,即可拥有丰富的方法:

继承通用的Mapper,必须指定泛型

评论