1.Mybatis
优秀的数据持久层框架
最新版本Maven配置:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
1.搭建工程:
mybatis依赖+数据库连接依赖
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
因为mybatis留有对数据库的接口,需要实现对应数据库(如mysql)的接口包,此时mybatis依赖搭建完成。
2.配置文件:
主标签:< configuration>< /configuration>
可选配置:
1.< properties >设置properties文件(可选)可用来存放配置文件需要用到的数据,如:数据库连接的相关信息。配置之后可以在配置文件中使用${key}来代指。
2.< settings >基本设置(可选)可用来设置SQL日志
3.< typeAliases>类型别名配置(可选)
必须配置:
1.< environments> 数据库环境配置
2.< mappers> mapper文件注册,***Mapper.xml 文件要想生效,这是必须的
<?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配置文件-->
<properties resource="jdbc.properties"/>
<!--配置 日志等功能-->
<settings>
<!--SQl日志-->
<!--<setting name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"/>-->
<!--这是使用别名-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--类型别名配置-->
<typeAliases>
<!--这是单独对类取别名-->
<!--<typeAlias type="com.qf.mybatis.model.User" alias="user"/>-->
<!--这是对包下的所有类进行取别名,别名为类名且不区分大小写(二者只能选其一)-->
<package name="com.dream.mybatis.model"/>
</typeAliases>
<!--数据库环境配置-->
<environments default="dev">
<!--表示开发时使用-->
<environment id="dev">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="com.dream.mybatis.dataSource.DruidDataSourceFactory">
<!--对properties文件中存储的信息进行取值,取值时使用的是OGNL表达式 ${properties文件中的key}-->
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
<!--表示作为产品时使用-->
<environment id="product">
<!--事务管理器配置 type指的是事务管理器使用的类-->
<transactionManager type="JDBC"></transactionManager>
<!--数据源配置-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver1}"/>
<property name="url" value="${jdbc.url1}"/>
<property name="username" value="${jdbc.username1}"/>
<property name="password" value="${jdbc.password1}"/>
</dataSource>
</environment>
<!--表示测试时使用-->
<environment id="test">
<!--事务管理器配置 type指的是事务管理器使用的类-->
<transactionManager type="JDBC"></transactionManager>
<!--数据源配置-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver2}"/>
<property name="url" value="${jdbc.url2}"/>
<property name="username" value="${jdbc.username2}"/>
<property name="password" value="${jdbc.password2}"/>
</dataSource>
</environment>
</environments>
<!--最后是注册mappers-->
<mappers>
<mapper resource="mapper/studentMapper.xml"/>
<mapper resource="mapper/userMapper.xml"/>
<mapper resource="mapper/courseMapper.xml"/>
<mapper resource="mapper/teacherMapper.xml"/>
</mappers>
</configuration>
3. mapper.xml:
< mapper namespace=" "> 写对应的mapper接口全路径。
<?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.dream.mybatis.mapper.StudentMapper">
<!--其中还有二级字标签-->
</mapper>
**二级子标签分别对应于数据库的 增删查改(CRUD) **
id需要完全对应本文件对应的mapper接口中的方法名,resultType为返回类型 且只有select标签有
<select id="getStudentByName" resultType="student">
<!--sql语句书写-->
</select>
<insert id="addStudent">
<!--sql语句书写-->
</insert>
<delete id="deleteStudent">
<!--sql语句书写-->
</delete>
<update id="updateStudent">
<!--sql语句书写-->
</update>
重要知识点:
1.mapper接口方法中的参数传递给xml文件:
取值有两种方式**KaTeX parse error: Expected 'EOF', got '#' at position 11: {表达式}**和**#̲{表达式}**,其中经常使用的…{}只是进行拼接,不能够有效的防止SQL注入。
在括号中的表达式书写,分为四种情况:
1.接口方法的参数只有一个基本类型的参数时:表达式可以取任意命名,都将会自动匹配。
2.单个引用类型的参数时,可以直接使用该类对象的属性名,mybatis将会自动注入。重点:map作为参数,可以直接以key作为键值对。
3.当有多个参数时,参数的表达式默认为arg0argN-1,param1paramN。当对应参数位置为引用数据类型时,可以直接使用对应的表达式.(例如:arg1.name,param2.name)
4.在接口方法的参数上使用@Param注解,注解中可以声明一个别名,该别名可以被xml文件自动识别到,从而进行注入(推荐)
注:在SQL语言中需要使用拼接的可以使用SQL自带的concat方法。**
如:LIKE CONCAT(’%’, #{params.name}, ‘%’)
2.主键回填
主键回填分为两种情况:一种是主键自增长的主键回填,第二种是主键由数据库随机生成(uuid)。
注意order的属性值。
1.主键自增长:
接口中的方法:
public interface ArticleMapper {
int addArticle(@Param("article") Article article);
}
map.xml文件
<mapper namespace="com.dream.mybatis.mapper.ScoreMapper">
<insert id="addScore">
<!-- selectKey表示选择键 通常都是用于主键回填功能 keyProperty表示回填的值设置到哪个属性上
resultType表示回填的值的数据类型 order表示主键回填的时机 AFTER表示数据保存后 BEFORE表示数据插入之前-->
<selectKey keyProperty="score.id" resultType="long" order="AFTER">
<!--获取当前表的最后一个id-->
SELECT LAST_INSERT_ID()
</selectKey>
<!--insert语句-->
INSERT INTO score(name, score)VALUES(#{score.name}, #{score.score})
</insert>
</mapper>
2.主键由数据库自动生成
<insert id="addArticle">
<selectKey keyProperty="article.id" resultType="string" order="BEFORE">
SELECT REPLACE(UUID(),'-','')
</selectKey>
INSERT INTO article(id, title, content, author)
VALUES (#{article.id}, #{article.title}, #{article.content}, #{article.author})
</insert>
3.结果映射:
<mapper namespace="com.dream.mybatis.mapper.EmployeeMapper">
<resultMap id="empMap" type="com.dream.mybatis.model.Employee">
<id property="id" column="id" />
<result property="name" column="name" />
<!--数据表中列名与实体类中的属性名匹配-->
<result property="entryTime" column="entry_time" />
<!--数据表中列名与实体类中的属性名匹配-->
<result property="leaveTime" column="leave_time" />
</resultMap>
<select id="getAllEmployees" resultMap="empMap">
SELECT id,name,entry_time,leave_time FROM employee
</select>
</mapper>
4.级联查询
级联查询需要使用结果映射。
一对一级联查询需要使用< association>标签
<mapper namespace="com.dream.mybatis.mapper.CourseMapper">
<!-- <resultMap id="courseMap" type="course">-->
<!-- <id column="cno" property="id"/>-->
<!-- <result property="name" column="cname"/>-->
<!-- <association property="teacher" javaType="teacher">-->
<!-- <id column="tno" property="id"/>-->
<!-- <result property="name" column="tname"/>-->
<!-- </association>-->
<!-- </resultMap>-->
<!-- <select id="getCourse" resultMap="courseMap">-->
<!-- SELECT-->
<!-- c.cno,-->
<!-- c.cname,-->
<!-- c.tno,-->
<!-- t.tname-->
<!-- FROM-->
<!-- `a_course` c-->
<!-- LEFT JOIN a_teacher t ON c.tno = t.tno-->
<!-- WHERE c.cname = #{arg0}-->
<!-- </select>-->
<resultMap id="courseMap" type="course">
<id column="cno" property="id"/>
<result property="name" column="cname"/>
<association property="teacher" column="{tid=tno}" select="getTeacher" />
</resultMap>
<select id="getCourse" resultMap="courseMap">
SELECT
cno,
cname,
tno
FROM
a_course
WHERE cname = #{arg0}
</select>
<select id="getTeacher" resultType="teacher">
SELECT
tno id,
tname name
FROM
a_teacher
WHERE tno = #{tid}
</select>
</mapper>
一对多级联查询需要使用< collection>标签
<mapper namespace="com.dream.mybatis.mapper.TeacherMapper">
<!-- <resultMap id="teaMap" type="tea">-->
<!-- <id column="tno" property="id"/>-->
<!-- <result property="name" column="tname"/>-->
<!-- <collection property="cous" column="tno" ofType="cou" >-->
<!-- <id column="cno" property="id"/>-->
<!-- <result property="name" column="cname"/>-->
<!-- </collection>-->
<!-- </resultMap>-->
<!-- <select id="getAllTea" resultMap="teaMap">-->
<!-- SELECT-->
<!-- t.tno,-->
<!-- t.tname,-->
<!-- c.cno,-->
<!-- c.cname-->
<!-- FROM-->
<!-- `a_course` c-->
<!-- RIGHT JOIN a_teacher t ON c.tno = t.tno-->
<!-- </select>-->
<resultMap id="teaMap" type="tea">
<id column="tno" property="id"/>
<result property="name" column="tname"/>
<collection property="cous" column="{tid = tno}" select="getCou"/>
</resultMap>
<select id="getAllTea" resultMap="teaMap">
SELECT
tno,
tname
FROM
a_teacher
</select>
<select id="getCou" resultType="cou">
SELECT
cno id,
cname name
FROM
a_course
WHERE tno = #{tid}
</select>
</mapper>
5.标签的应用
sql和include标签:SQL语言的重用,
<sql id="fields">
username, password,salt,sex,address
</sql>
<select id="getUserByUsername" resultType="user">
SELECT
<include refid="fields" />
FROM
`user`
WHERE
username = #{abcde}
</select>
if标签:在test中写上符合java的布尔表达式,
<if test="params.name != null and params.name != ''">
AND name LIKE CONCAT('%', #{params.name}, '%')
</if>
where标签:嵌套在if之外,能够自动添加where和忽略第一个and或者or关键字,
set标签:在updata语句中使用,也是嵌套在if之外,能够自动添加set和忽略SQL语句的后缀,如逗号,可以动态选择修改属性,
<set>
<if test="s.name != null and s.name != ''">
name = #{s.name},
</if>
<if test="s.score != null and s.score != ''">
score = #{s.score},
</if>
</set>
<where>
<if test="s.id != null and s.id != ''">
AND id = #{s.id}
</if>
</where>
trim标签:可以代替 where 标签和 set 标签
<!-- 其中 prefixOverrides 属性表示要被重写的前缀,prefix 属性表示用来替换重写的前缀内容。suffix和suffixOvverdides 属性表示对后缀的处理-->
<trim prefix="" prefixOverrides="" suffix="" suffixOverrides=""></trim>
foreach标签:循环标签经常用于sql含有in或not in的常见
<!--
collection表示遍历的元素类型,如果参数没有使用注解命名,那么该属性值只能是list,array,map其中之一;如果参数使用了注解命名,那么该属性值直接使用注解指定的名称即可。
item表示每次遍历时使用的对象名
open表示前面添加的内容
close表示最后添加的内容
seperator表示每次遍历时内容组装使用的分割符
index表示遍历时的下标
-->
<foreach collection="" item="" open="" seperator="" close="" index="">
</foreach>
4. Mybatis缓存
- 缓存的定义
缓存就是存储在内存中的临时数据,将用户经常查询的数据放在缓存(内存)中,用户再次查询数据的时候就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,能够提高查询效率,解决了高并发系统的性能问题
- 缓存的优势
减少和数据库的交互次数,减少系统开销,提高系统效率
- 缓存使用场景
经常查询并且不经常改变的数据
- Mybatis 缓存
mybatis包含一个非常强大的查询缓存特性,可以非常方便地定制和配置缓存,缓存可以极大地提高查询效率
mybatis系统默认定义了两级缓存:一级缓存和二级缓存
-
一级缓存:
默认情况下只有一级缓存开启(SqlSession级别的缓存,也称为本地缓存),同一个 SqlSession 执行多次同构查询时,结果将放入一级缓存。
-
二级缓存:
需要手动开启和配置,它是基于SqlSessionFactory级别的缓存,同一个 SqlSessionFactory 构建的 SqlSession 发起的多次同构查询,如果SqlSession关闭,则会将数据保存在二级缓存中
4.1 全局缓存配置
<!-- config.xml-->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
4.2 Mapper缓存配置
<!--
cache标签表示使用缓存
flushInterval:表示缓存刷新时间,单位是毫秒
readyOnly:表示是否只读;true 只读,MyBatis 认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。MyBatis 为了加快获取数据,直接就会将数据在缓存中的引用交给用户。不安全,速度快。读写(默认):MyBatis 觉得数据可能会被修改
size:表示存放多少条数据
eviction: 缓存回收策略,有这几种回收策略
LRU - 最近最少回收,移除最长时间不被使用的对象
FIFO - 先进先出,按照缓存进入的顺序来移除它们
SOFT - 软引用,移除基于垃圾回收器状态和软引用规则的对象
WEAK - 弱引用,更积极的移除基于垃圾收集器和弱引用规则的对象
-->
<cache flushInterval="300000" readOnly="true" size="10000" eviction="LRU"/>
4.3 二级缓存失效
二级缓存缓存数据的前提是查询的 SqlSession 关闭,如果 SqlSession 没有关闭,那么数据将不会进入二级缓存,再次进行同构查询时,二级缓存由于没有数据,查询将进入数据库,造成二级缓存失效的现象。另一种情况是,当前查询的 SqlSession 已经关闭,数据也进入了二级缓存,但在下一次查询之前,如果中间发生了更新操作,该操作更新的数据在的二级缓存中存在,那么二级缓存也将失效。
5.分页插件 PageHelper
1. 如何获取PageHelper
官方网站: https://pagehelper.github.io/
最新版本Maven配置:
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.0</version>
</dependency>
2. 插件配置
<!-- config.xml中进行配置 -->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
3. 测试分页
@Test
public void paginationTest(){
SqlSession sqlSession = MybatisUtil.openSession();
//从Sql会话中获取Mapper接口的代理实例
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
Map<String,Object> params = new HashMap<>();
params.put("sex", 1);
params.put("name", "理");
params.put("address", "千");
//设置分页
PageHelper.startPage(1, 5);
List<User> users = userMapper.retrieveUsers(params);
PageInfo<User> pageInfo = new PageInfo<>(users);//将分页查询的结果集保存在PageInfo对象中
System.out.println("总条数:" + pageInfo.getTotal());
System.out.println("总页数:" + pageInfo.getPages());
//当前页数据展示
pageInfo.getList().forEach(System.out::println);
}
6.配置数据源 Druid
Druid 是阿里巴巴开源平台上的一个项目,是性能最好的数据库连接池,如何在Mybatis中配置该数据源呢?
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
创建 DruidDataSourceFactory, 并继承 PooledDataSourceFactory,并替换数据源
public class DruidDataSourceFactory extends PooledDataSourceFactory {
public DruidDataSourceFactory() {
this.dataSource = new DruidDataSource();//替换数据源
}
}
<!--config.xml-->
<dataSource type="com.qf.mybatis.datasource.DruidDataSourceFactory"><!--数据源工厂-->
...
</dataSource>
最后,再附上一个mybatis的测试工具类和测试案例(注:一般在开发项目时使用ssm整合mybatis,放入ioc容器,不需要单独管理。)
/**
* Mybatis的工具类
*/
public class MybatisUtil {
//构造器私有化
private MybatisUtil(){}
//生产SQL会话的工厂
private static SqlSessionFactory factory;
//静态代码块初始化
static {
//创建SQL会话工厂构建者 SqlSessionFactoryBuilder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
try{
//读取配置文件
InputStream is = Resources.getResourceAsStream("config.xml");
//构建者根据配置文件来构建SqlSessionFactory
factory = builder.build(is);
} catch (IOException e){
throw new RuntimeException("配置文件读取错误");
}
}
public static SqlSession openSession(){
SqlSession session = factory.openSession();
//这下面是对session的代理模式
//获取SqlSession的字节码文件对象
Class<SqlSession> aClass = SqlSession.class;
//获取上下文类加载器
ClassLoader loader = aClass.getClassLoader();
//将字节码文件对象放入数组
Class[] interfaces = { aClass };
//
InvocationHandler handler = (proxy,method,args)-> {
//获取方法名
String name = method.getName();
if (name.equals("close")){
//如果是调用close方法,先提交事务
try {
session.commit();
} catch (Exception e){
//提交事物出错,回滚事务
session.rollback();
}
}
//执行方法,返回方法执行的结果
return method.invoke(session,args);
};
//此时返回一个SqlSession的代理
return (SqlSession) Proxy.newProxyInstance(loader,interfaces,handler);
}
}
@Test
public void searchStudentTest(){
//获取SqlSession的代理对象
SqlSession sqlSession = MybatisUtil.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
//执行查询
StudentCondition studentCondition = new StudentCondition("川", 24);
Student student = mapper.searchStudent(studentCondition);
sqlSession.close();
System.out.println(student);
}