概述
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
数据库准备
CREATE DATABASE
IF NOT EXISTS `study`
CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_bin';
USE `study`;
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户id',
`username` varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT '用户名',
`password` varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT '用户密码',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='用户表';
-- ----------------------------
-- Records of user
-- ----------------------------
BEGIN;
INSERT INTO `user` VALUES (1, 'admin', 'admin');
INSERT INTO `user` VALUES (2, 'user', '123456');
COMMIT;
使用步骤
编写核心配置文件
<?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="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/study?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--所有Mapper.xml都需要在这里注册才能使用-->
<mappers>
<mapper resource="org/example/dao/UserMapper.xml"/>
</mappers>
</configuration>
编写工具类
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* SqlSession包含了操作数据库的所有方法
*/
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
}
}
创建实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
}
创建Mapper接口
public interface UserMapper {
List<User> getUserList();
}
编写Mapper.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">
<!--命名空间与接口绑定-->
<mapper namespace="org.example.dao.UserMapper">
<!--id为接口方法名。返回类型单个或多个都写类类型-->
<select id="getUserList" resultType="org.example.entity.User">
select * from user;
</select>
</mapper>
运行测试
@Test
public void testGetList() {
// 1.获取SqlSession
try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
// 2.获取UserMapper接口
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 3.执行方法
List<User> userList = userMapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
}
}
可能会遇到的问题
- 找不到Mapper.xml:Maven资源导出失败,修改pom.xml
基本CRUD
增删改需要提交事务
带参查询
增加接口方法
/**
* 根据id获取用户
*/
User getUserById(int id);
编写SQL
<select id="getUserById" resultType="org.example.entity.User" parameterType="int">
select * from user where id = #{id};
</select>
测试
@Test
public void testGetUser() {
// 1.获取SqlSession
try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
// 2.获取UserMapper接口
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 3.执行方法
User user = userMapper.getUserById(1);
System.out.println(user);
}
}
插入单条
增加接口方法
/**
* 插入用户
*/
int addUser(User user);
编写SQL
<!-- #里面要跟实体类属性名相同 -->
<insert id="addUser" parameterType="org.example.entity.User" >
insert into `user`(`username`, `password`) values(#{username}, #{password});
</insert>
测试
@Test
public void testAddUser() {
// 1.获取SqlSession
try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
// 2.获取UserMapper接口
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 3.执行方法
int i = userMapper.addUser(new User(null, "root", "root"));
System.out.println(i); // 1,没必要写,插入失败直接报错
// 4.手动提交事务
sqlSession.commit();
}
}
修改单条
增加接口方法
/**
* 修改用户
*/
int updateUser(User user);
编写SQL
<!--会自动返回受影响的行数-->
<update id="updateUser" parameterType="org.example.entity.User">
update `user` set `username` = #{username}, `password` = #{password} where `id` = #{id};
</update>
测试
@Test
public void testUpdateUser() {
try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 会自动返回受影响的行数,看情况判断
userMapper.updateUser(new User(5, "root", "rootroot"));
sqlSession.commit();
}
}
删除单条
增加接口方法
/**
* 删除用户
*/
int deleteUser(int id);
编写SQL
<delete id="deleteUser" parameterType="int">
delete from `user` where id = #{id};
</delete>
测试
@Test
public void testDeleteUser() {
try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
userMapper.deleteUser(5);
sqlSession.commit();
}
}
模糊查询
增加接口方法
/**
* 模糊查询用户
*/
List<User> getUserByNameLike(String nameLike);
编写SQL
<select id="getUserByNameLike" resultType="org.example.entity.User">
select * from study.user where username like #{nameLike}
<!-- select * from foo where bar like concat('%', #{value}, '%') -->
</select>
测试
@Test
public void testGetUserLike() {
try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userByNameLike = userMapper.getUserByNameLike("%m%");
for (User user : userByNameLike) {
System.out.println(user);
}
}
}
多参数查询Map
万能Map,如果参数过多,应该考虑使用Map
增加接口方法
/**
* 查询用户 Map版
*/
User getUserByMap(Map<String, Object> map);
编写SQL
<select id="getUserByMap" parameterType="map" resultType="org.example.entity.User">
<!-- #号里与map的key对应 -->
select * from `user` where `username` = #{user} and `password` = #{pwd}
</select>
测试
@Test
public void testGetUser2() {
try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<>();
map.put("user", "admin");
map.put("pwd", "admin");
User user = userMapper.getUserByMap(map);
System.out.println(user);
sqlSession.commit();
}
}
配置解析
environments
配置多套运行环境,配置第三方数据源
properties
可以通过配置文件动态设置属性
编写db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/study?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username=root
password=root
引入db.properties
注意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 resource="db.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>
<!--所有Mapper.xml都需要在这里注册才能使用-->
<mappers>
<mapper resource="org/example/dao/UserMapper.xml"/>
</mappers>
</configuration>
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 |
手动指定别名
<typeAliases>
<typeAlias alias="User" type="org.example.entity.User"/>
</typeAliases>
扫描包添加别名
默认为类名首字母小写
<typeAliases>
<package name="org.example.entity"/>
</typeAliases>
也可以添加注解手动指定别名
@Alias("user")
public class User {
...
}
settings
日志工厂
STDOUT_LOGGING:
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
LOG4J(需要导包):
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
log4j.properties
# 将等级为DEBUG的日志信息输出到console和file这两个目的地,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/mybatis.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
程序中的使用:
@Test
public void shouldAnswerWithTrue()
{
Logger logger = Logger.getLogger(AppTest.class);
logger.info("info:进入selectUser方法");
logger.debug("debug:进入selectUser方法");
logger.error("error: 进入selectUser方法");
}
下划线转驼峰
<settings>
<!--开启数据库下划线转实体类属性驼峰-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
mappers
方式一:
<!-- 使用相对路径的资源引用 -->
<mappers>
<mapper resource="org/example/dao/UserMapper.xml"/>
</mappers>
方式二:
必须在同一包下,配置文件名称和接口名称一致
<!-- 使用映射接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.example.dao.UserMapper"/>
</mappers>
方式三:
可以注解xml同时存在
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="org.example.dao"/>
</mappers>
resultMap
解决列名和实体属性名不一致导致的问题
数据库表名区分大小写,列名不区分大小写
mybatis会根据这些查询的列名(会将列名转化为小写) , 去对应的实体类中查找相应列名的set方法设值 , 由于找不到返回null ; 【自动映射】
<!-- 结果集映射 -->
<resultMap id="userMap" type="user">
<!--<result column="id" property="id"/> 一样的不用写-->
<result column="password" property="pwd"/>
<result column="username" property="user"/>
</resultMap>
<select id="getUserList" resultMap="userMap">
select * from study.user;
</select>
多对一
搭建环境
student:
-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '学生id',
`name` varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT '学生姓名',
`teacher_id` int(11) NOT NULL COMMENT '老师id',
PRIMARY KEY (`id`),
KEY `fk_stu_teach` (`teacher_id`),
CONSTRAINT `fk_stu_teach` FOREIGN KEY (`teacher_id`) REFERENCES `teacher` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
-- ----------------------------
-- Records of student
-- ----------------------------
BEGIN;
INSERT INTO `student` VALUES (1, '张三', 1);
INSERT INTO `student` VALUES (2, '李四', 1);
INSERT INTO `student` VALUES (3, '王五', 1);
INSERT INTO `student` VALUES (4, 'John', 2);
INSERT INTO `student` VALUES (5, 'Tom', 2);
INSERT INTO `student` VALUES (6, 'Tim', 2);
COMMIT;
teacher:
-- ----------------------------
-- Table structure for teacher
-- ----------------------------
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '老师id',
`name` varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT '老师姓名',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
-- ----------------------------
-- Records of teacher
-- ----------------------------
BEGIN;
INSERT INTO `teacher` VALUES (1, '张老师');
INSERT INTO `teacher` VALUES (2, '李老师');
COMMIT;
建立表对应实体类、Mapper接口和Mapper.xml
PS:这样可以注解xml同时存在
增加接口方法
/**
* 查询所有学生,及学生对应的老师
*/
List<Student> getStudentList();
编写SQL
<!-- 子查询方式,按照查询嵌套 -->
<resultMap id="studentMap" type="student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<result property="teacherId" column="teacher_id"/>
<!-- association对象 -->
<association property="teacher" column="teacher_id" javaType="teacher" select="getTeacherById"/>
</resultMap>
<select id="getStudentList" resultMap="studentMap">
select * from study.student
</select>
<select id="getTeacherById" resultType="teacher" parameterType="int">
select * from study.teacher where `id` = #{id}
</select>
测试
@Test
public void testGetStudentList() {
try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> studentList = mapper.getStudentList();
for (Student student : studentList) {
System.out.println(student);
}
}
}
按照结果嵌套
<!-- 一条sql连接查询 -->
<resultMap id="studentMap" type="student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="teacherId" column="teacher_id"/>
<association property="teacher" javaType="teacher">
<result property="id" column="teacher_id"/>
<result property="name" column="tname"/>
</association>
</resultMap>
<select id="getStudentList" resultMap="studentMap">
SELECT s.id sid, s.`name` sname, s.teacher_id, t.id tid, t.`name` tname
FROM study.student s
JOIN study.teacher t ON s.teacher_id = t.id
</select>
一对多
新增接口方法
/**
* 查询所有老师,及老师对应的学生集合
*/
List<Teacher> getTeacherList();
编写SQL
<resultMap id="teacherMap" type="teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="studentList" ofType="student">
<result property="id" column="sid"/>
<result property="name" column="sid"/>
<result property="teacherId" column="teacher_id"/>
</collection>
</resultMap>
<select id="getTeacherList" resultMap="teacherMap">
SELECT t.id tid, t.`name` tname, s.id sid, s.`name` sname, s.teacher_id
FROM study.student s
JOIN study.teacher t ON s.teacher_id = t.id
</select>
测试
@Test
public void testGetTeacherList() {
try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
List<Teacher> studentList = mapper.getTeacherList();
System.out.println(studentList.size());
for (Teacher teacher : studentList) {
System.out.println(teacher);
}
}
}
按照查询嵌套
<resultMap id="teacherMap" type="teacher">
<!--当有多个column映射列时,只能映射一个,多个要手动写出来-->
<result property="id" column="id"/>
<collection property="studentList" column="id" javaType="arraylist" ofType="student" select="getStudentsByTeacherId"/>
</resultMap>
<select id="getTeacherList" resultMap="teacherMap">
select * from study.teacher
</select>
<select id="getStudentsByTeacherId" resultType="student" parameterType="int">
select * from study.student where teacher_id = #{id}
</select>
分页查询
limit分页
增加接口方法
/**
* 分页查询
*/
List<User> getUserListByLimit(Map<String, Object> map);
编写SQL
<select id="getUserListByLimit" parameterType="map" resultType="user">
select * from study.user limit #{startIndex},#{pageSize}
</select>
测试
@Test
public void testGetUserListByLimit() {
try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<>();
map.put("startIndex", 0);
map.put("pageSize", 2);
List<User> userList = userMapper.getUserListByLimit(map);
for (User user : userList) {
System.out.println(user);
}
}
}
RowBounds分页
增加接口方法
/**
* 分页查询
*/
List<User> getUserListByLimit(Map<String, Object> map);
编写SQL
<select id="getUserListByRowBounds" resultType="user">
select * from study.user
</select>
测试
@Test
public void testGetUserListByRowBounds() {
try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
int currentPage = 2;
int pageSize = 2;
RowBounds rowBounds = new RowBounds((currentPage - 1) * pageSize, pageSize);
// 通过session.**方法进行传递rowBounds,[此种方式现在已经不推荐使用了]
List<User> userList = sqlSession.selectList("org.example.dao.UserMapper.getUserListByRowBounds", null, rowBounds);
for (User user : userList) {
System.out.println(user);
}
}
}
分页插件
添加Maven依赖
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
在mybatis-config.xml中定义
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 使用MySQL方言的分页 -->
<property name="helperDialect" value="mysql"/>
<!-- pageSize=0查询全部结果 -->
<property name="pageSizeZero" value="true"/>
</plugin>
</plugins>
在需要分页的查询前使用
@Test
public void testGetUserListByPageHelper() {
try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
PageHelper.startPage(1, 2);
List<User> userList = userMapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
// 分页信息
PageInfo<User> pageInfo = new PageInfo<>(userList);
System.out.println("当前页码:" + pageInfo.getPageNum());
System.out.println("每页的记录数:" + pageInfo.getPageSize());
System.out.println("总记录数:" + pageInfo.getTotal());
System.out.println("总页码:" + pageInfo.getPages());
System.out.println("是否第一页:" + pageInfo.isIsFirstPage());
System.out.println("是否最后一页:" + pageInfo.isIsLastPage());
}
}
注解使用
在mybatis的核心配置文件中注入,不需要mapper.xml
了:
<mappers>
<mapper class="org.example.dao.UserMapper"/>
</mappers>
设置自动提交事务:
/**
* 自动提交SqlSession
*/
public static SqlSession getSqlSession(boolean autoCommit) {
return sqlSessionFactory.openSession(autoCommit);
}
查询
接口方法上添加注解
/**
* 获取全部用户
*/
@Select("select * from user")
List<User> getUserList();
测试
@Test
public void testGetList() {
try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
}
}
查询单条
接口方法上添加注解
/**
* 根据id获取用户
* Param注解建议基本类型和String都加上,#可以防止sql注入
*/
@Select("select * from user where id = #{xxid}")
User getUserById(@Param("xxid") int id);
测试
@Test
public void testGetUser() {
try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.getUserById(1);
System.out.println(user);
}
}
新增一条
接口方法上添加注解
/**
* 插入用户
* Param注解引用类型不用加
*/
@Insert("insert into user(username, password) values(#{username}, #{password})")
int addUser(User user);
测试
@Test
public void testAddUser() {
try (SqlSession sqlSession = MybatisUtils.getSqlSession(true)) {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
userMapper.addUser(new User(null, "root123", "root123"));
}
}
更新一条
接口方法上添加注解
/**
* 修改用户
*/
@Update("update `user` set `username` = #{username}, `password` = #{password} where id = #{id}")
int updateUser(User user);
测试
@Test
public void testUpdateUser() {
try (SqlSession sqlSession = MybatisUtils.getSqlSession(true)) {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
userMapper.updateUser(new User(20, "root", "rootroot"));
}
}
删除一条
接口方法上添加注解
/**
* 删除用户
*/
@Delete("delete from `user` where id = #{id}")
int deleteUser(int id);
测试
@Test
public void testDeleteUser() {
try (SqlSession sqlSession = MybatisUtils.getSqlSession(true)) {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
userMapper.deleteUser(20);
}
}
万能Map
接口方法上添加注解
/**
* 查询用户 Map版
*/
@Select("select * from `user` where `username` = #{user} and `password` = #{pwd}")
User getUserByMap(Map<String, Object> map);
测试
@Test
public void testGetUserByMap() {
try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<>();
map.put("user", "admin");
map.put("pwd", "admin");
User user = userMapper.getUserByMap(map);
System.out.println(user);
}
}
动态SQL
if
添加接口方法
/**
* 条件查询
*/
List<User> getUserByCondition(Map<String, Object> map);
编写SQL
<select id="getUserByCondition" parameterType="map" resultType="user">
select * from study.user where 1=1
<if test="id != null">
and id = #{id}
</if>
<if test="username != null">
and username = #{username}
</if>
<if test="password != null">
and password = #{password}
</if>
</select>
测试
@Test
public void testGetUserByCondition() {
try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<>();
// map.put("username", "admin");
List<User> userList = userMapper.getUserByCondition(map);
for (User user : userList) {
System.out.println(user);
}
}
}
choose
<select id="getUserByCondition" parameterType="map" resultType="user">
select * from study.user
<where>
<choose>
<when test="username != null">
and username = #{username}
</when>
<when test="password != null">
and password = #{password}
</when>
<otherwise>
and id = #{id}
</otherwise>
</choose>
</where>
</select>
@Test
public void testGetUserByCondition() {
try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<>();
map.put("id", 1);
map.put("username", "admin");
List<User> userList = userMapper.getUserByCondition(map);
for (User user : userList) {
System.out.println(user);
}
}
}
trim
where
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE
” 子句。而且,若子句的开头为 “AND
” 或 “OR
”,where 元素也会将它们去除
<select id="getUserByCondition" parameterType="map" resultType="user">
select * from study.user
<where>
<if test="id != null">
and id = #{id}
</if>
<if test="username != null">
and username = #{username}
</if>
<if test="password != null">
and password = #{password}
</if>
</where>
</select>
set
set 元素会动态地在行首插入 SET
关键字,并会删掉额外的逗号
接口新增方法:
/**
* 条件更新
*/
int updateUserByCondition(Map<String, Object> map);
编写SQL:
<update id="updateUserByCondition" parameterType="map">
update study.user
<set>
<if test="username != null">username = #{username}, </if>
<if test="password != null">password = #{password}, </if>
</set>
where id = #{id}
</update>
测试:
@Test
public void testUpdateUserByCondition() {
try (SqlSession sqlSession = MybatisUtils.getSqlSession(true)) {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<>();
map.put("id", 3);
map.put("username", "root");
map.put("password", "root");
userMapper.updateUserByCondition(map);
}
}
foreach
当使用可迭代对象或者数组时,index
是当前迭代的序号,item
的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index
是键,item
是值
新增接口方法
/**
* in查询
*/
List<User> getUserByIds(List<Integer> list);
编写SQL
<select id="getUserByIds" parameterType="list" resultType="user">
select * from study.user
<where>
<foreach collection="list" item="id" open="id in (" separator="," close=")">
#{id}
</foreach>
</where>
</select>
测试
@Test
public void testGetUserByIds() {
try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
List<User> userList = userMapper.getUserByIds(list);
for (User user : userList) {
System.out.println(user);
}
}
}
SQL片段
<update id="updateUserByCondition" parameterType="map">
update study.user
<set>
<include refid="if-username-password"></include>
</set>
where id = #{id}
</update>
<!-- 最好只提取if -->
<sql id="if-username-password">
<if test="username != null">username = #{username}, </if>
<if test="password != null">password = #{password}, </if>
</sql>
缓存
简介
- 什么是缓存 [ Cache ]?
存在内存中的临时数据。
将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。 - 为什么使用缓存?
减少和数据库的交互次数,减少系统开销,提高系统效率。 - 什么样的数据能使用缓存?
经常查询并且不经常改变的数据。
Mybatis缓存
- MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
- MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
- 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存
- 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
一级缓存
一级缓存也叫本地缓存
- 与数据库同一次会话期间查询到的数据会放在本地缓存中
- 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库
失效的情况
- sqlSession不同,每个sqlSession中的缓存相互独立
- sqlSession相同,查询条件不同,当前缓存中,不存在这个数据
- sqlSession相同,两次查询之间执行了增删改操作,可能会对当前数据产生影响
- sqlSession相同,手动清除一级缓存,
sqlSession.clearCache();
一级缓存就是一个map
二级缓存
二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
基于namespace
级别的缓存,一个名称空间,对应一个二级缓存
工作机制:不同的mapper查出的数据会放在自己对应的缓存(map)中
使用步骤
- 开启全局缓存 【mybatis-config.xml】
<setting name="cacheEnabled" value="true"/>
- 去每个mapper.xml中配置使用二级缓存【xxxMapper.xml】
<!--置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,
而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者 产生冲突。-->
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
测试
所有的实体类先实现序列化接口,readOnle默认是false,需要序列化,改为true即只读,不需要序列化。序列化是深拷贝,所以反序列化后的对像和原对象不是同一个对象,故哈希值不同
结论
- 只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据
- 查出的数据都会被默认先放在一级缓存中
- 只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中
自定义EhCache
Ehcache是一种广泛使用的java分布式缓存,用于通用缓存
Maven引入
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency>
编写ehcache.xml文件
如果在 加载时 未找到 /ehcache.xml 资源或出现问题,则将使用默认配置
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<!--
diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。
参数解释如下:
user.home – 用户主目录
user.dir – 用户当前工作目录
java.io.tmpdir – 默认临时文件路径
-->
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxEntriesLocalHeap="1000"
eternal="false"
timeToIdleSeconds="1200"
timeToLiveSeconds="3000"
diskSpoolBufferSizeMB="30"
maxEntriesLocalDisk="10000"
diskExpiryThreadIntervalSeconds="1200"
memoryStoreEvictionPolicy="LRU"
statistics="false">
<persistence strategy="localTempSwap"/>
</defaultCache>
<cache name="harryBootCache"
eternal="false"
timeToIdleSeconds="1200"
timeToLiveSeconds="3000"
maxElementsInMemory="1000"
memoryStoreEvictionPolicy="FIFO"/>
<!--
name:缓存名称
maxElementsInMemory:缓存最大个数
eternal:对象是否永久有效,一但设置了,timeout将不起作用
timeToIdleSeconds:设置对象在失效前允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0,也就是对象存活时间无穷大。
overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区
maxElementsOnDisk:硬盘最大缓存个数
diskPersistent:是否缓存虚拟机重启期数据
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最少使用)。你可以设置为FIFO(先进先出)或是LFU
clearOnFlush:内存数量最大时是否清除
-->
</ehcache>
在mapper.xml中使用
<mapper namespace = “org.acme.FooMapper” >
<cache type = “org.mybatis.caches.ehcache.EhcacheCache” />
</mapper>