Mybatis(持久层)
-
数据持久化
持久化就是程序的数据在持久状态和瞬时状态转换的过程
-
持久层
完成持久化工作的代码块,层界限十分明显
1、依赖
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
2、编写核心配置文件
[注]:最好不要添加中文注释
- mybatis-config.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>
<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>
</configuration>
- MybatisUtils
/**
* @author master
* sqlSessionFactoty sql会话工厂
*/
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
// 在使用工具时初始化
static {
try {
// 获取Mybatis获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream resourceAsStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* sqlSessionFactory提取SqlSession包含了面向数据库执行sql命令所需的所有方法
* @return SqlSession
*/
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
}
}
- XXXMapper.xml
<?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">
<!--namespace绑定一个对应的Dao接口/Mapper接口-->
<mapper namespace="com.test.dao.UserDao[包]">
<select id="getUserList[方法名]" resultType="com.test.pojo.User[集合中的实例类包]">
select * from mybatis.user[sql语句]
</select>
</mapper>
- pom.xml
<!-- 在build中配置resources,防止资源导出失败问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
3、CURD
-
dao创建接口方法
-
Mapper.xml写配置
id:方法名 parameterType:参数类型 resultType:返回类型
-
测试
@Test public void Test(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(接口类.class); int result = mapper.方法名(); if (result > 0){ System.out.println("操作成功"); } // 增删改需要提交事务 sqlSession.commit(); sqlSession.close(); }
4、Map和模糊查询
1、Map
如果实体类或者数据库中的表,字段或者参数过多,可以考虑吧使用Map
5、配置解析
1、核心配置文件优化
- db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/数据库名?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=用户
password=密码
- 引入外部配置文件
<properties resource="db.properties">
<!-- 可以增加属性配置,如果两文件有同一字段,优先使用外部配置文件 -->
<property name="password" value=""/>
</properties>
2、类型别名(typeAliases)
-
方式一:给特定实体类起别名(实体类少时)
<typeAliases> <typeAlias type="com.test.pojo.User" alias="User" /> </typeAliases>
-
方式二:扫描实体类包,默认别名为类名,首字母小写
<typeAliases> <package name="com.test.pojo"/> </typeAliases>
- 注解
@Alias("别名") public class User{}
3、设置(settings)
4、映射器(mappers)
-
方式一:通过资源xml注册
<mappers> <mapper resource="com/test/dao/UserMapper.xml"/> </mappers>
-
方式二:通过类注册(不建议)
<mappers> <mapper class="com.test.dao.UserMapper" /> </mappers>
-
方式三:使用包扫描注册(需要取名为xxxMapper.java)
<mappers> <package name="com.test.dao"/> </mappers>
6、映射
解决属性名和字段名不一致问题
<resultMap id="userMap" type="user">
<id property="password" column="pwd"/>
</resultMap>
<select id="getUserById" resultMap="userMap">
select *
from mybatis.user
where id = #{id}
</select>
7、日志
1、标准日志
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
2、LOG4J
-
导入依赖(pom.xml)
<!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
-
配置(log4j.properties)
#将等级为DEBUG的日志信息输出到console和file两个目的地 log4j.rootLogger=DEBUG,console,file #控制台输出的相关设置 log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.Target=System.out log4j.appender.console.Threshold=DEBUG log4j.appender.console.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=【%c】-%m%n #文件输出的相关配置 log4j.appender.file=org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/kuang.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=【%p】[%d{yy-MM-dd}【%c】%m%n #日志输出级别 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG
-
启动(mybatis-config.xml)
<settings> <setting name="logImpl" value="LOG4J"/> </settings>
8、分页
1、面对sql:使用limit关键字分页
2、面对对象(RowBounds):
// 创建RowBounds对象
RowBounds rowBounds = new RowBounds(1, 2);
// 执行包下方法,查询出所有列表,再使用参数rowBounds参数限制大小
List<User> userList = sqlSession.selectList("com.test.dao.UserMapper.getUserListByLimit2",null,rowBounds);
3、分页插件pagehelper
9、注解开发
1.步骤
- 核心配置
<mappers>
<mapper class="com.test.dao.UserMapper" />
</mappers>
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession(true);
}
- dao
// 存在多个参数必须添加@Param注解
@Select("select * from user where id=#{id}")
User getUserById(@Param("id") int id);
2.关于@Param()注解
- 基本类型的参数或者String类型需要加上
- 引用类型不需要加(对象)
- 存在多个参数必须添加@Param注解
10、Lombok
- 依赖
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
11、多对一(association)
1、根据查询嵌套处理(子查询)
<!-- Nested processing according to query -->
<select id="getStudent" resultMap="stuAndTeacher">
select * from mybatis.student
</select>
<resultMap id="stuAndTeacher" type="student">
<!--
property 属性名
column 字段,根据字段处理其他表
javaType 结果返回
select 处理其他表的方法
-->
<association property="teacher" column="tid" javaType="teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="teacher">
select * from mybatis.teacher where id=#{tid}
</select>
2、根据结果嵌套处理
<!-- Nested processing based on results -->
<select id="getStudent2" resultMap="stuAndTeacher2">
select s.id id, s.name sname, t.name tname
from mybatis.student s,
mybatis.teacher t
where s.tid = t.id
</select>
<resultMap id="stuAndTeacher2" type="student">
<result property="id" column="id"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="teacher">
<result property="name" column="tname"/>
</association>
</resultMap>
12、一对多(collection)
1、根据结果嵌套处理
<select id="getTeacherById" resultMap="stuAndTeacher">
select t.id tid, t.name tname, s.id sid, s.name sname
from mybatis.teacher t,
mybatis.student s
where t.id = s.tid
</select>
<resultMap id="stuAndTeacher" type="teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<!-- ofType="" 指定集合中的类型 -->
<collection property="students" ofType="student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
2、根据查询嵌套处理
<select id="getTeacherById" resultMap="stuAndTeacher2">
select *
from mybatis.teacher
where id = #{tid}
</select>
<resultMap id="stuAndTeacher2" type="teacher">
<collection property="students" column="id" javaType="ArrayList" ofType="student" select="getStudentByTid"/>
</resultMap>
<select id="getStudentByTid" resultType="student">
select * from mybatis.student where tid=#{id}
</select>
11&12 小结:
- 关联 - association
- 集合 - collection
- javaType & ofType
- javaType:用来指定实体类中属性的类型
- ofType:用来指定映射到List或者集合中的pojo类型,类型 泛型中的约束类型
13、动态SQL
1、if
<where>
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
2、choose(when,otherwise)
像switch
<where>
<choose>
<when test="title!=null">title=#{title}</when>
<when test="author!=null">author=#{author}</when>
<otherwise>
views=#{views}
</otherwise>
</choose>
</where>
3、set
<update id="updateBlog" parameterType="map">
update mybatis.blog
<set>
<if test="title !=null">title=#{title},</if>
<if test="author !=null">author=#{author},</if>
</set>
where id=#{id}
</update>
4、trim
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
<trim prefix="SET" suffixOverrides=",">
...
</trim>
5、sql片段
- 抽取sql标签公共部分
<sql id="if-title-author">
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
- 使用
<include refid="if-title-author"></include>
注意事项:
- 最好基于单表来定义SQL片段
- 不要存在where标签
6、foreach
<select id="queryBlogForeach" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<foreach collection="ids" open="and (" separator="or" close=")" item="id">
id = #{id}
</foreach>
</where>
</select>
14、缓存
1、一级缓存(sqlSession)
缓存失效:
-
查询不同的东西
-
增删改操作
-
查询不同的Mapper.xml
-
手动清理缓存
sqlSession.clearCache();
小结:一级缓存默认开启,只在一次sqlSession中有效,也就是拿到连接池到关闭连接这个区间段!一次缓存就是一个Map。
2、二级缓存
-
开启全局缓存
<setting name="cacheEnabled" value="true"/>
-
在要使用二级缓存的mapper中开启
<!-- 1、启动 --> <cache /> <!-- 2、自定义 --> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
小结:
- 只要开启二级缓存,在同一个Mapper下就有效
- 所有数据都会先放在一级缓存中
- 只有当会话提交,才会提交到二级缓存中