https://mybatis.org/mybatis-3/zh/index.html
Maven 仓库:https://mvnrepository.com/search?q=mybatis
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.7version>
dependency>
Github:https://github.com/mybatis/mybatis-3
中文文档:https://mybatis.org/mybatis-3/zh/index.html
数据持久化:
为什么需要持久化?
MyBatis 程序思路:
搭建一个数据库
CREATE DATABASE `mybatis`;
USE `mybatis`;
CREATE TABLE `user`(
`id` INT(20) NOT NULL,
`name` VARCHAR(20) DEFAULT NULL,
`pwd` VARCHAR(20) DEFAULT NULL,
PRIMARY KEY (`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `user`(`id`,`name`,`pwd`)
VALUE (1,'张三','123456'),
(2,'李四','123456'),
(3,'王五','123456')
新建一个项目
新建一个普通的 maven 项目
核对 maven 配置
删除 src (这是一个父工程,干净最好)
导入依赖包
<dependencies>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.25version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.7version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13version>
dependency>
dependencies>
编写 mybatis 的核心配置文件(mybatis-config.xml)
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.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
dataSource>
environment>
environments>
configuration>
编写 mybatis 工具类(MybatisUtils)
// SqlSessionFactory ---> SqlSession
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
// 获取 sqlSessionFactory 对象
try {
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
// 既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例
// SqlSession 提供了在数据库执行 SQL 命令所需的所有方法
// 你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
实体类
package com.aze.pojo;
public class User {
private int id;
private String name;
private String pwd;
public User() {
}
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
Dao 接口(Mapper 接口)
package com.aze.dao;
import com.aze.pojo.User;
import java.util.List;
public interface UserMapper {
// 查询所用用户的所有信息
List<User> getUserList();
}
接口实现类(UserMapper.xml)
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.aze.dao.UserMapper">
<select id="getUserList" resultType="com.aze.pojo.User">
select * from mybatis.user;
select>
mapper>
package dao;
import com.aze.dao.UserMapper;
import com.aze.pojo.User;
import com.aze.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class UserMapperTest {
@Test
public void test1(){
// 1. 获得 sqlSession 对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 2. 获取 UserMapper 接口中的方法
// 方式一
// 方式一方法有很多优势,首先它不依赖于字符串字面值,会更安全一点
// 其次,如果你的 IDE 有代码补全功能,那么代码补全可以帮你快速选择到映射好的 SQL 语句
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
// 方式二
// 这种方式和用全限定名调用 Java 对象的方法类似
// 这样,该命名就可以直接映射到在命名空间中同名的映射器类,并将已映射的 select 语句匹配到对应名称、参数和返回类型的方法
//List userList1 = sqlSession.selectList("com.aze.dao.UserMapper.getUserList");
// 遍历查询结果
for (User user : userList) {
System.out.println(user);
}
// 3. 关闭 sqlSession
sqlSession.close();
}
}
编写的 Mapper.xml 配置文件没有注册(很多人都会忘记的事!)
<mappers>
<mapper resource="com/aze/dao/UsetMapper.xml"/>
mappers>
文件过滤,在 java 文件夹下的 xml 文件是会被过滤的(Maven 导出资源问题)
<build>
<resources>
<resource>
<directory>src/main/resourcesdirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>truefiltering>
resource>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
resources>
build>
java.io.IOException: Could not find resource org/mybatis/example/mybatis-config.xml
Loading class ‘com.mysql.jdbc.Driver’. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver’. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
Cause: java.sql.SQLSyntaxErrorException: Unknown database ‘mybatis/’
以上是本人遇到的,可能还有其他的,给我上网查!!!
还有可能遇到的问题:
namespace 中的 包名 要和 Dao/mapper 接口的包名一致
注意:增删改 需要提交事务,否则数据库中的数据不会改变!!!
注意:增删改 需要提交事务,否则数据库中的数据不会改变!!!
注意:增删改 需要提交事务,否则数据库中的数据不会改变!!!
选择,查询语句:
编写Mapper接口方法(UserMapper.java)
// 查询所用用户的所有信息
List<User> getUserList();
// 根据 id 查询用户
User getUserById(int id);
编写 Mapper接口方法的配置文件(UserMapper.xml)
<mapper namespace="com.aze.dao.UserMapper">
<select id="getUserList" resultType="com.aze.pojo.User">
select * from mybatis.user;
select>
<select id="getUserById" resultType="com.aze.pojo.User" parameterType="int">
select * from mybatis.user where id=#{id}
select>
mapper>
编写测试方法
// 获取所有用户所有信息
@Test
public void test1(){
// 1. 获得 sqlSession 对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 2. 获取 UserMapper 接口中的方法
// 方式一
// 方式一方法有很多优势,首先它不依赖于字符串字面值,会更安全一点
// 其次,如果你的 IDE 有代码补全功能,那么代码补全可以帮你快速选择到映射好的 SQL 语句
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
// 方式二
// 这种方式和用全限定名调用 Java 对象的方法类似
// 这样,该命名就可以直接映射到在命名空间中同名的映射器类,并将已映射的 select 语句匹配到对应名称、参数和返回类型的方法
//List userList1 = sqlSession.selectList("com.aze.dao.UserMapper.getUserList");
// 遍历查询结果
for (User user : userList) {
System.out.println(user);
}
// 3. 关闭 sqlSession
sqlSession.close();
}
// 根据 id 查询用户
@Test
public void test2(){
// 1. 获取 SqlSession 对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 2. 获取 UserMapper 接口中的方法
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User userById = mapper.getUserById(2);
System.out.println(userById);
// 3.关闭 sqlSession
sqlSession.close();
}
测试
增添语句:
编写Mapper接口方法(UserMapper.java)
// 新增一个用户
int addUser(User user);
编写 Mapper接口方法的配置文件(UserMapper.xml)
<insert id="addUser" parameterType="com.aze.pojo.User">
insert into mybatis.user(id, name, pwd) value (#{id},#{name},#{pwd})
insert>
编写测试方法
// 新增一个用户,增删改必须要提交事务
@Test
public void addTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.addUser(new User(4,"老六","123456"));
if (i > 0){
System.out.println("成功!");
}
// 提交事务(增删改都必须要有)
sqlSession.commit();
sqlSession.close();
}
测试
修改语句:
编写Mapper接口方法(UserMapper.java)
// 修改一个用户
int updateUser(User user);
编写 Mapper接口方法的配置文件(UserMapper.xml)
<update id="updateUser" parameterType="com.aze.pojo.User">
update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id}
update>
编写测试方法
// 修改用户信息
@Test
public void updateUserTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.updateUser(new User(4, "火箭", "666666"));
if (i > 0){
System.out.println("修改成功!");
}
sqlSession.commit();
sqlSession.close();
}
测试
删除语句:
编写Mapper接口方法(UserMapper.java)
// 根据 id 删除用户
int deleteUserById(int id);
编写 Mapper接口方法的配置文件(UserMapper.xml)
<delete id="deleteUserById" parameterType="int">
delete from mybatis.user where id=#{id}
delete>
编写测试方法
// 根据 id 删除用户
@Test
public void deleteUserByIdTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.deleteUserById(4);
if (i > 0){
System.out.println("删除成功!");
}
sqlSession.commit();
sqlSession.close();
}
测试
如果实体类或者数据库中的表,字段或者参数很多,我们就可以使用 Map
// 使用 map 新增用户
int addUserByMap(Map<String,Object> map);
<insert id="addUserByMap" parameterType="Map">
insert into mybatis.user(id, name, pwd) value (#{id4},#{name4},#{password4})
insert>
// 使用 Map 新增用户
@Test
public void addUserByMapTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("id4",4);
map.put("name4","老六");
map.put("password4","123456");
int i = mapper.addUserByMap(map);
if (i > 0){
System.out.println("新增成功!");
}
sqlSession.commit();
sqlSession.close();
}
java 代码执行的时候,传递通配符 %
List<User> userList = mapper.getUserLike("%李%");
在 sql 拼接中使用通配符
select * from mybatis.user where name like "%"#{value}"%"
mybatis-config.xml
MyBatis 可以配置成适应多种环境
MyBatis 默认的事务管理器是 JDBC,默认连接池 POOLED
通过 properties 属性来实现引用配置文件
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。(db.properties)
编写一个配置文件:
db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=root
在核心配置文件中引入
<properties resource="db.properties"/>
<properties resource="db.properties">
<property name="键" value="值">
properties>
类型别名是为 Java 类型设置一个短的名字
存在的意义仅用于减少类完全限定名的冗余
<typeAliases>
<typeAlias type="com.aze.pojo.User" alias="User"/>
typeAliases>
<typeAliases>
<package name="com.aze.pojo"/>
typeAliases>
区别:
第一种起别名的方法,在实体类比较少的情况下使用
第二种方法,在实体类非常多的时候使用
第一种起的别名可以DIY(自定义)
第二种起的别名在核心配置文件中不能够自定义,只能在注解中修改
@Alias("user")
public class User{}
这是 MyBatis 中纪委重要的调整设置,他们会改变 MyBatis 的运行时行为
MapperRegistry:注册绑定我们的 Mapper 文件
每一个 Mapper.xml 都需要在 MyBatis 核心配置文件中注册!
方式一:使用相对于类路径的资源引用(推荐使用这个)
<mappers>
<mapper resource="com/aze/dao/UserMapper.xml"/>
mappers>
方式二:使用映射器接口实现类的完全限定类名
<mappers>
<mapper class="com.aze.dao.UserMapper"/>
mappers>
方式三:将包内的映射器接口实现全部注册为映射器
<mappers>
<mapper name="com.aze.dao"/>
mappers>
作用域和生命周期是至关重要的,因为错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder:
SqlSessionFactory:
SqlSession:
每一个 Mapper 就代表着一个具体的业务。
数据库中的字段:
如果实体类字段与数据库字段不一致怎么办?
public class User{
private int id;
private String name;
private String password;
}
这样就会出现这种情况,而这种情况 MyBatis 会自动创建一个 ResultMap,在基于属性名来映射列到 JavaBean 的属性上
数据库字段 实体类字段
id id
name name
pwd password
<resultMap id="UserMap" type="User">
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="password"/>
resultMap>
<select id="getUserList" resultMap="UserMap">
select * from mybatis.user;
select>
如果一个数据库操作出现了异常,这样就需要排错,而日志就是最好的帮手!
在 MyBatis 中具体使用哪一个日志实现
STDOUT_LOGGING 标准日志输出
在 mybatis 核心配置文件中配置我们的日志
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
什么是 Log4j?
这个日志类型需要导包:
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
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/aze.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
配置 log4j 为日志的实现
<settings>
<setting name="logImpl" value="LOG4J"/>
settings>
测试 log4j
Log4j 的简单使用:
在要使用 Log4j 的类中导入包
import org.apache.log4j.Logger;
日志对象,参数为当前类的 class
static Logger logger = Logger.getLogger(UserMapperTest.class);
日志级别
logger.info("info:666666666");
logger.debug("debug:666666666");
logger.error("error:666666666");
语法
select * from `表` limit startIndex,pageSize;
select * from `表` limit n; #[0,n]
配置接口
// 分页
List<User> getUserListByLimit(Map<String,Integer> map);
接口配置文件
<mapper namespace="com.aze.dao.UserMapper">
<resultMap id="UserMap" type="User">
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="password"/>
resultMap>
<select id="getUserList" resultMap="UserMap">
select * from mybatis.user;
select>
<select id="getUserListByLimit" resultMap="UserMap" parameterType="User">
select * from mybatis.user limit #{startIndex},#{pageSize}
select>
mapper>
测试
// 分页
@Test
public void getUserListByLimitTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("startIndex",0);
map.put("pageSize",2);
List<User> userListByLimit = mapper.getUserListByLimit(map);
for (User user : userListByLimit) {
System.out.println(user);
}
sqlSession.close();
}
配置接口
// 分页
List<User> getUserListByRowBounds();
接口配置文件
<select id="getUserListByRowBounds" resultMap="UserMap">
select * from mybatis.use
select>
测试
// 分页
@Test
public void getUserListByRowBoundsTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
RowBounds rowBounds = new RowBounds(0,2);
List<User> userListByLimit = mapper.getUserListByLimit("com.aze.dao.UserMapper.getUserListByRowBounds",null,rowBounds);
for (User user : userListByLimit) {
System.out.println(user);
}
sqlSession.close();
}
https://pagehelper.github.io/
在真实开发中,很多时候就是选择面向接口编程
而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。
本质:反射机制实现
底层:动态代理
编写接口
@Select("select * from user")
List<User> getUser();
绑定接口
<mappers>
<mapper class="com.aze.dao.UserMapper"/>
mappers>
测试
可以在工具类创建的时候实现自动提交事务!
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession(true);
}
编写接口
@Select("select * from user")
List<User> getUser();
@Insert("insert into user(id,name,pwd) values (#{id},#{name},#{password})")
User addUser(User user);
@Update("update user set name=#{name},pwd=#{password} where id=#{id}")
int updateUser(User user);
@Delete("delete from user where id=#{id}")
int deleteUserById(@Param("id") int id);
#{}
${}
能用 #{} 就用 #{}
偷懒专用的!
安转完成后,重启 IDEA
在项目中导入 Lombok 的 jar 包
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.20version>
dependency>
在实体类中加注解
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String password;
}
Lombok 中的注解:
@Getter and @Setter // get/set
@FieldNameConstants
@ToString // toString
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor // 有参构造,无参构造
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data //无参,get,set,equals,canEqual,hashCod,toString
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
什么是多对一?
resultMap
元素,或是对其它结果映射的引用resultMap
元素,或是对其它结果映射的引用CREATE TABLE `teacher` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO teacher(`id`,`name`) VALUES (1, '秦老师');
CREATE TABLE `student` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`tid` INT(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fktid` (`tid`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (1, '小明', 1);
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (2, '小红', 1);
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (3, '小张', 1);
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (4, '小李', 1);
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (5, '小王', 1);
StudentMapper.xml(像子查询)
select s.id,s.name,t.name from student s,teacher t where s.tid=t.id;
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.aze.dao.StudentMapper">
<select id="getStudent" resultMap="StudentTeacher">
select * from mybatis.student
select>
<resultMap id="StudentTeacher" type="Student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
resultMap>
<select id="getTeacher" resultType="Teacher">
select * from mybatis.teacher where id=#{id}
select>
mapper>
StudentMapper.xml(像联表查询)
select s.id,s.name,t.name from student s left join teacher t on s.tid = t.id
<select id="getStudent2" resultMap="StudentTeacher2">
select s.id sid,s.name sname,t.name tname from student s,teacher t where s.tid=t.id
select>
<resultMap id="StudentTeacher2" type="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
association>
resultMap>
例如:一个老师拥有多个学生(集合)
对于老师而言,就是一对多的关系
环境搭建
@Data
public class Student {
private int id;
private String name;
private int tid;
}
@Data
public class Teacher {
private int id;
private String name;
// 一个老师有多有个学生
private List<Student> students;
}
Teacher getTeacherById(@Param("tid") int id);
<select id="getTeacherById" resultMap="StudentTeacher2">
select s.id sid,s.name sname,t.name tname,t.id tid from student s,teacher t where s.tid=t.id and t.id=#{tid}
select>
<resultMap id="StudentTeacher2" 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"/>
<result property="tid" column="tid"/>
collection>
resultMap>
Teacher getTeacherById2(@Param("tid") int id);
<select id="getTeacherById2" resultMap="StudentTeacher3">
select id,name from teacher where id=#{tid}
select>
<resultMap id="StudentTeacher3" type="Teacher">
<result property="id" column="id"/>
<result property="name" column="name"/>
<collection property="students" column="id" javaType="ArrayList" ofType="Student" select="getStudentGetTeacherById"/>
resultMap>
<select id="getStudentGetTeacherById" resultType="Student">
select * from student where tid=#{tid}
select>
关联(多对一):association
集合(一对多):collection
注意点:
本质还是 sql 语句,只是我们可以在 SQL 层面,去执行一个逻辑代码
动态 SQL 就是在拼接 SQL 语句,只要保证 SQL 的正确性,按照 SQL 的格式,去排列组合即可
动态 SQL 就是指根据不同的条件生成不同的 SQL 语句
利用动态 SQL 这一特性可以彻底摆脱根据不同条件拼接 SQL 语句的痛苦
MySQL:
CREATE TABLE `blog`(
`id` VARCHAR(50) NOT NULL COMMENT '博客id',
`title` VARCHAR(100) NOT NULL COMMENT '博客标题',
`author` VARCHAR(30) NOT NULL COMMENT '博客作者',
`create_time` DATETIME NOT NULL COMMENT '创建时间',
`views` INT(30) NOT NULL COMMENT '浏览量'
)ENGINE=INNODB DEFAULT CHARSET=utf8
创建一个基础工程:
新建项目并导包
编写配置文件
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=root
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="db.properties"/>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
settings>
<typeAliases>
<typeAlias type="com.aze.pojo.Blog" alias="blog"/>
typeAliases>
<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>
<mappers>
<mapper class="com.aze.dao.BlogMapper"/>
mappers>
configuration>
编写 MyBatis工具类
// SqlSessionFactory ---> SqlSession
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
// 获取 sqlSessionFactory 对象
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
// 既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例
// SqlSession 提供了在数据库执行 SQL 命令所需的所有方法
// 你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession(true);
}
}
编写实体类
@Data
public class Blog {
private String id;
private String title;
private String author;
private Date createTime;
private int views;
}
编写实体类对应 Mapper 接口 和 Mapper.xml 文件
public interface BlogMapper {
int addBlog(Blog blog);
}
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.aze.dao.BlogMapper">
<insert id="addBlog" parameterType="blog">
insert into mybatis.blog (id, title, author, create_time, views) values (#{id}, #{title}, #{author}, #{createTime}, #{views})
insert>
mapper>
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from mybatis.blog where 1=1
<if test="title != null">
and title=#{title}
if>
<if test="author != null">
and author=#{author}
if>
select>
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用,这怎么办呢?
<select id="queryBlogChoose" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<choose>
<when test="title != null">
title=#{title}
when>
<when test="author != null">
and author=#{author}
when>
<otherwise>
and views=#{views}
otherwise>
choose>
where>
select>
where
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<if test="title != null">
title=#{title}
if>
<if test="author != null">
and author=#{author}
if>
where>
select>
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>
trim:
可以通过自定义 trim 元素来定制元素的功能。
prefix:需要定义的元素
prefixOverrides:前缀
suffixOverrides:后缀
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
trim>
<trim prefix="SET" suffixOverrides=",">
...
trim>
有时候可能有一些 SQL 的逻辑代码需要复用,我们就会提出来
<sql id="if-title-author">
<if test="title != null">
and title=#{title}
if>
<if test="author != null">
and author=#{author}
if>
sql>
<select id="getBlogBySql" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<include refid="if-title-author">include>
where>
select>
注意:
动态 SQL 的另一个常见使用场景是对集合进行遍历
提示:
你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach
当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素
当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
<select id="getBlogByForeach" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id=#{id}
foreach>
where>
select>
测试
@Test
public void test01(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap();
List<String> idList = new ArrayList<String>();
idList.add("0906933ce1e74c869ed18591950c8e65");
idList.add("42a45373b0534353af51581521502dc4");
idList.add("74a6f72b805945d980561ffaac80d3a4");
map.put("ids",idList);
List<Blog> blogList = mapper.getBlogByForeach(map);
for (Blog blog : blogList) {
System.out.println(blog);
}
sqlSession.close();
}
什么是缓存[Cache]?
为什么用缓存?
什么样的数据能使用缓存?
MyBatis 包含一个非常强大的查询缓存特性,他可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
MyBatis 系统中默认定义了两级缓存:一级缓存和二级缓存
一级缓存也加本地缓存:SqlSession
测试一级缓存:
注意:
查询不同数据
增删改操作,这回改变原来的数据,所以会刷新缓存
查询不同的 Mapper.xml
手动清理缓存
sqlSession.clearCache();
二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
开启全局缓存步骤:
在核心配置文件中设置 settings,显示开启缓存
<setting name="cacheEnabled" value="true"/>
在要开启全局缓存的 Mapper.xml 配置文件中开启二级缓存
<cache/>
测试
@Test
public void test01(){
SqlSession sqlSession1 = MybatisUtils.getSqlSession();
SqlSession sqlSession2 = MybatisUtils.getSqlSession();
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
User user1 = mapper1.getUser(1);
System.out.println("user1:" + user1);
sqlSession1.close();
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
User user2 = mapper2.getUser(1);
System.out.println("user2:" + user2);
System.out.println("user1==user2" + (user1 == user2));
sqlSession2.close();
}
注意:
二级缓存 —> 一级缓存 —> 连接数据库查询
EhCache 是一种广泛使用的开源 Java 分布式缓存,主要面向通用缓存
使用步骤:
导入 ehcache 的 jar 包
<dependency>
<groupId>org.mybatis.cachesgroupId>
<artifactId>mybatis-ehcacheartifactId>
<version>1.2.1version>
dependency>
配置 ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<diskStore path="./tmpdir/Tmp_EhCache"/>
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"/>
<cache
name="cloud_user"
eternal="false"
maxElementsInMemory="5000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
memoryStoreEvictionPolicy="LRU"/>
ehcache>
在 Mapper.xml 中指定使用 ehcache 缓存
注意:如今都是用 Redis 数据库来做缓存(K-V 键值对)