0%

MyBatis基础2

MyBatis基础2

核心配置文件

环境配置

  • MyBatis在environments来配置适应多种环境,不过每个SqlSessionFactory只能选择一种环境

  • MyBatis的事务管理器有JDBC|MANAGED两种,默认是JDBC

  • MyBatis的数据源类型有UNPOOLED|POOLED|JNDI三种,一般采用POOLED

    1. UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。
    2. POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。
    3. JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。

属性

我们可以使用properties来引用配置文件,编写一个配置文件

1
2
3
4
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=123456
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!--    引入外部配置文件-->
<properties resource="db.properties">
<!-- 可以添加一些属性-->
<property name="" value=""/>
</properties>
<environments default="development">
<environment id="development">
<!-- 事务管理-->
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>

别名

可以给一个实体类起别名,也可以扫描包下的类,以类名来作为别名

1
2
3
4
5
<!--    给实体类起别名-->
<typeAliases>
<!-- <typeAlias type="com.muchlab.mybatis02.entity.User" alias="User"/>-->
<package name="com.muchlab.mybatis02.entity"/>
</typeAliases>

下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。

别名 映射的类型
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
object Object
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator

设置

主要有如下几个设置:其他可以通过官方文档查阅

设置名 描述 有效值 默认值
cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 true | false true
lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 true | false false
useGeneratedKeys 允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。 true | false False
logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未设置
mapUnderscoreToCamelCase 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 true | false False

映射器

  • 使用resource方式注册,只需指出Mapper配置文件的位置,没有任何限制,推荐。
  • 使用class方式注册,接口和对应的Mapper配置文件必须同名且在同一个包下。
  • 使用package方式注册,接口和对应的Mapper配置文件必须同名且在同一个包下。

插件

Mybatis-Generator

Mybatis-Plus

使用limit进行分页

startIndex为每页的第一项下标,pageSize为每页的项数

1
2
3
<select id="getStudentByLimit" parameterType="map" resultType="Student">
select * from t_student limit #{startIndex}, #{pageSize}
</select>

多对一

创建学生表和教师表,多个学生对应一个老师

1
2
3
4
5
6
7
8
9
10
11
12
drop table if exists `t_student`;
drop table if exists `t_teacher` ;
create table t_teacher(
id int primary key,
name varchar(50)
);
create table t_student(
id int primary key,
name varchar(50),
tid int not null,
foreign key(tid) references t_teacher(id)
);

在Mapper.xml文件中的多对一有两种写法:1、按结果集进行映射,2、按子查询进行映射

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
<!--按联表进行映射-->
<resultMap id="studentTeacher1" type="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
<result property="id" column="tid"/>
</association>
</resultMap>
<select id="getAll" resultMap="studentTeacher1">
select s.id as sid, s.name as sname, t.name as tname, t.id as tid
from t_student as s, t_teacher as t
where s.tid = t.id
</select>
<!--按子查询进行映射-->
<resultMap id="studentTeacher2" type="Student">
<association property="teacher" javaType="Teacher" select="getTeacher" column="tid"/>
</resultMap>

<select id="getAllBySubquery" resultMap="studentTeacher2">
select * from t_student
</select>
<select id="getTeacher" resultType="Teacher">
select * from t_teacher t where t.id=#{id};
</select>

一对多

一个老师对应多个学生,跟多对一一样,也有两种写法

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
<!--按联表进行映射-->
<resultMap id="TeacherStudent1" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
</collection>
</resultMap>

<select id="getAll" resultMap="TeacherStudent1">
select s.id as sid, t.id as tid, s.name as sname, t.name as tname
from t_teacher as t, t_student as s
where t.id=s.tid
</select>
<!--按子查询进行映射-->
<resultMap id="TeacherStudent2" type="Teacher">
<result column="tid" property="id"/>
<result column="tname" property="name"/>
<collection property="students" ofType="Student" select="getStudent" column="tid"/>
</resultMap>
<select id="getAllSubquery" resultMap="TeacherStudent2">
select id as tid, name as tname
from t_teacher
</select>
<select id="getStudent" resultType="Student">
select * from t_student where tid=#{id}
</select>

动态Sql

  • 所谓的动态Sql就是指根据不同的条件生成不同的Sql语句
  • 分别有四种拼接条件:
    1. if
    2. choose (when, otherwise)
    3. foreach
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
<select id="getCourseIf" parameterType="map" resultType="Course">
select * from t_course where 1=1
<include refid="if-id-name"></include>
</select>
<select id="getCourseChoose" resultType="Course">
select * from t_course where 1=1
<choose>
<!--如果满足when条件,则拼接when中的sql语句-->
<when test="name !=null">
and name like #{name}
</when>
<!--如果没有when条件满足,则拼接otherwise中的sql语句-->
<otherwise>
and id = 1
</otherwise>
</choose>
</select>
<!-- where标签的含义是,如果有条件,则会sql自动地添加上where,如果没有条件,则不会添加where
,如果where前面是连接关键字的话,会把关键字给去掉-->
<select id="getCourseWhere" resultType="Course">
select * from t_course
<where>
<include refid="if-id-name"></include>
</where>
</select>
<!-- select * from t_course where (id = ? or id = ?...)-->
<select id="getCourseForeach" resultType="Course">
select * from t_course
<where>
<!--这里的集合是从map中传递过来的,这里的值必须与map的key一致,而item的值必须与后面的条件#{}中的值一致,否则值将无法传入-->
<!--这里open的and是为了如果前面有条件而添加的,如果没有where会将其去掉-->
<foreach collection="ids" item="id"
open="and (" close=")" separator="or">
id = #{id}
</foreach>
</where>
</select>

Sql片段

  • 讲公共部分的sql抽取出来,如下:
1
2
3
4
5
6
7
8
9
<sql id="if-id-name">
<if test="id != null">
id = #{id}
</if>
<!--从第二个标签开始需要加连接关键字-->
<if test="name != null">
and name = #{name}
</if>
</sql>
  • 然后在要插入的地方使用include,如下:
1
2
3
4
5
6
7
8
9
10
<select id="getCourseIf" parameterType="map" resultType="Course">
select * from t_course where 1=1
<include refid="if-id-name"></include>
</select>
<select id="getCourseWhere" resultType="Course">
select * from t_course
<where>
<include refid="if-id-name"></include>
</where>
</select>
  • 注意事项:
    1. 最好只基于单表来定义Sql片段
    2. 不要在Sql片段中使用where

缓存

  • 缓存就是储存在内存中的临时数据,将用户经常查询的数据放在缓存中,用户查询数据就不用从磁盘上去读取,而从缓存中读取,从而提高查询的效率,解决了高并发系统的性能问题。
  • 优点:减少和数据库的交互(连接)次数,减少系统开销,提高系统效率,经常查询且不经常改变的数据就可以使用缓存。
  • MyBatis有两种缓存,分别是:
    1. 一级缓存:默认是开启的,它是SqlSession对象下的作用域有效,如果关闭了SqlSession缓存将会失效,一级缓存有几个会刷新缓存的操作:1、执行增删改操作;2、使用不同的Mapper文件;3、使用clearCache方法sqlSession.clearCache()
    2. 二级缓存:二级缓存也叫全局缓存,一级缓二级缓存是基于namespace级别的缓存,一个namespace对应一个二级缓存

二级缓存的使用

  • 如果想让某个Mapper文件开启缓存,可以在Mapper文件中添加cache标签
1
2
<!--属性含义分别是:缓存使用的策略;缓存刷新的间隔时间;缓存的最大数目;是否开启只读属性-->
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
  • 如果想全局地开启缓存,则可以在MBatis的配置文件下进行配置
1
<setting name="cacheEnabled" value="true"/>
  • 注意:Mybatis默认是开启了二级缓存的,所以不用配置也能生效
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
try(final SqlSession sqlSession = MybatisUtils.getSqlSession()){
final CourseMapper mapper = sqlSession.getMapper(CourseMapper.class);
final HashMap<Object, Object> map = new HashMap<>();
final List<Integer> list = Arrays.asList(2, 3);
map.put("ids", list);
final List<Course> courses = mapper.getCourseForeach(map);
for (Course cours : courses) {
System.out.println(cours);
}
//二级缓存会读取一级缓存进行保存
}
//第二次查询数据的时候会从二级缓存中读取数据
try(final SqlSession sqlSession = MybatisUtils.getSqlSession()){
final CourseMapper mapper = sqlSession.getMapper(CourseMapper.class);
final HashMap<Object, Object> map = new HashMap<>();
final List<Integer> list = Arrays.asList(2, 3);
map.put("ids", list);
final List<Course> courses = mapper.getCourseForeach(map);
for (Course cours : courses) {
System.out.println(cours);
}
}

Mybatis和SpringBoot的整合

  • 添加依赖
1
2
3
4
5
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
  • 在application.yml中进行配置(即mybatis-config的配置)
1
2
3
4
5
6
7
8
9
10
11
12
spring:
datasource:
url: jdbc:mysql://localhost:3306/test
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver

mybatis:
configuration:
map-underscore-to-camel-case: true
mapper-locations: mapper/*Mapper.xml
type-aliases-package: com.chapter24mybatis.chaper42.domain
  • 使用:SpringBoot中有两种方式进行Mapper的注册:
    1. 使用@MapperScan("com.xxx.mapper")
    2. 在Mapper接口中添加@Mapper

类型转换器

  • 当数据库数据类型和Java中的类型存在不一致的时候,或者说无法自动转换的时候,我们需要编写类型转换器来处理这类的转换。
  • 一般类型转换器需要实现TypeHandlerBaseTypeHandler,这两个接口都可以加具体的泛型,泛型的类型即是转换的Java类型,下面的例子是关于Money和Long之间的转换
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
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.joda.money.CurrencyUnit;
import org.joda.money.Money;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
* 再Money与Long之间转换的TypeHandler,处理CNY人民币
*/
public class MoneyTypeHandler extends BaseTypeHandler<Money> {
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, Money money, JdbcType jdbcType) throws SQLException {
//插入数据时,把Money类型转换成Long,精确到分
preparedStatement.setLong(i, money.getAmountMinorLong());
}

@Override
public Money getNullableResult(ResultSet resultSet, String s) throws SQLException {
return parseMoney(resultSet.getLong(s));
}

private Money parseMoney(long value) {
return Money.of(CurrencyUnit.of("CNY"), value/100.0);
}

@Override
public Money getNullableResult(ResultSet resultSet, int i) throws SQLException {
//获取数据时,把Long转换成Money
return parseMoney(resultSet.getLong(i));
}

@Override
public Money getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
return parseMoney(callableStatement.getLong(i));
}
}
  • 注册转换器:在application.yml中进行注册
1
type-handlers-package:com.xxx.handler

参考资料:B站狂神

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