框架
框架
-
Framework
:框架-
可重用的设计:表现为一组抽象构件及构件实例间交互的方法
- 可被开发者定制的应用骨架、模板
-
就是一个半成品软件、一组组件
- 自己完善为成品
-
安全的、可复用的、不断升级的软件
-
-
框架就是一个模板
-
规定内容格式,有基础的功能可用
-
添加自己的功能实现
- 可以通过框架中的基础功能实现
-
特点
- 一般不是全能的,不能做所有事
- 针对某个方面擅长
- 例如:
MyBatis
对数据库操作- 但不能进行其他操作
三层架构
结构
-
界面层:
User Interface Layer
- 表示层、视图层;主要接受用户数据,显示请求的处理结果
- 使用
web
界面和用户交互- 类似手机 app 就是表现层,用户在 app 中操作
- 调用数据访问层获取数据
-
业务逻辑层:
Business Logic Layer
- 服务层;接收表示层传递过来的数据、检查数据、计算业务逻辑
- 调用数据访问层获取数据
-
数据访问层:
Date Access Layer
-
持久层,与数据库打交道
-
主要实现对数据的增、删、改、查
-
将存储在数据库的数据提交给业务层,同时将业务层处理的数据保存到数据库
-
交互
- 用户 → 界面层 → 业务逻辑层 → 数据库访问层 → DB 数据库
对应结构包
- 界面层:
controller
包,servlet
类 - 业务逻辑层:
service
包,XxxService
类 - 数据库访问层:
dao
包,XxxDao
类
对应框架
-
SSM
:SpringMVC
、Spring
、MyBatis
-
界面层 –
servlet
类 →SpringMVC
框架 -
业务逻辑层 –
service
类 →Spring
框架 -
数据访问层 –
dao
类 →MyBatis
框架
-
MyBatis
- 相当于增强版 JDBC,便捷连接、操作数据库
介绍
-
最初
apache
的开源项目iBatis
- 2010 年项目由 apache software foundation 迁移到 google code 并改名为 MyBatis
- 2013 年 11 月迁移到 GitHub
-
基于 Java 的持久层框架:
MyBatis SQL Mapper Framework for Java
- 基于 Java 的 SQL 映射框架
- 包括
SQL Mapper
:SQL 映射- 表中一行记录映射为一个 Java 对象
Data Access Objects(DAOs)
:数据访问,操作数据库数据
作用
- 减轻使用 JDBC 的复杂性
- 不用编写重复的
Connection
、Statement
- 不用编写关闭资源代码
- 不用编写重复的
- 直接使用 Java 对象表示结果数据
- 让开发者专注 SQL 的处理
- 其他工作由 MyBatis 完成
- MyBatis 可完成
-
注册数据库驱动
-
创建 JDBC 中必须使用的对象
Connection
、Statement
、RsultSet
-
从
xml
文件中获取sql
,并执行sql
语句- 把
ResultSet
结果转换为 Java 对象 - 将对象封装到到 List 集合中
- 把
-
关闭资源连接
-
基础实现
pom.xml
-
Maven 的 pom.xml 文件配置
-
导入
mybatis
、mysql-connector-java
相关依赖 -
配置
resources
资源 -
其他配置文件基于 Maven 版本、JDK 版本 和 IDEA 环境配置
- 不同版本的 JDK 和 Maven 以及高版本编译工具可能不兼容
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>li_maven</groupId> <artifactId>demo10</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> <!-- mysql-connector-java 数据库连接 Java 驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.25</version> </dependency> <!-- mybatis 框架依赖 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.9</version> </dependency> <!-- junit 单元测试依赖 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> </dependencies> <!-- 配置此项目的编码格式 和 编译、执行 JDK 版本 --> <properties> <project.build.sourceEncoding>utf8</project.build.sourceEncoding> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> </properties> <build> <!-- 此插件更新测试插件版本 --> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.0.0-M3</version> <configuration> <forkCount>0</forkCount> <testFailureIgnore>true</testFailureIgnore> </configuration> </plugin> </plugins> <!-- 配置以下两项以保证 Maven 将 src\mian\ 目录下的 .xml、.properties 配置文件也进行打包 --> <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> </project>
-
mybatis-config.xml
-
MyBatis 的配置文件
- 配置数据源、连接池、映射文件 等
<?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"> <!-- mybatis-3-config.dtd:约束文件名,固定值 --> <!--Mybatis 核心配置文件:配置了数据源、连接池、映射mapper文件 --> <configuration> <settings> <!-- 设置 MyBatis 输出日志,将日志打印到控制台输出 --> <setting name="logImpl" value="STDOUT_LOGGING" /> </settings> <!-- 环境配置:数据库的连接信息 default 值必须和某个环境id相同,是当前默认使用的数据库 --> <environments default="MySQL"> <!-- environment:一个数据库信息的配置; id:唯一值,自定义,表示此环境的名称 --> <environment id="MySQL"> <!-- transactionManager:MyBatis 的事务类型 type=JDBC:表示使用 JDBC 中的 Connection 对象的 commit、rollback进行事务处理 --> <transactionManager type="JDBC"/> <!-- dataSource:数据源,连接数据库的配置;k-v 格式配置 type:表示使用连接池 name 值固定不能更改,value 是当前对应的数据库连接参数 --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/demo01"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <!-- 注册mapper文件到mybatis-config.xml中--> <mappers> <!-- 一个 Mapper 对应一个映射文件,resource 值为编译后 Mapper.xml 文件的全路径 --> <mapper resource="mapper/DemoMapper.xml" /> </mappers> </configuration>
Mapper
Demo 类
- 对应数据库表数据的实体类
- 一个对象对应表中的一条记录
package empty;
import java.util.Date;
public class Demo {
// 属性名与数据表的字段名对应,根据属性名匹配对应的 set 方法
private int id;
private String name;
private int age;
private Date date;
// 必须保留无参构造可以让 MyBatis 框架将数据自动封装到实例对象中
public Demo(){}
public Demo(int id, String name, int age, Date date){
this.id = id;
this.name = name;
this.age = age;
this.date = date;
}
//get、set、toString 方法 略
}
DemoDao 接口
- 定义要执行的方法
- 之后使用动态代理无需自己创建实现类
package mapper;
import empty.Demo;
import java.util.List;
public interface DemoDao {
public List<Demo> getAll();
public int add();
}
DemoMapper.xml
- sql 映射文件
- 写实现方法的 sql 语句
<?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">
<!--
上面代码指定约束文件
mybatis-3-mapper.dtd:约束文件名,拓展名 dtd
约束文件作用:限制、检查当前文件中出现的标签、属性名必须符合 MyBatis 要求
-->
<!-- namespace:命名空间,自定义唯一值;常规使用 dao 接口全限定名 -->
<mapper namespace="mapper.DemoDao">
<!--
select、insert、update、delete 标签对应相应的数据库操作
id:执行的 sql 语法的唯一标识,MyBatis 根据 id 值找到要执行的 sql 语句
使用接口中方法名;可自定义,但不能重复;默认通过方法名自动匹配对应 id
-->
<!-- select 语句,id 是接口方法名 getAll -->
<!-- resultType:查询结果返回类型,将查询到的记录自动封装到该类的实例对象,并放到集合中 -->
<select id="getAll" resultType="empty.Demo">
select * from demo;
</select>
<!-- insert 语句,id 是接口方法名 add -->
<!-- 返回受影响行数,无需指定返回类型 -->
<!-- #{} 表示引用 Demo 的属性值进行赋值,执行 sql 语句时传入 Demo 类对象 -->
<insert id="add">
insert into demo values(#{id}, #{name}, #{age}, #{date});
</insert>
</mapper>
测试类
package testMapper;
import empty.Demo;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
public class TestDemoDao {
private static InputStream in;
static {
//访问 mybatis 配置文件读取数据,传入配置文件全路径
try {
in = Resources.getResourceAsStream("mybatis-config.xml");
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void testGetAll() {
//得到 sql 语句执行对象,链式编程
SqlSession session = new SqlSessionFactoryBuilder().build(in).openSession();
//指定执行的 sql 语句的标识,包括 Mapper 文件的命名空间 和 sql 标签 id
String sqlId = "mapper.DemoDao.getAll";
//执行 sql 语句,根据 sqlId 找到对应的 sql 语句
List<Demo> Demo = session.selectList(sqlId);
//遍历输出查询结果
for (empty.Demo demo : Demo) {
System.out.println(demo);
}
//关闭资源
session.close();
}
@Test
public void testAddOne() {
//得到 sql 语句执行对象,链式编程
SqlSession session = new SqlSessionFactoryBuilder().build(in).openSession();
String sqlId = "mapper.DemoDao.addOne";
//自动查找并执行 insert 语句,返回受影响行数
int rows = session.insert(sqlId,new Demo(100, "张三", 18, new Date()));
if (rows > 0) {
System.out.println("执行成功,受影响行数: " + rows);
}
//MyBatis 默认开启事务,需要手动提交后才会添加到数据库
session.commit();
//关闭资源
session.close();
}
}
主要类
Resources
-
通过
getResourceAsAtream()
方法读取资源文件InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
-
MyBatis 中的类
SqlSessionFactoryBuilder
-
通过
build()
方法得到工厂类对象SqlSessionFactory factory = new SqlSessionFactoryBuilder().bulid(in)
- 传入读取的资源文件
-
创建
SqlSessionFactory
之后就不再需要
SqlSessionFactory
-
重量级对象
- 创建对象耗时较长,消耗资源较多
- 整个项目中一个即可
-
是一个接口
- 实现类:
DefaultSqlSessionFactory
- 默认使用此实现类方法
- 实现类:
-
作用:获取
SqlSession
对象SqlSession Session = factory.openSession();
-
openSession()
方法说明- 无参数,获取非自动提交事务的
SqlSession
对象 openSession(boolean)
:默认false
- 参数为
true
:获取自动提交事务的SqlSession
对象 - 参数为
false
:获取非自动提交事务的SqlSession
对象
- 无参数,获取非自动提交事务的
SqlSession
-
是一个接口
- 定义了操作数据的方法
- 例如:
selectOne()
、selectList()
、insert()
、update()
、delete()
、commit()
、rollback()
等
- 例如:
- 创建接口代理对象:使用
dao
接口的Class
对象getMapper(Dao.class)
- 定义了操作数据的方法
-
接口实现类:
DefaultSqlSession
- 实现了接口中的操作方法
- 默认使用此实现类方法
-
使用要求
SqlSession
对象不是线程安全的,需要在方法内部使用- 执行
sql
语句之前使用openSession()
方法获取对象 - 执行
sql
语句之后进行关闭:sqlSession.close()
- 保证是线程安全使用
- 执行
动态代理
条件
- 接口和
Mapper.xml
文件在同一个目录下 - 接口和
Mapper
文件名统一 Mapper.xml
文件命名空间使用Dao
接口全限定名Mapper.xml
文件中 sql 语句 id 使用接口方法名- 接口中不要使用重载方法
- 反射机制通过类全限定名和方法名找到对应实体类和方法
使用
-
<T> T getMapper(Class<T> var1)
- 传入 Dao 接口的 Class 对象
- 框架底层自动完成所有操作得到 接口对象
- 使用接口对象直接执行方法
-
例如
int i = sqlSession.getMapper(DemoDao.class).add(); /* getMapper() 方法得到接口实例的代理对象,通过对象调用方法执行 框架自动创建接口实现子类并实现抽象方法 */
参数传递
参数
-
从 Java 代码中将数据传入到
mapper.xml
文件的sql
语句中 -
parameterType
:mapper.xml
文件的一个属性-
通过反射可以得到参数类型
- 非强制使用,一般不使用
-
表示
dao
接口中方法的参数数据类型 -
值为 Java 数据类型全限定名或 MyBatis 定义的别名
- 例如:
java.lang.Integer
别名int
- 例如:
<select id="getById" parameterType = "java.lang.Integer" resultType="empty.Demo"> select * from demo where id = #{id}; </select>
-
传参
简单类型
-
一个参数
sql
语句中参数位置使用#{任意字符}
-
执行方法时传入参数
-
原理
- 框架底层使用
PreparedStatement
对象预编译sql
- 使用
#{}
后MyBatis
执行sql
使用 JDBC 中的PrepareStatement
对象 - 传参时对占位符进行替换
类似于:
// 示例执行查询语句,使用 PreparedStatement对象 PreparedStatement pre = con.prepareStatement(sql); pre.setInt(1, 参数); // 执行 sql 后将查询结果封装到 resultType 结果类型对象中
- 框架底层使用
命名参数
-
多参数
- 多个参数使用
@Param
命名参数 - 多个参数必须添加
@Param
;否则无法检测参数
//多参数查询方法,使用 @Param 标明参数名 Demo getOne(@Param("id") int id, @Param("age") int age)
<select id="getOne" resultType="empty.Demo"> select * from demo where id = #{id} or age #{name} </select> <!-- sql 中 #{} 内容必须和 @Param 值对应 -->
- 多个参数使用
对象传参
-
多参数
- 使用 Java 对象传递参数
-
对象的属性值就是 sql 语句的参数值
- 每一个属性对应一个字段
-
完整语法格式
-
#{property, javaType=Java中数据类型, jdbcType数据库中数据类型}
javaType
、jdbcType
类型MyBatis
可以自动检测,一般不需要设置
-
常用格式
:#{property}
-
// 方法定义,参数类型为对象
List<Demo> get(Demo demo);
// 测试方法
@Test
public void testGet(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
Demo demo = new Demo(100, "二狗", 23, new Date())
//自动找到 demo 对象中的属性,匹配 sql 语句中的 id、name 传入参数
List<Demo> demos = sqlSession.getMapper(DemoDao.class).getOne(demo);
for (Demo demo : demos) {
System.out.println(demo);
}
sqlSession.close();
}
<!-- mapper文件编写 -->
<select id="getAll" resultType="empty.Demo">
<!-- 占位符中值为对应表中字段的对象属性名,必须一致,否则找不到属性 -->
select * from demo where id = #{id} or name = #{name}
</select>
索引
-
参数位置从 0 开始
-
语法:
#{arg0}
- mybatis 3.3 及之前版本使用 #{0}、#{1}
- mybatis 3.4 开始使用 #{arg0}、#{arg1} 方式
- 要定位对应参数坐标
Demo getByIdName(int id, String name); // 测试方法
<!-- mapper文件编写,arg0、agr1 对应参数列表中索引位置参数 --> <select id="getAll" resultType="empty.Demo"> select * from demo where id = #{arg0} or name = #{arg1} </select>
Map传参
-
使用 Map 存放参数
- 占位符使用 key 值
- 自动找到 Map 中对应的 value 值
//方法定义 List<Demo> get(Map<String, Integer> map); //测试类 @Test public void testGetMap() { SqlSession sqlSession = MybatisUtils.getSqlSession(); //创建 Map 对象 添加数据 Map<String, Object> map = new HashMap<>(); map.put("id", 99); map.put("name", "张三"); //传入 Map 对象查询数据 List<Demo> demos = sqlSession.getMapper(DemoDao.class).getByMap(map); for (Demo demo : demos) { System.out.println(demo); } sqlSession.close(); }
<!-- mapper文件编写:key 作为占位符 --> <select id="getAll" resultType="empty.Demo"> select * from demo where id = #{id} or name = #{name} </select>
# 和 $
作用
-
#:占位符
-
对 MyBatis 表明该位置使用实际参数值代替
#{}
等价于sql
语句的?
-
使用
PreparedStatement
对象执行 sql 语句- 更加安全和迅捷,首选做法
<select id="get" resultType="empty.Demo"> select * from demo where id = #{id} </select>
- sql 语句等价于
select * from demo where id = ?
- 使用
PreparedStatement
对象编译 sql 语句并传递参数pre.setInt(1, 参数)
- 参数会找到并替换掉
#{id}
- 执行 sql 语句
-
-
$:字符替换符
-
直接用参数替换
${}
位置内容 -
使用
Statement
将 sql 语句和${}
的内存连接- 有 sql 注入风险
- 不会自动匹配数据格式
- 字符串文本需要手动添加
""
- 字符串文本需要手动添加
-
使用
#
占位符会导致部分关键字自动添加""
变成字符格式,造成语法错误- 主要用在替换表名、列名、不同列排序等操作的关键字拼接
- 例:
desc
等
<select id="get" resultType="empty.Demo"> select * from demo where name = '?{name}' </select> <!-- 等价于 select * from demo where id = '参数'; 直接进行参数替换,使用 Statement 对象拼接字符串 -->
-
替换列名
List<Demo> orderBy(String colName); // 定义方法
<select id="orderBy" resultType="empty.Demo">
select * from demo order by ${colName} <!-- 必须使用 ?,否则排序关键字会自动拼接引号,造成语法错误 -->
</select>
区别
#
使用?
在代码中做占位符- 使用
PreparedStatement
对象执行 sql,效率高 - 能避免 sql 注入,更安全
- 使用
$
不使用占位符,字符串拼接形式传入参数- 使用
Statement
对象执行 sql,效率较低 - 存在 sql 注入的风险
- 但是对于关键字输入适用
PrepareStatement
传入参数自动匹配格式- 会将字符自动添加引号,无法正常传入关键字
- 使用
resultType
定义
-
执行完后返回的结果类型
- DQL 语句执行必须添加
resultType
- DQL 语句执行必须添加
-
DQL 语句执行完成后查询到的数据转为的 Java 对象
- 可以是任意的 Java 类型,自定义
-
处理方式
-
MyBatis 执行 sql 语句
- 然后 MyBatis 调用类的无参构造创建对象
-
执行 sql 语句得到查询结果集
-
将
ResultSet
中的字段值封装到对象的同名属性中-
找不到同名属性的字段自动忽略
-
找不到同名字段的属性默认 null 值
- 即 实体类属性名和表中字段一致
-
-
返回执行结果为封装后的对象
- 多条结果则将对象封装到 List 集合中
-
别名
-
resultType
值:最好使用全限定名- 绝对路径查找更准确
-
对于 Java 系统类可使用全限定名或使用
MyBatis
定义的别名- 例如:
java.lang.Integer
别名int
- 例如:
-
对于自定义 Java 类一般使用全限定名
- 也可以自定义别名使用
-
自定义别名,在 MyBatis 配置文件中定义
- 使用
<typeAlias>
标签定义- 自定义单个类别名
- 使用
<package name=""/>
定义name
赋值要取别名的包全限定名称- 此包中所有类名就是别名,类名不区分大小写
- 不建议使用,多个包中有同名类时会产生冲突,导致异常
<!-- 此标签下定义所有类型别名 --> <typeAliases> <!-- 一个标签定义一个别名 --> <typeAlias type="empty.Demo" alias= "Demo"/> <!-- empty 包中所有类类名就是别名 --> <package name = "empty"> </typeAliases>
- 使用
返回 Map
-
查询结果以
Map
返回Map
中key
为字段名,value
是属性值- 只能查询单行记录
- 查询多行记录可将
Map
封装到List
中
- 查询多行记录可将
// 方法定义 Map<Object, Object> getById(int id); // 测试方法 @Test public void testGetByID() { SqlSession sqlSession = MybatisUtils.getSqlSession(); Map<Object, Object> demo = sqlSession.getMapper(DemoDao.class).getByID(123); for (Object o : demo.keySet()) { System.out.println(o + " = " + demo.get(o)); } }
<!-- mapper.xml 文件 --> <select id="getByID" resultType="map"> select * from demo where id = #{id}; </select>
ResultMap
-
结果映射
-
指定列名和 Java 对象的属性间的对应关系
- 自定义字段和属性名的映射关系
- 只需要映射命名不相同的字段
- 数据库字段和Java实体类中属性命名相同时自动映射
- 字段名和属性名不相同时
- 使用
ResultMap
指定映射关系 - 指定字段别名和属性名相同
- 使用
- 自定义字段和属性名的映射关系
-
ResultMap
中未指定的字段、属性映射会自动映射- 优先使用已经指定的映射
- 未指定的映射会自动查找装配
-
和
resultType
不要同时使用<!-- 数据库字段和Java对象属性间的映射关系 --> <resultMap id="Demo" type="empty.Demo"> <!-- 主键字段,使用 id column --> <id column="id" property="id"/> <!-- 普通字段映射 --> <result column="name" property="name"/> <result column="age" property="age"/> <result column="date" property="date"/> </resultMap> <!-- 使用映射关系,不再写 resultType --> <select id="orderBy" resultMap="Demo"> select * from demo where name = ${name} </select>
模糊查询
-
两种方式
- 方法参数中写模糊条件
- 模糊条件比较灵活
- sql 语句中拼接模糊条件
-
List<Demo> getByName1(String name); // 方法定义,在方法参数中拼接模糊条件 List<Demo> getByName2(String name); // 方法定义,在sql语句中拼接模糊条件
-
<!-- mapper.xml文件中 sql 语句 --> <select id="getByName1" resultType = "empty.Demo"> <!-- 在参数中写模糊条件 --> select * from demo where name like #{name} </select> <select id="getByName2" resultType = "empty.Demo"> <!-- 直接拼接 --> select * from demo where name like '%'#{name}'%' </select>
-
// 测试方法 @Test public void testGetByName() { SqlSession sqlSession = MybatisUtils.getSqlSession(); Demo demo = sqlSession.getMapper(DemoDao.class) // 参数中写模糊条件:拼接模糊查询符号 % List<Demo> demos1 = demo.getByName1("%三%"); // 参数传入到模糊条件 List<Demo> demos2 = demo..getByName("三"); for (Demo demo : demos) { System.out.println(demo); } sqlSession.close(); }
- 方法参数中写模糊条件
注解开发
-
在 dao 接口方法上添加注解执行对应语句
@Select
、@Insert
、@Delete
、@Update
-
使用注解也可以同时使用
mapper.xml
文件- 但同一个方法只能选择 注解 或 xml 使用
- 注解默认以方法名为 id,会造成重名冲突
- 但同一个方法只能选择 注解 或 xml 使用
动态 SQL
-
动态SQL:sql 内容是变化的
- 根据不同条件获取到不同的 sql 语句
- 主要是 where 字句的变化
- 根据不同条件获取到不同的 sql 语句
-
动态SQL实现使用 MyBatis 提供的标签
<if>、<where>、<foreach>
<if>
<if>
:判断条件- 在方法定义中使用对象传参
- 注意
-
单独使用
<if>
需要在where
后先添加条件- 条件随意,不影响查询结果即可
- 避免
if
条件不成立时可能引起的语法错误
- 避免
- 条件随意,不影响查询结果即可
-
和
where
标签搭配使用自动进行关键字替换
-
<select id="getAllByIf" resultMap="Demo">
select * from demo
<!-- where 子句中追加一个条件避免 if 条件不成立造成语法错误,此条件不对查询结果造成影响即可 -->
where name is not null
<!-- test 进行属性条件判断,满足条件才执行包含的语句 -->
<if test="name != null and name != ''">
and name like '%' #{name} '%'
</if>
<if test="id != 0">
and id > #{id}
</if>
<if test="age != 0">
or age > #{age}
</if>
</select>
<!--
实际执行:select * from demo where name is not null and name like '%' ? '%' and id > ? or age > ?
传递参数:二(String), 2(Integer), 15(Integer)
-->
<where>
- 用来包含
<if>
标签- 对于
<if>
使用进行完善- 方法中同样需要对象传参
- 按照语法格式将满足条件的
<if>
内容追加到主sql
语句
- 对于
- 当所有
<if>
中有条件成立<where>
标签自动添加where
关键字- sql 语句中不再声明
where
- sql 语句中不再声明
- 替换
<if>
中多余的关键字and
、or
等,避免语法错误- 检测到满足条件的首个
if
标签- 此段
sql
子句句首存在关键字则替换为 where - 没有关键字直接添加
where
关键字
- 此段
- 检测到满足条件的首个
- 所有
if
条件都不满足则进行全表查询- 等价于
sql
语句中不含where
子句
- 等价于
<select id="getAllByIf" resultMap="Demo">
select * from demo
<!-- sql 语句中不再声明 where -->
<!-- where 标签中所有 if 标签都不满足条件进行全表查询-->
<where>
<if test="id != 0">
<!-- 满足条件时自动添加 where -->
id > #{id}
</if>
<if test="age != 0">
<!-- 上一个 if 不满足条件但此条满足时 or 替换为 where -->
or age > #{age}
</if>
</where>
</select>
<!--
实际执行:select * from demo WHERE name like '%' ? '%' and id > ? or age > ?
传入参数:二(String), 2(Integer), 15(Integer)
-->
<foreach>
-
循环标签
- 实现对数组与集合的遍历
- 主要用在
in
子句中in
、all
、any
等关键字语句
-
<foreach collection = "list" item = "i" open = "(" close = ")" separator = ","> #{i} </foreach> <!-- 等价于 (#{i}), (#{i}) ... ; 遍历 list 集合中所有元素-->
collection
:遍历的集合类型- 数组:
array
- List 集合:
list
- 数组:
open
:开始的字符cloae
:结束的字符item
:集合中的成员- 自定义表示数组或集合成员的变量
separator
:集合成员间的分隔符- 所有字符拼接使用属性都可以手动在 sql 语句中进行拼接
遍历 List<>
- 存放普通数据
- 使用
in
判断在满足条件的数据
- 使用
- 集合中封装对象
- 通过封装的对象属性进行数据判断
- 使用
insert into demo values(),()...;
方式可同时进行多条记录插入
// 方法定义,传入 List 集合
int addList(List<Demo> list);
// 方法测试
@Test
public void testAddList() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
ArrayList<Demo> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
list.add(new Demo(i + 101, "废物", 22, new Date())); // 准备数据
}
int rows = sqlSession.getMapper(DemoDao.class).addList(list); // 执行 sql,参数为 list 集合
sqlSession.commit();
if (rows == 1){
System.out.println("执行成功,受影响行数 " + rows);
}
sqlSession.close();
}
<insert id="addList">
insert into demo values
<!-- 遍历传入的 List 集合 -->
<foreach collection="list" item="demo" separator=",">
<!-- demo 属于 List 集合中成员,是一个对象,调用对象的属性 -->
(#{demo.id}, #{demo.name}, #{demo.age}, #{demo.date})
</foreach>
</insert>
<!--
执行:insert into demo values (?, ?, ?, ?) , (?, ?, ?, ?)
传参:108, 废物, 22, 2022-02-11, 109, 废物, 22, 2022-02-11
-->
<
- 在
xml
文件中<
会被编译器认为是标签体,造成编译错误- 使用
<![CDATA[ ]]>
使编译器忽略[]
里内容
- 使用
- 或使用
<
表示<
>
表示>
代码片段
-
复用语句
- 自定义一段 sql 语句进行重复使用
-
使用步骤
-
定义
- 片段中可以是 sql 语句任何部分
<sql id="自定义唯一名">sql语句</sql>
-
引用
<include refid="sql id值"/>
<!-- 定义 sql 片段 --> <sql id="select*"> select * from demo </sql> <!-- 引用代码片段 --> <select id="getAll" resultMap="Demo"> <include refid="select*"/> </select>
-
配置文件
主配置文件
- 标签的顺序都有要求,不能随意改变顺序
- 所有标签都在
configuration
标签内,按顺序排列properties
:属性配置settings
:全局设置typeAliases
:类型别名typeHandlers
:类型处理器:数据库类型与 Java 类型转换objectFactory
:对象工厂objectWrapperFactory
:对象加工工厂reflectorFactory
:反射工厂plugins
:插件environments
:环境配置databaseIdProvider
:数据源信息mappers
:mapper 文件映射
<?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">
<!-- mybatis-3-config.dtd:约束文件名,固定值 -->
<!-- Mybatis 核心配置文件:配置数据源、连接池、映射mapper文件等 -->
<configuration>
<!-- 全局设置 -->
<settings>
<!-- 设置 MyBatis 输出日志,将日志打印到控制台输出 -->
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>
<!-- 此标签下定义所有类型别名 -->
<typeAliases>
<!-- 自定义类别名,一个标签定义一个别名 -->
<typeAlias type="empty.Demo" alias= "Demo"/>
<!-- empty 包中所有类类名就是别名 -->
<package name = "empty"/>
</typeAliases>
<!--
环境配置:数据库的连接信息
default 值必须和某个环境值相同,是当前默认使用的数据库
-->
<environments default="MySQL">
<!--
environment:一个数据库信息的配置
id:唯一值,自定义,表示此环境的名称
-->
<environment id="MySQL">
<!--
transactionManager:MyBatis 的事务类型
type=JDBC:表示使用 JDBC 中的 Connection 对象的 commit、rollback进行事务处理
-->
<transactionManager type="JDBC"/>
<!--
dataSource:数据源,连接数据库的配置;type:表示使用连接池类型
name 值固定不能更改,value 是对应的数据库连接参数
-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/demo01"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 注册 mapper 文件到 mybatis-config.xml 中-->
<mappers>
<!-- 一个 Mapper 对应一个映射文件,resource 值为编译后 Mapper.xml 文件全路径 -->
<mapper resource="mapper/DemoMapper.xml" />
</mappers>
</configuration>
<settings>
<setting>
:MyBatis 中全局的调整设置- 会改变 MyBatis 运行时行为,谨慎设置
- 设置在 MyBatis 主配置文件
<configuration>
标签下- 一般使用默认值即可,无需修改
输出日志
- 日志类别
SLF4J
:使用 ALF4J日志框架LOG4J
:使用 LOG4J框架实现,1.x 版本LOG4J2
:使用 LOG4J 框架,2.x 版本- JDK_LOGGING:使用 java.util.logging
- COMMONS_LOGGING:使用 Apache Commons Logging 实现
STDOUT_LOGGING
:使用 System 类实现,打印到控制台输出- NO_LOGGING:不打印日志
<settings>
<!-- 设置 MyBatis,将日志打印到控制台输出 -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
全局缓存
<settings>
<!-- 影响所有映射器的缓存的全局开关,默认值 true -->
<setting name="cacheEnabled" value="true" />
</settings>
本地缓存
- 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询
- SESSION:缓存一个会话中执行的所有查询
- STATEMENT:本地会话仅用在语句执行上
- 对相同 SqlSession 的不同调用将不会共享数据
<settings>
<!-- 默认值 SESSION,缓存一个会话中执行的所有查询 -->
<setting name="localCacheScope" value="SESSION"/>
</settings>
延迟加载
- 延迟加载的全局开关
- 特定关联关系可通过设置 fetchType 属性覆盖该项全局开关
<settings>
<!-- 开启时所有关联对象都延迟加载,默认 false -->
<setting name ="lazyLoadingEnabled" value = "fasle"/>
</setting>
多结果集
<settings>
<!-- 允许单一语句返回多结果集,需要兼容驱动,默认值 true -->
<setting name ="multipleResultSetsEnabled" value = "true"/>
</setting>
列名代替表名
<settings>
<!-- 不同驱动有不同表现,具体参考相关驱动文档或测试观察结果,默认 true -->
<setting name ="useColumnLable" value = "true"/>
</setting>
自动生成主键
- 允许 JDBC 支持自动生成主键,需要驱动兼容,部分驱动不兼容但仍可工作,比如 Derby
<settings>
<!--
设为 true 强制使用自动生成主键,默认值 false -->
<setting name ="useGeneratedKeys" value = "false"/>
</setting>
指定映射方式
- 指定 MyBatis 应如何自动映射列到字段或属性
- NONE:取消自动映射
PARTIAL
:只会自动映射没有定义嵌套的结果集- FULL:自动映射任意复杂结果的结果集
- 无论是否嵌套
<settings>
<!-- 默认值 PARTIAL-->
<setting name ="autoMappingBehavior" value = "PARTIAL"/>
</setting>
执行器
- SIMPLE:普通执行器
- REUSE:执行器会重用预处理语句
- PreparedStatement
- BATCH:重用语句并执行批量更新
<settings>
<!-- 配置默认的执行器,默认 SIMPLE -->
<setting name ="defaultExecutorType" value = "SIMPLE"/>
</setting>
超时时间
<settings>
<!-- 设置超时时间,决定驱动等待数据库相应的秒数 -->
<setting name ="defaultStatementTimeout" value = "25"/>
</setting>
允许分页
<settings>
<!-- 允许在嵌套语句中使用分页(RowBounds),默认值 false -->
<setting name ="safeRowBoundsEnabled" value = "false"/>
</setting>
驼峰命名
- 从经典数据库列名 A_COLUMN 到 经典 Java 属性名 aColumn 的类似映射
<settings>
<!-- 开启驼峰命名规则,(camel case),默认值 false -->
<setting name ="mapUnderscoreToCamelCase" value = "true"/>
</setting>
配置别名
- 为单独的类配置别名
- 直接将一个包配置别名
- 不同包中有同名类时会发生冲突
- 通过注解
@Alias
指定类别名解决同名冲突- 注解在实体类上,属性值为自定义类别名
<!-- 此标签下定义所有类型别名 -->
<typeAliases>
<!-- 一个标签定义一个别名 -->
<typeAlias type="empty.Demo" alias= "Demo"/>
<!-- empty 包中所有类类名就是别名 -->
<package name = "empty"/>
<!-- 通过 @Alias 注解在类上面自定义别名解决重名冲突 -->
</typeAliases>
plugins
- 插件配置
mybatis-generator-core
- 逆向工程生成实体类
mybatis-plus
- mybatis 功能增强
- 通用
mapper
PageHelper
-
MyBatis 通用分页插件,支持多种数据库
- Oracle、MySQL、DB2、SqlServer(2225、2008、2012)
- MariaDB、SQOLite、Hsqdb、PostgreSQL
- Informix、H2、Derby、Phoenix
-
使用流程
-
添加 maven 依赖引入 jar 包
-
或下载导入 jar 包
-
<!-- 导入 Maven 依赖 --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.3.0</version> </dependency>
-
-
MyBatis 主配置文件加入 plugin 配置
- 加在
<environments>
之前,类似于过滤器
<plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"/> </plugins>
- 加在
-
查询之前调用
PageHelper.startPage
静态方法- 类似用法的
PageHelper.offsetPage
方法 - 在此方法后的第一个 MyBatis 查询方法会被分页
// 测试分页插件 @Test public void testGetAll() { // 在执行sql前调用插件方法进行分页 PageHelper.startPage(2, 10); SqlSession sqlSession = MybatisUtils.getSqlSession(); Demo demo = sqlSession.getMapper(DemoMapper.class) // 执行方法进行查询 List<Demo> demos = demo.getAll(); for (Demo demo : demos) { System.out.println(demo); } sqlSession.close(); } /* 预查询得到总记录数: SELECT count(0) FROM demo 执行的 sql:select * from demo LIMIT ?, ? 传入的参数:10(Long), 10(Integer) */
- 类似用法的
-
<environments>
<environments default="MySQL"> <!-- 指定默认使用的环境 -->
<environment id="MySQL"> <!-- 配置环境 -->
<!-- 事务处理 -->
<transactionManager type="JDBC"/>
<!-- 配置数据源 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/demo01"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
事务处理
-
<transactionManager type="JDBC"/>
-
transactionManager
:事务处理机制 -
type
:事务处理的类型-
JDBC
- MyBatis 底层调用 JDBC 中 Connection 对象的 commit、rollback 等方法处理事务
-
MANAGED
- 把 MyBatis 的事物处理委托给其他的容器
- 服务器软件、框架(Spring) 等
- 把 MyBatis 的事物处理委托给其他的容器
-
-
dataSource
-
<dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/demo01"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource>
-
dataSource
:数据源- Java 中 规定实现了
javax.sql.DataSource
接口的都是数据源- 接口中抽象方法:
getConnection()
- 接口中抽象方法:
- 用来获取数据库连接对象
- 设置数据库连接参数
- Java 中 规定实现了
-
type:指定数据源的类型
-
POOLED
:使用连接池- MyBatis 创建 PooledDataSource 类
-
UNPOOLED
:不使用连接池-
每次执行 sql 语句先创建连接、执行 sql
、关闭连接
-
MyBatis 创建 UnpooledDataSource 类
-
-
JNDI~
- Java 命名和目录服务
- 类似 windows 注册表
- 很古老的方式
- Java 命名和目录服务
-
-
属性配置文件
-
把数据库连接信息放到单独的文件,和 MyBatis 主配置文件分开
- 便于修改、保存,处理多个数据库的信息
-
流程
-
resource
目录中定义属性配置文件- 例如:
mybatis.properties
- 例如:
-
文件中 key 值一般以目录名拼接方便区分所属的环境配置
-
# MySQL 环境的外部属性配置文件 MySQL.driver=com.mysql.jdbc.Driver MySQL.url=jdbc:mysql://loaclhost:3306/demo01 MySQL.username=root MySQL.password=123456
-
-
在 MyBatis 主配置文件使用
<property>
标签指定文件位置-
需要写值的位置引用:
${key}
key
值即为外部配置文件中的key
<!-- 使用外部配置文件 --> <!-- 引用配置文件,从类的根路径开始找 --> <properties resource="db.properties"/> <environments default="MySQL"> <environment id="MySQL"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <!-- 引用配置文件中的值 --> <property name="driver" value="${MySQL.driver}"/> <property name="url" value="${MySQL.url}"/> <property name="username" value="${MySQL.username}"/> <property name="password" value="${MySQL.password}"/> </dataSource> </environment> </environments>
-
-
mapper 映射
-
两种常用方式
- 单独指定类路径:资源定位
<mapper resource="mapper/DemoMapper.xml" />
- 一个 mapper 标签对应一个 mapper.xml 文件
/
路径分隔符
- 指定同一报下所有文件:包定位
<package name="empty">
- name:
mapper.xml
文件所在包名全路径 - 将包内所有 mapper 映射文件注册为映射器
- name:
- 使用条件
mapper.xml
文件和 dao 接口名称必须一样- 区分大小写
mapper.xml
文件和 dao 接口在同一目录下
- 单独指定类路径:资源定位
-
其他方式
- 使用 dao 接口全限定名:类定位
<mapper class="dao接口全限定名">
- dao 接口和
mapper.xml
文件必须在同一个包下 - dao 接口和
mapper.xml
文件名称必须完全一致
- dao 接口和
- 使用完全限定资源定位符
<mapper url="file:mapper.xml文件路径">
- 使用 dao 接口全限定名:类定位
<!-- 注册 mapper 文件到 mybatis-config.xml 中-->
<mappers>
<!-- 一个 Mapper 对应一个映射文件;resource 值为编译后 Mapper.xml 文件全路径 -->
<mapper resource="mapper/DemoMapper.xml" />
<!-- mapper 包中所有 mapper.xml 文件都加载给 MyBatis -->
<package name="mapper">
<!-- 使用映射器接口实现类的完全限定类名 -->
<mapper class="org.mybatis.builder.AuthorMapper"/>
<!-- 使用完全限定资源定位符(URL) -->
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
</mappers>
日志
-
输出执行日志以查询执行流程
-
<setting name="logImpl" value="日志类型">
STDOUT_LOGGING
- 标准日志输出
- 依靠 Java 的 System 执行,将 sql 执行流程打印到控制台显示
LOG4J
-
作用
- 可以将日志输出到控制台、文件、组件、服务器 等位置
- 控制日志输出格式
- 定义日志信息级别,控制日志生成过程
- 通过配置文件灵活配置,不需要修改应用代码
-
要求
-
需要添加 jar 包才能使用
-
配置 log4j 日志设置
-
在要使用的类中加载日志对象
-
参数为当前类 Class 对象
static Logger log = Logger.getLogger(要使用类的 Class 对象)
-
-
缓存
作用
- 存在内存中的临时数据
- 将用户常查询的数据放到缓存中
- 再次查询时就不用再到磁盘读取
- 关系型数据库
- 从缓存中查询效率更高,提高性能
- 使用缓存可以减少数据库的交互次数
- 减少系统开销,提高系统效率
- 经常查询且不经常改变的数据应放在缓存中
MyBatis 缓存
- 可以方便的定制和配置缓存
- MyBatis 系统默认定义了两级缓存
- 一级缓存、二级缓存
- 默认只开启一级缓存
SqlSession
级别,本地缓存
- 二级缓存需要手动开启和配置
- 基于
namespace
级别的缓存
- 基于
- 为提高拓展性 MyBatis 提供缓存接口 Cache
- 可以通过实现 Cache 接口自定义二级缓存
一级缓存
- 当一个新
session
被创建,MyBatis
就会创建一个相关联的本地缓存- 任何在
session
执行过的查询结果都会被保存在本地缓存中
- 任何在
- 当再次执行参数相同的相同查询时,不需要实际查询数据库了
- 本地缓存将会在做出修改、事务提交或回滚,以及关闭 session 时清空
- 默认情况下,本地缓存数据的生命周期等同于整个 session 的周期
- 缓存会被用来解决循环引用问题和加快重复嵌套查询的速度
- 所以无法将其完全禁用
- 可以通过设置
localCacheScope=STATEMENT
只在语句执行时使用缓存
- 缓存会被用来解决循环引用问题和加快重复嵌套查询的速度
localCacheScope
被设置为SESSION
- 对于某个对象,MyBatis 将返回在本地缓存中唯一对象的引用
- 对返回的对象(例如 list)做出的任何修改将会影响本地缓存的内容
- 进而将会影响到在本次 session 中从缓存返回的值
- 因此,不要对 MyBatis 所返回的对象作出更改,以防后患
二级缓存
开启
-
启用全局的二级缓存需要在的 SQL 映射文件中添加一行
mapper.xml
文件中
-
缓存只作用于
cache
标签所在的映射文件中的语句- 混合使用 Java API 和 XML 映射文件
- 在共用接口中的语句将不会被默认缓存
- 需要使用
@CacheNamespaceRef
注解指定缓存作用域
<cache/> <!-- 这一句即可开启全局二级缓存 -->
- 混合使用 Java API 和 XML 映射文件
-
语句作用
-
映射语句文件中的所有 select 语句的结果将会被缓存
-
映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存
-
缓存会使用最近最少使用算法来清除不需要的缓存,(LRU, Least Recently Used)算法
LRU
:最近最少使用:移除最长时间不被使用的对象- 默认使用
FIFO
:先进先出:按对象进入缓存的顺序来移除它们SOFT
:软引用:基于垃圾回收器状态和软引用规则移除对象WEAK
:弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象
-
-
缓存不会定时进行刷新,即没有刷新间隔
-
缓存会保存列表或对象的 1024 个引用
- 无论查询方法返回哪种
-
缓存会被视为读/写缓存
- 意味着获取到的对象并不是共享的
- 可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改
属性
<!-- 自定义二级缓存的属性 -->
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
- 配置创建了一个
FIFO
缓存- 每隔 60 秒刷新
- 最多可以存储结果对象或列表的 512 个引用
- 返回的对象被认为是只读的
- 对它们进行修改可能会在不同线程中的调用者产生冲突
flushInterval
:刷新间隔,任意的正整数- 值以毫秒为单位的合理时间量
- 默认不设置,没有刷新间隔,缓存仅仅会在调用语句时刷新
size
:引用数目,任意正整数- 注意欲缓存对象的大小和运行环境中可用的内存资源
- 默认值是 1024
readOnly
:只读;true 或 false- 只读:给所有调用者返回缓存对象的相同实例
- 因此这些对象不能被修改;提供了可观的性能提升
- 可读写:通过序列化返回缓存对象的拷贝
- 速度上会慢一些,但是更安全,因此默认使用
- 默认值是 false
- 只读:给所有调用者返回缓存对象的相同实例
自定义缓存
- 自己的缓存
- 或为其他第三方缓存方案创建适配器,来完全覆盖缓存行为
- 二级缓存的配置不能应用于自定义缓存
- 如清除策略、可读或可读写等
<cache type="com.domain.something.MyCustomCache"/>
<!-- 使用一个自定义的缓存实现
type 属性指定的类必须实现 org.apache.ibatis.cache.Cache 接口,且提供一个接受 String 参数作为 id 的构造器
-->
完整示例
-
使用 Eclipse 完成
- 真难用这玩意儿
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>li_maven</groupId>
<artifactId>mybatis-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<!-- mysql连接java的驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
<!-- mybatis 依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<!-- pagehelper分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.0</version>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<!-- 属性配置 -->
<properties>
<project.build.sourceEncoding>utf8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<build>
<plugins>
<!-- 更新测试插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M3</version>
<configuration>
<forkCount>0</forkCount>
<testFailureIgnore>true</testFailureIgnore>
</configuration>
</plugin>
</plugins>
<!-- 检索 src/main/resource 目录下所有文件,包括 properties、xml 文件 -->
<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>
</project>
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">
<!-- mybatis-3-config.dtd:约束文件名,固定值 -->
<!-- Mybatis 核心配置文件:配置了数据源、连接池、映射mapper文件 -->
<configuration>
<settings>
<!-- 设置 MyBatis 输出日志,将日志打印到控制台输出 -->
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>
<!-- 此标签下定义所有类型别名 -->
<typeAliases>
<!-- 单独定义类别名 -->
<!-- <typeAlias type="demo.empty.Demo" alias="Demo"/> -->
<!-- 定义包中所有类类名为别名 -->
<package name = "demo.empty"/>
</typeAliases>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>
<!--
环境配置:数据库的连接信息
default 值必须和某个环境值相同,是当前默认使用的数据库
-->
<environments default="MySQL">
<!--
environment:一个数据库信息的配置
id:唯一值,自定义,表示此环境的名称
-->
<environment id="MySQL">
<!--
transactionManager:MyBatis 的事务类型
type=JDBC:表示使用 JDBC 中的 Connection 对象的 commit、rollback进行事务处理
-->
<transactionManager type="JDBC"/>
<!--
dataSource:数据源,连接数据库的配置
type:表示使用连接池
name 值固定不能更改,value 是当前对应的数据库连接参数
-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/demo01"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 注册mapper文件到mybatis-config.xml中-->
<mappers>
<!-- 一个 Mapper 对应一个映射文件,resource 值为编译后 Mapper.xml 文件全路径 -->
<!-- <package name="demo/mapper/DemoMapper.xml"/> -->
<!-- 包中所有 xml 文件都加载到 mybatis -->
<mapper resource="demo/mapper/DemoMapper.xml" />
</mappers>
</configuration>
empty
Demo
package demo.empty;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Objects;
public class Demo {
private int id;
private String name;
private int age;
private Date date;
public Demo() {}
public Demo(Date date) {
this.date = date;
}
public Demo(int id, String name, int age, Date date) {
this.id = id;
this.name = name;
this.age = age;
this.date = date;
}
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 int getAge() { return age;}
public void setAge(int age) this.age = age }
public Date getDate() { return date; }
public void setDate(Date date) { this.date = date; }
private String changeDate(Date date) {
return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(date);
}
@Override
public int hashCode() { return Objects.hash(age, date, id, name);}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Demo other = (Demo) obj;
return age == other.age && Objects.equals(date, other.date) && id == other.id
&& Objects.equals(name, other.name);
}
@Override
public String toString() {
return "id = " + id +
"\t name = " + name +
"\t age = " + age +
"\t date = " + changeDate(date);
}
}
mapper
DemoMapper.java
package demo.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import demo.empty.Demo;
public interface DemoMapper {
/**
* 获取全部记录
* @return 返回记录的集合
* 一条记录封装到一个对象
*/
List<Demo> getAll();
/**
* 分页查询所有记录
* @param pageNum 页码
* @param pageSice 一页记录数
* @return 返回查询结果
*/
List<Demo> getAllByPage(int pageNum, int pageSice);
/**
* 排序查询
* @param colName 要排序的列
* @return 返回查询结果
*/
List<Demo> getByOrder(String colName);
/**
* 通过 id name 精确查找
* @param id id
* @param name name
* @return 返回记录
*/
Demo getByIdName(@Param("id") int id, @Param("name") String name);
/**
* 模糊查询
* @param name1 模糊名1
* @param name2 模糊名2
* @return 返回满足条件的记录
*/
List<Demo> getByLike(@Param("name1") String name1, @Param("name2") String name2);
/**
* 使用if、where 完成查询
* @param demo 传入对象参数,使用对象属性作为参数
* @return 返回查询结果
*/
List<Demo> getByIf(Demo demo);
/**
* 添加默认记录,时间为添加时的时间
* 使用 statement 执行,无参数追加
* @return 返回受影响行数
*/
int addDefault();
/**
* 通过 list 集合添加多条记录
* @param unm 添加记录数
* @param list 传入封装了Demo 对象的 List 集合
* @return 返回受影响行数
*/
int addSome(int unm, @Param("list") List<Demo> list);
/**
* 自定义添加数据
* @param demo 传入 Demo 对象
* 使用对象属性传参
* @return 返回受影响行数
*/
int add(Demo demo);
/**
* 修改记录,通过 id 修改
* @param demo 传入Demo 对象
* 根据 demo 属性内容自动进行参数修改,id不改变
* @return 返回受影响行数
*/
int update(Demo demo) ;
}
DemoMapper.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">
<!--
上面代码指定约束文件
mybatis-3-mapper.dtd:约束文件名,拓展名 dtd
约束文件作用:限制、检查当前文件中出现的标签、属性名必须符合 MyBatis 要求
-->
<!-- namespace:命名空间,自定义唯一值;常规使用 dao 接口全限定名 -->
<mapper namespace="demo.mapper.DemoMapper">
<!-- 字段名和属性名的映射关系 -->
<resultMap id="Demo" type="demo.empty.Demo">
<!-- 主键列使用 id column -->
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
<result column="date" property="date"/>
</resultMap>
<!-- sql 语句片段,可复用 -->
<sql id="select*">
select * from demo
</sql>
<select id="getAll" resultMap="Demo">
<!-- 复用 sql 片段 -->
<include refid="select*"/>
</select>
<select id="getAllByPage" resultMap="Demo">
<include refid="select*"/>
</select>
<select id="getByOrder" resultMap="Demo">
<!-- 根据自定义列名排序查找 -->
<include refid="select*"/> order by ${colName}
</select>
<select id="getByIdName" resultMap="Demo">
<include refid="select*"/> where id = #{id} and name = #{name}
</select>
<select id="getByLike" resultMap="Demo">
<include refid="select*"/>
where name like '%' #{name1} '%' or name like '%' #{name2} '%'
</select>
<select id="getByIf" resultMap="Demo">
<!-- 满足 if 条件时按条件查找,否则全部查找 -->
<include refid="select*"/>
<where>
<if test="id != 0">
id > #{id}
</if>
<if test="age != 0">
and age > #{age}
</if>
</where>
</select>
<insert id="addDefault" statementType="STATEMENT">
insert into demo(date) values (now());
</insert>
<!-- 批量添加默认数据 -->
<insert id="addSome">
insert into demo values
<foreach collection="list" item="demo" separator=",">
(#{demo.id}, #{demo.name}, #{demo.age}, #{demo.date})
</foreach>
</insert>
<!-- 添加记录 -->
<insert id="add">
insert into demo values(#{id}, #{name}, #{age}, #{date})
</insert>
<!-- 根据属性值内容修改记录,根据 id 查找且 id 不修改 -->
<update id="update">
update demo set id = #{id}
<if test="name != null">
, name = #{name}
</if>
<if test="age != 0">
, age = #{age}
</if>
<if test="date != null">
, date = #{date}
</if>
where id = #{id}
</update>
</mapper>
utils
MybatisUtils
- 工具类
- 静态加载 MyBatis 配置文件
- 提供获取 SqlSession 对象的方法
- 提供关闭 SqlSession 对象的方法
package utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class MybatisUtils {
private static SqlSessionFactory factory;
// 静态代码块读取 mybatis 配置文件,获取工厂对象
static{
try {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
factory = new SqlSessionFactoryBuilder().build(in);
} catch (IOException e) {
e.printStackTrace();
}
}
// 获取 SqlSession 对象
public static SqlSession getSqlSession() {
SqlSession sqlSession = null;
if (factory != null) {
sqlSession = factory.openSession();
}
return sqlSession;
}
// 关闭连接资源
public static void close(SqlSession sqlSession){
if (sqlSession != null){
sqlSession.close();
}
}
}
test
TestDemoMapper
package testDemoMapper;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import com.github.pagehelper.PageHelper;
import demo.empty.Demo;
import demo.mapper.DemoMapper;
import demo.utils.MybatisUtil;
public class TestDemoMapper {
@Test
public void testGetAll() {
SqlSession sql = MybatisUtil.getSqlSession();
List<Demo> demos = sql.getMapper(DemoMapper.class).getAll();
for(Demo demo : demos) {
System.out.println(demo);
}
sql.close();
}
@Test
public void testGetAllByPage() {
SqlSession sql = MybatisUtil.getSqlSession();
PageHelper.startPage(2, 10);
List<Demo> demos = sql.getMapper(DemoMapper.class).getAllByPage(2, 10);
for(Demo demo : demos) {
System.out.println(demo);
}
sql.close();
}
@Test
public void testGetByOrder() {
SqlSession sql = MybatisUtil.getSqlSession();
List<Demo> demos = sql.getMapper(DemoMapper.class).getByOrder("age");
for(Demo demo : demos) {
System.out.println(demo);
}
sql.close();
}
@Test
public void testGetByIdName() {
SqlSession sql = MybatisUtil.getSqlSession();
Demo demo = sql.getMapper(DemoMapper.class).getByIdName(5, "张三");
System.out.println(demo);
sql.close();
}
@Test
public void testGetByLike() {
SqlSession sql = MybatisUtil.getSqlSession();
List<Demo> demos = sql.getMapper(DemoMapper.class).getByLike("二", "三");
for(Demo demo : demos) {
System.out.println(demo);
}
sql.close();
}
@Test
public void testGetByIf() {
SqlSession sql = MybatisUtil.getSqlSession();
Demo demo = new Demo();
demo.setAge(10);
demo.setName("张三");
List<Demo> demos = sql.getMapper(DemoMapper.class).getByIf(demo);
for(Demo demo1 : demos) {
System.out.println(demo1);
}
sql.close();
}
@Test
public void testAddDefault() {
SqlSession sql = MybatisUtil.getSqlSession();
int rows = sql.getMapper(DemoMapper.class).addDefault();
sql.commit();
System.out.println("受影响行数 " + rows);
sql.close();
}
@Test
public void testAddSome() {
SqlSession sql = MybatisUtil.getSqlSession();
List<Demo> list = new ArrayList<>();
for(int i = 0; i < 5; i++) {
list.add(new Demo(128 + i, "废狗", 22, new Date()));
}
int rows = sql.getMapper(DemoMapper.class).addSome(5, list);
sql.commit();
System.out.println("受影响行数 " + rows);
sql.close();
}
@Test
public void testAdd() {
SqlSession sql = MybatisUtil.getSqlSession();
int rows = sql.getMapper(DemoMapper.class).add(new Demo(110, "废狗", 23, new Date()));
sql.commit();
System.out.println("受影响行数 " + rows);
sql.close();
}
@Test
public void testUpdate() {
SqlSession sql = MybatisUtil.getSqlSession();
Demo demo = new Demo();
demo.setId(100);
demo.setName("李二狗");
int rows = sql.getMapper(DemoMapper.class).update(demo);
sql.commit();
System.out.println("受影响行数 " + rows);
}
}