文章目录
MyBatis(简化数据库操作的持久层框架)
1 MyBatis 介绍
官方文档
需要什么 jar 包,搜索得到对应的 maven dependency
为什么需要 MyBatis
● 传统的 Java 程序操作 DB 分析
Mybatis基本介绍
- MyBatis 是一个持久层框架
- 前身是 ibatis, 在 ibatis3.x 时,更名为 MyBatis
- MyBatis 在 java 和 sql 之间提供更灵活的映射方案
- mybatis 可以将对数据表的操作(sql,方法)等等直接剥离,写到 xml 配置文件,实现和 java代码的解耦
- mybatis 通过 SQL 操作 DB, 建库建表的工作需要程序员完成
MyBatis 工作原理
2 MyBatis 快速入门
快速入门需求说明
要求: 开发一个 MyBatis 项目,通过 MyBatis 的方式可以完成对 monster 表的 crud 操作
快速入门-代码实现
- 创建 mybatis 数据库 - monster 表
CREATE DATABASE `mybatis`
USE `mybatis`
CREATE TABLE `monster` (
`id` INT NOT NULL AUTO_INCREMENT,
`age` INT NOT NULL,
`birthday` DATE DEFAULT NULL,
`email` VARCHAR(255) NOT NULL,
`gender` TINYINT NOT NULL, # TINYINT 的 0 1分别表示男女
`salary` DOUBLE NOT NULL,
PRIMARY KEY(`id`)
) CHARSET=utf8
导入依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<!--表示该jar包作用范围在test目录下, 在src目录下没法使用-->
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
</dependencies>
- 创建 resources/mybatis-config.xml
这个是作为mybatis的配置文件。从mybatis官方文档中找出,直接复制粘贴
mybatis-config.xml: (不一定非得叫这个名)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--下面的这些配置的顺序还是有讲究的-->
<properties resource="application.properties"/> <!-- 告诉mybatis-config.xml我的外部的properties文件在哪里 -->
<!--配置Mybatis自带的日志输出, 查看原声的sql-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--设置类型别名-->
<typeAliases>
<typeAlias type="com.maven.quickstart.entity.Monster" alias="Monster"/>
</typeAliases>
<environments default="development">
<environment id="development">
<!--配置事务管理器-->
<transactionManager type="JDBC"/>
<!--配置数据源-->
<dataSource type="POOLED">
<!--配置驱动-->
<property name="driver" value="${driver}"/>
<!--配置连接mysql-url
老韩解读:
1. jdbc:mysql 协议
2. 127.0.0.1:3306 : 指定连接mysql的ip+port
3. mybatis: 连接的DB
4. useSSL=true 表示使用安全连接
5. & 表示 & 防止解析错误
6. useUnicode=true : 使用unicode 作用是防止编码错误
7. characterEncoding=UTF-8 指定使用utf-8, 防止中文乱码
8. 老韩温馨提示:不要背,直接使用即可
-->
<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!--这里我们配置需要关联的Mapper.xml文件-->
<mappers>
<mapper resource="com/maven/quickstart/mapper/MonsterMapper.xml"/>
</mappers>
</configuration>
上面的某些东西如果不想配置的非常死,可能需要从外面导入properties配置文件。
这里注意,properties配置文件需要在下面的特定的路径下:项目名/src/main/resources/xxx.properties
application.properties (作为mybatis-config.properties的属性文件)
driver=com.mysql.cj.jdbc.Driver
username=root
password=输入密码
- 创 建
D:\idea_java_projects\HSP_MyBatis\01_mybatis_quickstart\src\main\java\com\hspedu\entity\Monster.java
要求大家这里的实体类属性名和表名字段保持一致。
public class Monster {
private Integer id;
private Integer age;
private String name;
private String email;
private Date birthday;
private double salary;
private Integer gender;
....方法....
}
- 创 建
D:\idea_java_projects\HSP_MyBatis\01_mybatis_quickstart\src\main\java\com\hspedu\mapper\MonsterMapper.java
这里的mapper是一个接口,写了很多的方法, 是用于声明操作monster表的方法. 这些方法可以通过注解或xml文件来实现.
/**
* @Author: GQLiu
* @DATE: 2024/3/7 22:29
* 接口, 用于生命操作monster表的方法
* 这些方法可以通过注解或xml文件来实现。
*/
public interface MonsterMapper {
// 添加monster
public void addMonster(Monster monster);
// 根据id删除monster
public void delMonster(Integer id);
// 修改Monster
public void updateMonster(Monster monster);
// 根据id查询
public Monster getMonsterById(Integer id);
// 查询所有的Monster
public List<Monster> findAllMonster();
}
- 创 建
D:\idea_java_projects\HSP_MyBatis\01_mybatis_quickstart\src\main\java\com\hspedu\mapper\MonsterMapper.xml
这是一个mapper xml文件
该文件可以去实现对应的接口的方法。
namespace 指定该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 xml文件
该文件可以去实现对应的接口的方法。
namespace 指定该xml文件和哪个接口对应
-->
<mapper namespace="com.maven.quickstart.mapper.MonsterMapper">
<!--
配置addMonster
id=addMonster 就是接口的方法名
parameterType=“com.maven.quickstart.entity.Monster" 就是放入的形参的类型
注意com.maven.quickstart.entity.Monster 可以简写
写sql语句 => 建议先在navicat中写,测试通过后再拿过来
(`age`, `birthday`, `email`, `gender`, `name`, `salary`) 表示表的数据
(#{age},#{birthday}, #{email}, #{gender}, #{name}, #{salary}) 表示传入的monster的对象属性
#{age} 表示monster的属性名。
useGeneratedKeys="true" 表示取出mysql自己设置的主键
keyProperty="id" 表示主键为 id
-->
<insert id="addMonster" parameterType="Monster" useGeneratedKeys="true" keyProperty="id">
INSERT INTO `monster`
(`age`, `birthday`, `email`, `gender`, `name`, `salary`)
VALUES (#{age},#{birthday}, #{email}, #{gender}, #{name}, #{salary})
</insert>
<!--
配置实现delMonster方法, 根据id删除Monster
这里的 java.lang.Integer 是java类型, 可以简写成Integer
-->
<delete id="delMonster" parameterType="Integer">
DELETE FROM `monster` WHERE id=#{id};
</delete>
<!--配置实现updateMonster-->
<update id="updateMonster" parameterType="Monster">
UPDATE `monster` SET `age`=#{age}, `birthday` = #{birthday}, `email`=#{email}, `gender`=#{gender}, `name`=#{name}, `salary` = #{salary} WHERE id=#{id}
</update>
<!--配置实现根据id查询monster-->
<select id="getMonsterById" resultType="Monster">
SELECT * FROM `monster` WHERE id = #{id}
</select>
<!--配置实现查询所有的Monster. 返回所有 monster, resultType也是Monster而不是List<Monster>-->
<select id="findAllMonster" resultType="Monster">
SELECT * FROM `monster`
</select>
</mapper>
- 修 改D:\idea_java_projects\HSP_MyBatis\01_mybatis_quickstart\src\main\resources\mybatis-config.xml
</environments>
<mappers>
<!-- 这里会引入(注册)我们的 Mapper.xml 文件 -->
<mapper resource="com/hspedu/mapper/MonsterMapper.xml"/>
</mappers>
- 创 建 工 具 类D:\idea_java_projects\HSP_MyBatis\01_mybatis_quickstart\src\main\java\com\hspedu\util\MyBatisUtils.java
工具类提供了获取SqlSession实例的方法. SqlSession可以提供对数据库执行sql命令所需的所有方法. 因此可以通过SqlSession实例来执行已映射的SQL语句.
/**
* 工具类, 用于得到SqlSession
*/
public class MyBatisUtils {
// 会话工厂属性
private static SqlSessionFactory sqlSessionFactory;
// 编写静态代码块, 读取资源配置文件.
static {
try {
// 指定资源文件, 配置文件mybatis-config.xml
String resource = "mybatis-config.xml";
// 获取到配置文件mybatis-config.xml 对应的输入流inputstream
// 这里在加载文件时, 默认到resouces目录 => 运行后的工作目录 classes
InputStream resourceAsStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
System.out.println("sqlSessionFactory=" + sqlSessionFactory.getClass());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// 编写方法, 返回SqlSession对象会话
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession(); // 返回会话 是通过openSession方法得到的.
}
}
- 创 建D:\idea_java_projects\HSP_MyBatis\01_mybatis_quickstart\src\test\java\com\hspedu\mapper\MonsterMapperTest.java
是在test目录下创建的.
public class MonsterMapperTest {
// 属性
private SqlSession sqlSession;
private MonsterMapper monsterMapper;
// 编写方法完成初始化
@Before // @Before标注的方法会在 JTest执行前就执行的方法
public void init() {
// 获取到sqlSession
sqlSession = MyBatisUtils.getSqlSession();
// 获取到MonsterMapper对象 (代理对象:MapperProxy)
// 底层使用了动态代理机制
monsterMapper = sqlSession.getMapper(MonsterMapper.class);
System.out.println("monsterMapper=" + monsterMapper);
}
@Test
public void addMonster() {
for(int i=0;i<2;i++) {
Monster monster = new Monster();
monster.setAge(10 + i);
monster.setBirthday(new Date());
monster.setEmail("kate@qq.com");
monster.setGender(1);
monster.setName("松鼠精#" + i);
monster.setSalary(1000 + i * 10);
monsterMapper.addMonster(monster);
System.out.println("添加对象--" + monster);
}
// 如果是增删改, 需要提交事务
if(sqlSession != null) {
sqlSession.commit();
sqlSession.close(); //把获取的连接释放给连接池,不是真正关闭
}
System.out.println("添加成功");
}
@Test
public void delMonster() {
monsterMapper.delMonster(20);
// 如果是增删改, 需要提交事务
if(sqlSession != null) {
sqlSession.commit();
sqlSession.close();
}
System.out.println("删除成功");
}
@Test
public void updateMonster() {
for(int i=0;i<2;i++) {
Monster monster = new Monster();
monster.setAge(550 + i);
monster.setBirthday(new Date());
monster.setEmail("2222@qq.com");
monster.setGender(1);
monster.setName("老鼠精#" + i);
monster.setSalary(100000 + i * 10);
monster.setId(22+i);
monsterMapper.updateMonster(monster);
System.out.println("修改对象--" + monster);
}
// 如果是增删改, 需要提交事务
if(sqlSession != null) {
sqlSession.commit();
sqlSession.close(); //把获取的连接释放给连接池,不是真正关闭
}
System.out.println("修改成功");
}
@Test
public void getMonsterById() {
Monster monster = monsterMapper.getMonsterById(24);
System.out.println("monster=" + monster);
// 查询语句不需要提交, 但是需要释放连接
if(sqlSession != null) {
sqlSession.close(); // 释放连接到连接池
}
System.out.println("查询成功");
}
@Test
public void findAllMonster(){
List<Monster> allMonster = monsterMapper.findAllMonster();
for (Monster monster : allMonster) {
System.out.println(monster);
}
if(sqlSession != null) {
sqlSession.close();
}
System.out.println("查询成功");
}
@Test
public void t1(){
System.out.println("t1()....");
}
@Test
public void t2(){
System.out.println("t2()....");
}
}
-
测试,看看是否可以添加成功, 这时会出现找不到 Xxxxmapper.xml 错误, 分析了原因
(实际执行的东西是在target目录下.)
-
解决找不到 Mapper.xml 配置文件问题, 老韩提示:如果顺利,你会很快解决,不顺利,你 会 干 到 怀 疑 人 生
//在父工程的 pom.xml 加入 build 配置
<!--
在build种配置resources, 防止我们资源导出失败的问题.
1. 不同idea/maven版本可能提示错误不一样.
2. 以不变应万变, 少什么文件, 就增加相应的配置即可.
3. 含义是将 src/main/java目录和子目录以及 src/main/resources目录和子目录下的资源文件xml和properties在build项目时,导出到对应target目录下
-->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
</build>
删
- 修改 MonsterMapper.java, 增加方法接口
//根据 id 删除一个 Monster
public void delMonster(Integer id);
- 修改 MonsterMapper.xml, 实现方法接口
<!--
配置实现delMonster方法, 根据id删除Monster
这里的 java.lang.Integer 是java类型, 可以简写成Integer
-->
<delete id="delMonster" parameterType="Integer">
DELETE FROM `monster` WHERE id=#{id};
</delete>
- 完 成 测 试 , 修 改D:\idea_java_projects\HSP_MyBatis\01_mybatis_quickstart\src\test\java\com\hspedu\mapper\MonsterMapperTest.java, 增加测试方法
@Test
public void delMonster() {
monsterMapper.delMonster(20);
// 如果是增删改, 需要提交事务
if(sqlSession != null) {
sqlSession.commit();
sqlSession.close();
}
System.out.println("删除成功");
}
改 查
- 修改 MonsterMapper.java, 增加方法接口
//修改 Monster
public void updateMonster(Monster monster);
//查询-根据 id
public Monster getMonsterById(Integer id);
//查询所有的 Monster
public List<Monster> findAllMonster();
- 修改 MonsterMapper.xml, 实现方法接口
<!--配置实现updateMonster-->
<update id="updateMonster" parameterType="Monster">
UPDATE `monster` SET `age`=#{age}, `birthday` = #{birthday}, `email`=#{email}, `gender`=#{gender}, `name`=#{name}, `salary` = #{salary} WHERE id=#{id}
</update>
<!--配置实现根据id查询monster-->
<select id="getMonsterById" resultType="Monster">
SELECT * FROM `monster` WHERE id = #{id}
</select>
<!--配置实现查询所有的Monster. 返回所有 monster, resultType也是Monster而不是List<Monster>-->
<select id="findAllMonster" resultType="Monster">
SELECT * FROM `monster`
</select>
- 为了配置方便,在 mybatis-config.xml 配置 Monster 的别名
找到Mybatis中文官网, 配置类型别名:
<!--设置类型别名-->
<typeAliases>
<typeAlias type="com.maven.quickstart.entity.Monster" alias="Monster"/>
</typeAliases>
- 修改 MonsterMapper.xml, 实现方法接口, 可以使用 Monster 别名了.
parameterType="Monster"写的是Monster而不是com.maven.quickstart.entity.Monster
<!--配置实现updateMonster-->
<update id="updateMonster" parameterType="Monster">
UPDATE `monster` SET `age`=#{age}, `birthday` = #{birthday}, `email`=#{email}, `gender`=#{gender}, `name`=#{name}, `salary` = #{salary} WHERE id=#{id}
</update>
<!--配置实现根据id查询monster-->
<select id="getMonsterById" resultType="Monster">
SELECT * FROM `monster` WHERE id = #{id}
</select>
<!--配置实现查询所有的Monster. 返回所有 monster, resultType也是Monster而不是List<Monster>-->
<select id="findAllMonster" resultType="Monster">
SELECT * FROM `monster`
</select>
- 完成测试, 修改 MonsterMapperTest.java , 增加测试方法
@Test
public void getMonsterById() {
Monster monster = monsterMapper.getMonsterById(24);
System.out.println("monster=" + monster);
// 查询语句不需要提交, 但是需要释放连接
if(sqlSession != null) {
sqlSession.close(); // 释放连接到连接池
}
System.out.println("查询成功");
}
@Test
public void findAllMonster(){
List<Monster> allMonster = monsterMapper.findAllMonster();
for (Monster monster : allMonster) {
System.out.println(monster);
}
if(sqlSession != null) {
sqlSession.close();
}
System.out.println("查询成功");
}
日志输出-查看 SQL
- 在开发 MyBatis 程序时,比如执行测试方法,程序员往往需要查看 程序底层发给 MySQL的 SQL 语句, 到底长什么样, 怎么办?
2. 解决方案: 日志输出
日志文档 https://mybatis.org/mybatis-3/zh/logging.html
配置日志 https://mybatis.org/mybatis-3/zh/configuration.html#settings
配置日志-具体操作
-
查看文档 : https://mybatis.org/mybatis-3/zh/configuration.html#settings
-
修 改
D:\java_projects\HSP_MyBatis\01_mybatis_quickstart\src\main\resources\mybatis-config.xml, 加入日志输出配置, 方便分析 SQL 语句
<!--配置Mybatis自带的日志输出, 查看原声的sql-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
SqlSession原生API的调用
创建E:\JavaCode\mybatis\mybatits_quickstart\src\test\java\com\maven\quickstart\com\mapper\MyBatisNativeTest.java,演示SqlSession原生API的调用
这里虽然是调用的原生API,但是还是需要到MonsterMapper.xml文件中去找对应方法的实现语句。所以本质前面的是一样的。方法先到MonsterMapper.java接口中,然后再到MonsterMapper.xml文件中找具体的实现。
/**
* @Author: GQLiu
* @DATE: 2024/3/10 11:17
* 演示使用MyBatis原生API操作DB
*/
public class MyBatisNativeTest {
private SqlSession sqlSession;
private MonsterMapper monsterMapper;
@Before
public void init(){
sqlSession = MyBatisUtils.getSqlSession();
System.out.println("sqlSession==" + sqlSession.getClass()); // DefaultSqlSession
}
// 使用SqlSession原生的API调用我们编写的方法【了解】
@Test
public void myBatisNativeCrud() {
// 添加
/**
* @Override
* public int insert(String statement, Object parameter) {
* return update(statement, parameter);
* }
* statement 就是接口方法的完整声明
* parameter: 入参
*
* */
Monster monster = new Monster();
monster.setAge(1099);
monster.setBirthday(new Date());
monster.setEmail("kat他e@qq.com");
monster.setGender(1);
monster.setName("松鼠精安抚#");
monster.setSalary(1000);
// 返回受影响的行数
int insert = sqlSession.insert("com.maven.quickstart.mapper.MonsterMapper.addMonster", monster);
System.out.println(insert);
// 删除
sqlSession.delete("com.maven.quickstart.mapper.MonsterMapper.delMonster", 26);
// 修改
Monster monster1 = new Monster();
monster1.setAge(1099);
monster1.setBirthday(new Date());
monster1.setEmail("kat他e@qq.com");
monster1.setGender(1);
monster1.setName("松鼠精安抚#");
monster1.setSalary(1000);
monster1.setId(27);
sqlSession.update("com.maven.quickstart.mapper.MonsterMapper.updateMonster", monster1);
// 查询
List<Monster> monsters = sqlSession.selectList("com.maven.quickstart.mapper.MonsterMapper.findAllMonster");
for (Monster monster2 : monsters) {
System.out.println("monseter-" + monster2);
}
if(sqlSession != null) {
sqlSession.commit(); // 增删改一定要提交。
sqlSession.close();
}
System.out.println("操作成功");
}
}
MyBatis使用注解方式操作
- 创建文件E:\JavaCode\mybatis\mybatits_quickstart\src\main\java\com\maven\quickstart\mapper\MonsterAnnotation.java
/**
* @Author: GQLiu
* @DATE: 2024/3/10 15:40
* 使用注解方式配置接口方法
*/
public interface MonsterAnnotation {
// 添加monster
/*
* MonsterMapper.xml中的配置方法
*
* <insert id="addMonster" parameterType="Monster" useGeneratedKeys="true" keyProperty="id">
INSERT INTO `monster`
(`age`, `birthday`, `email`, `gender`, `name`, `salary`)
VALUES (#{age},#{birthday}, #{email}, #{gender}, #{name}, #{salary})
</insert>
* 这里注解的方式addMonster不用配置, parameter不用写,只需要写上要进行处理的语句即可。
* */
@Insert(value = "INSERT INTO `monster` " +
"(`age`, `birthday`, `email`, `gender`, `name`, `salary`) " +
"VALUES (#{age},#{birthday}, #{email}, #{gender}, #{name}, #{salary})")
public void addMonster(Monster monster);
// 根据id删除monster
/* MonsterMapper.xml中的配置方法:
* <delete id="delMonster" parameterType="Integer">
DELETE FROM `monster` WHERE id=#{id};
</delete>
* */
@Delete(value = "DELETE FROM `monster` WHERE id=#{id}")
public void delMonster(Integer id);
// 修改Monster
/*
* MonsterMapper.xml中的配置方法:
<update id="updateMonster" parameterType="Monster">
UPDATE `monster` SET `age`=#{age}, `birthday` = #{birthday}, `email`=#{email}, `gender`=#{gender}, `name`=#{name}, `salary` = #{salary} WHERE id=#{id}
</update>
* */
@Update(value = "UPDATE `monster` SET `age`=#{age}, `birthday` = #{birthday}, `email`=#{email}, `gender`=#{gender}, `name`=#{name}, `salary` = #{salary} WHERE id=#{id}")
public void updateMonster(Monster monster);
// 根据id查询
/*
* MonsterMapper.xml中的配置方法:
* <select id="getMonsterById" resultType="Monster">
SELECT * FROM `monster` WHERE id = #{id}
</select>
* */
@Select(value = "SELECT * FROM `monster` WHERE id = #{id}")
public Monster getMonsterById(Integer id);
// 查询所有的Monster
/*
* MonsterMapper.xml中的配置方法:
* <select id="findAllMonster" resultType="Monster">
SELECT * FROM `monster`
</select>
* */
@Select(value = "SELECT * FROM `monster`")
public List<Monster> findAllMonster();
}
- 修改mybatis-config.xml, 对MonsterAnnotation进行注册
<!--这里我们配置需要关联的Mapper.xml文件-->
<mappers>
<!--<mapper resource="com/maven/quickstart/mapper/MonsterMapper.xml"/>-->
<!--
如果使用注解的方式, 可以不再使用MonsterMapper.xml, 上面这行可以注释掉
但是需要在mybatis-config.xml中注册/引入含注解的类
如果没有引入,不能使用
-->
<mapper class="com.maven.quickstart.mapper.MonsterAnnotation"/>
</mappers>
- 创建E:\JavaCode\mybatis\mybatits_quickstart\src\test\java\com\maven\quickstart\com\mapper\MonsterAnnotationTest.java, 完成使用注解的测试。
/**
* @Author: GQLiu
* @DATE: 2024/3/10 15:52
* 使用注解方式完成各种对DB的操作
*/
public class MonsterAnnotationTest {
private SqlSession sqlSession;
private MonsterAnnotation monsterAnnotation;
@Before
public void init(){
sqlSession = MyBatisUtils.getSqlSession();
monsterAnnotation = sqlSession.getMapper(MonsterAnnotation.class);
System.out.println("monsterAnnotation=" + monsterAnnotation); // 依然是一个代理对象:org.apache.ibatis.binding.MapperProxy@853265
}
@Test
public void addMonsterTest() {
Monster monster1 = new Monster();
monster1.setAge(109139);
monster1.setBirthday(new Date());
monster1.setEmail("kat@qq.com");
monster1.setGender(1);
monster1.setName("狐狸安抚#");
monster1.setSalary(1000);
monsterAnnotation.addMonster(monster1);
if(sqlSession != null) {
sqlSession.commit(); //提交
sqlSession.close();
}
System.out.println("添加成功");
}
// 使用注解方式完成查询
@Test
public void findAllMonster() {
List<Monster> allMonster = monsterAnnotation.findAllMonster();
for (Monster monster : allMonster) {
System.out.println(monster);
}
if(sqlSession != null) {
sqlSession.close();
}
}
}
注意事项和说明
- 如果是通过注解的方式,就不再使用MonsterMapper.xml文件。但是需要在mybatis-config.xml文件中 注册 含注解的类 / 接口。
- 使用注解方式,如果要返回自增长的id值,可以使用@Option 注解,组合使用:
@Insert(value = "INSERT INTO `monster` " +
"(`age`, `birthday`, `email`, `gender`, `name`, `salary`) " +
"VALUES (#{age},#{birthday}, #{email}, #{gender}, #{name}, #{salary})")
@Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id") // useGeneratedKeys=true 表示返回自增的值. keyProperty="id" 表示 自增值对应的对象属性. keyColumn="id" 表示 自增值对应的表的字段.
public void addMonster(Monster monster);
成功拿到:
monster1=Monster{id=35, age=109139, name='狐狸安抚#', email='kat@qq.com', birthday=Sun Mar 10 16:16:58 CST 2024, salary=1000.0, gender=1}
mybatis-config.xml配置文件详解
1.(properties)属性
在mybatis-config.xml文件中,如果需要一些从外部文件引入的属性来当作在mybatis-config.xml文件中的某个属性,就需要用到这个。eg:下面的 d r i v e r 、 {driver}、 driver、{username}
<environments default="development">
<environment id="development">
<!--配置事务管理器-->
<transactionManager type="JDBC"/>
<!--配置数据源-->
<dataSource type="POOLED">
<!--配置驱动-->
<property name="driver" value="${driver}"/>
<!--配置连接mysql-url
老韩解读:
1. jdbc:mysql 协议
2. 127.0.0.1:3306 : 指定连接mysql的ip+port
3. mybatis: 连接的DB
4. useSSL=true 表示使用安全连接
5. & 表示 & 防止解析错误
6. useUnicode=true : 使用unicode 作用是防止编码错误
7. characterEncoding=UTF-8 指定使用utf-8, 防止中文乱码
8. 老韩温馨提示:不要背,直接使用即可
-->
<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
使用步骤
- 在src的resources目录下建立XX.properties文件:
编写相应的需要的内容:
- 修改mybatis-config.xml,引入properties文件源:
<properties resource="application.properties"/> <!-- 告诉mybatis-config.xml我的外部的properties文件在哪里 -->
- 修改父项目的pom.xml(如果已经配置了 *.properties 就不用再配置)
<!--
在build种配置resources, 防止我们资源导出失败的问题.
1. 不同idea/maven版本可能提示错误不一样.
2. 以不变应万变, 少什么文件, 就增加相应的配置即可.
3. 含义是将 src/main/java目录和子目录以及 src/main/resources目录和子目录下的资源文件xml和properties在build项目时,导出到对应target目录下
-->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
</build>
2. 设置(settings)
一些基本的设置
我是在MonsterMapper.xml文件中使用的,也就是mybatis-config.xml管理的多个xxMapper中使用的。
MonsterMapper.xml:
<insert id="addMonster" parameterType="Monster" useGeneratedKeys="true" keyProperty="id">
INSERT INTO `monster`
(`age`, `birthday`, `email`, `gender`, `name`, `salary`)
VALUES (#{age},#{birthday}, #{email}, #{gender}, #{name}, #{salary})
</insert>
3. 类型别名(typeAliases)
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:
<!--设置类型别名-->
<typeAliases>
<typeAlias type="com.maven.quickstart.entity.Monster" alias="Monster"/>
</typeAliases>
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<!--设置类型别名-->
<typeAliases>
<!--<typeAlias type="com.maven.quickstart.entity.Monster" alias="Monster"/>-->
<package name="com.maven.quickstart.entity"/>
</typeAliases>
4. 类型处理器(typeHandlers)
5. 环境配置(environments)
- resource 注册Mapper文件:XXXMapper.xml(常见)
<!--这里我们配置需要关联的Mapper.xml文件-->
<mappers>
<mapper resource="com/maven/quickstart/mapper/MonsterMapper.xml"/>
</mappers>
- class:接口注解实现
<!--这里我们配置需要关联的Mapper.xml文件-->
<mappers>
<!--<mapper resource="com/maven/quickstart/mapper/MonsterMapper.xml"/>-->
<!--
如果使用注解的方式, 可以不再使用MonsterMapper.xml
但是需要在mybatis-config.xml中注册/引入含注解的类
如果没有引入,不能使用
-->
<mapper class="com.maven.quickstart.mapper.MonsterAnnotation"/>
</mappers>
- package 方式注册:直接把一个包下的所有的mapper全部都注册
方便省事,省时省力
<!--这里我们配置需要关联的Mapper.xml文件-->
<mappers>
<!--<mapper resource="com/maven/quickstart/mapper/MonsterMapper.xml"/>-->
<!--
如果使用注解的方式, 可以不再使用MonsterMapper.xml
但是需要在mybatis-config.xml中注册/引入含注解的类
如果没有引入,不能使用
-->
<!--<mapper class="com.maven.quickstart.mapper.MonsterAnnotation"/>-->
<package name="com.maven.quickstart.mapper"/>
</mappers>
6. SQL映射文件——XXXMapper.xml
MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。
SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出)
- cache – 该命名空间的缓存配置。
- cache-ref – 引用其它命名空间的缓存配置。
- resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
- sql – 可被其它语句引用的可重用语句块。
- insert – 映射插入语句。
- update – 映射更新语句。
- delete – 映射删除语句。
- select – 映射查询语句。
XxxMapper.xml-详细说明
新建 Module xml-mapper
1、在原来的项目中,新建 xml-mapper 项目演示 xml 映射器的使用
2、新建 Module 后,先创建需要的包,再将需要的文件/资源拷贝过来(这里我们拷贝 Monster.java、resources/jdbc.properties 和 mybatis-config.xml)
3、创建 MonsterMapper.java MonsterMapper.xml 和 MonsterMapperTest.java
基本使用
- insert、delete、update、select 这个我们在前面讲解过,分别对应增删改查的方法和 SQL语句的映射.
- 如何获取到刚刚添加的 Monster 对象的 id 主键
parameterType
● parameterType(输入参数类型)
- 传入简单类型,比如按照 id 查 Monster(前讲过)
- 传入 POJO 类型,查询时需要有多个筛选条件
- 当有多个条件时,传入的参数就是 Pojo 类型的 Java 对象,比如这里的 Monster 对象
- 当传入的参数类是 String 时,也可以使用 ${} 来接收参数
● parameterType-应用案例
案例 1:请查询 id = 1 或者 name = ‘白骨精’ 的妖怪
案例 2:请查询 name 中 包含 “牛魔王” 的妖怪
- 修改 MonsterMapper.java, 增加方法接口
//通过 id 或者名字查询
public List<Monster> findMonsterByNameORId(Monster monster);
//查询名字中含义'精'妖怪
public List<Monster> findMonsterByName(String name);
- 修改 MonsterMapper.xml
<!-- 实现 findMonsterByNameORId -->
<select id="findMonsterByNameORId" parameterType="Monster"
resultType="Monster">
SELECT * FROM monster
WHERE id=#{id} OR name=#{name}
</select>
<!-- 看看模糊查询的使用 取值 需要 ${value} 取值-->
<select id="findMonsterByName" parameterType="String" resultType="Monster">
SELECT * FROM monster
WHERE name LIKE '%${value}%' </select>
- 修改 MonsterMapperTest.java ,完成测试
@Test
public void findMonsterByNameORIdTest() {
Monster monster = new Monster();
monster.setId(25);
monster.setName("老鼠精#1");
List<Monster> monsters = monsterMapper.findMonsterByNameORId(monster);
for (Monster monster1 : monsters) {
System.out.println(monster1);
}
// 释放连接回连接池
if(sqlSession != null) {
sqlSession.close();
}
}
// 模糊查询
@Test
public void findMonsterByNameTest() {
String name = "松鼠精";
List<Monster> monsters = monsterMapper.findMonsterByName(name);
for (Monster monster : monsters) {
System.out.println(monster);
}
}
传入 HashMap
● 传入 HashMap(重点)
- HashMap 传入参数更加灵活,比如可以灵活的增加查询的属性,而不受限于 Monster 这个 Pojo 属性本身
- 演示如何遍历一个 List<Map<String,Object>> 的数据类型
● 传入 HashMap- 应用实例 1
要求:声明一个方法,按传入参数是 HashMap 的方式,查询 id > 10 并且 salary 大于 40的所有妖怪
- 修改 MonsterMapper.java, 增加方法接口
//查询 id > 10 并且 salary 大于 40, 要求传入的参数是 HashMap
public List<Monster>
findMonsterByIdAndSalary_PrameterHashMap(Map<String, Object> map);
- 修改 MonsterMapper.xml
<!--
如果是以map形式传入参数, 当你写`id` > #{id} 时,表示你的map中有一个k-v中 key是id
-->
<select id="findMonsterByIdAndSalary_PrameterHashMap" parameterType="map" resultType="Monster">
SELECT * FROM `monster` WHERE `id` > #{id} AND `salary` > #{salary}
</select>
- 修改 MonsterMapperTest.java 进行测试
@Test
public void findMonsterByIdAndSalary_PrameterHashMapTest() {
// 报错:Diamond types are not supported at language level '5'
// 因为目前只支持jdk5. 在父项目的pom中指定maven的编译器和jdk版本
HashMap<String, Object> map = new HashMap<>();
map.put("id", 10);
map.put("salary", 1000);
List<Monster> monsters = monsterMapper.findMonsterByIdAndSalary_PrameterHashMap(map);
for (Monster monster : monsters) {
System.out.println(monster); // 是对象形式 : Monster{id=21, age=11, name='松鼠精#1', email='kate@qq.com', birthday=Fri Mar 08 00:00:00 CST 2024, salary=1010.0, gender=1}
}
if(sqlSession != null) {
sqlSession.close(); // 关闭连接
}
}
● 传入和返回 HashMap- 应用实例
要求:将上面的方法的改成返回参数也以 HashMap 的类型
- 修改 MonsterMapper.java
//查询 id > 10 并且 salary 大于 40, 要求传入的参数是 HashMap
public List<Map<String, Object>>
findMonsterByIdAndSalary_PrameterHashMap_ReturnHashMap(Map<String, Object> map);
- 修改 MonsterMapper.xml
<select id="findMonsterByIdAndSalary_ParameterHashMap_ReturnHashMap" parameterType="map" resultType="map">
SELECT * FROM `monster` WHERE `id` > #{id} AND `salary` > #{salary}
</select>
- 修改 MonsterMapperTest.java
@Test
public void findMonsterByIdAndSalary_ParameterHashMap_ReturnHashMapTest() {
HashMap<String, Object> map = new HashMap<>();
map.put("id", 10);
map.put("salary", 1000);
List<Map<String, Object>> monsterList = monsterMapper.findMonsterByIdAndSalary_ParameterHashMap_ReturnHashMap(map);
// 取出返回的结果,以map方式
for (Map<String, Object> monsterMap : monsterList) {
// System.out.println("monsterMap=" + monsterMap); // 是kv形式 : {birthday=2024-03-09, gender=1, name=老鼠精#0, id=22, salary=100000.0, age=550, email=2222@qq.com}
// 取出 kv
Set<String> keySet = monsterMap.keySet();
for(String key : keySet) {
System.out.println(key + " ===> " + monsterMap.get(key));
}
System.out.println("===================");
// entry方法
// Set<Map.Entry<String, Object>> entries = monsterMap.entrySet();
// Iterator<Map.Entry<String, Object>> iterator = entries.iterator();
// while (iterator.hasNext()) {
// Map.Entry<String, Object> next = iterator.next();
// System.out.println(next.getKey() + " ===> " + entry.getValue());
// }
// System.out.println("===================");
}
if(sqlSession != null) {
sqlSession.close(); // 关闭连接
}
}
resultMap(结果集映射)
应用实例
● 基本介绍
当实体类的属性和表的字段名字不一致时,我们可以通过 resultMap 进行映射,从而屏蔽实体类属性名和表的字段名的不同.
● 案例演示
- 创建表 user
2. 创 建 entity
D:\idea_java_projects\HSP_MyBatis\01_mybatis_quickstart\src\main\java\com\hspedu\entity\User.java
public class User {
private Integer userid;
private String username;
private String useremail;
}
- 创 建
D:\idea_java_projects\HSP_MyBatis\01_mybatis_quickstart\src\main\java\com\hspedu\mapper\UserMapper.java
public interface UserMapper {
//添加方法
public void addUser(User user);
//查询所有的 User
public List<User> findAllUser();
}
- 修 改
D:\idea_java_projects\HSP_MyBatis\01_mybatis_quickstart\src\main\resources\mybatis-config.xml, 改变别名的写法
- 创 建
D:\idea_java_projects\HSP_MyBatis\01_mybatis_quickstart\src\main\java\com\hspedu\mapper\UserMapper.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 xml文件
该文件可以去实现对应的接口的方法。
namespace 指定该xml文件和哪个接口对应
-->
<mapper namespace="com.maven.quickstart.mapper.UserMapper">
<!--
配置方法
注意user属性和表的字段不一致。
user_email 和 user_name 是表的字段
useremail 和 username 是javabean的字段。
-->
<insert id="addUser" parameterType="User" useGeneratedKeys="true" keyProperty="userid">
INSERT INTO `user` (`user_email`, `user_name`) VALUES (#{useremail},#{username});
</insert>
<!--
用默认方法resultType="User",如果对象属性名和表的字段名一样的话,就会设置值。如果不一样就会使默认值。
这里可以使用resultMap解决。
resultMap : 表示要定义一个resultMap
id="findAllUserMap" id就是程序员指定的resultMap的id,后面可以通过id使用这个resultM ap
type="User" 就是你需要返回的对象类型。
column="user_email" 表示表的字段名
property="useremail" 表示javabean的值。
-->
<resultMap id="findAllUserMap" type="User">
<result column="user_email" property="useremail"/>
<result column="user_name" property="username"/>
</resultMap>
<select id="findAllUser" resultMap="findAllUserMap">
SELECT * FROM `user`;
</select>
</mapper>
- 创 建
D:\idea_java_projects\HSP_MyBatis\01_mybatis_quickstart\src\test\java\com\hspedu\mapper\UserMapperTest.java 完成测试
/**
* @Author: GQLiu
* @DATE: 2024/3/12 20:59
*/
public class UserMapperTest {
public SqlSession sqlSession;
public UserMapper userMapper;
@Before
public void init(){
sqlSession = MyBatisUtils.getSqlSession();
userMapper = sqlSession.getMapper(UserMapper.class);
}
@Test
public void addUserTest() {
User user = new User();
user.setUserid(123);
user.setUseremail("123@qq.com");
user.setUsername("a1a1");
userMapper.addUser(user);
// 如果是增删改,需要提交事务
if(sqlSession != null) {
sqlSession.commit();
sqlSession.close();
}
System.out.println("提交User成功");
}
@Test
public void findAllUserTest() {
List<User> users = userMapper.findAllUser();
for (User user : users) {
System.out.println("user=" + user);
}
if(sqlSession != null) {
sqlSession .close();
}
}
}
注意事项和细节
2、老韩说明:如果是 MyBatis-Plus 处理就比较简单, 可以使用 注解@TableField 来解决实体字段名和表字段名不一致的问题,还可以使用@TableName 来解决 实体类名和表名不一致的问题
7. 动态 SQL 语句-更复杂的查询业务需求
为什么需要动态 SQL?
1、动态 SQL 是 MyBatis 的强大特性之一
2、使用 JDBC 或其它类似的框架,根据不同条件拼接 SQL 语句非常麻烦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号等
3、 SQL 映射语句中的强大的动态 SQL 语言, 可以很好的解决这个问题.
动态 SQL-基本介绍
- 在一个实际的项目中,sql 语句往往是比较复杂的
- 为了满足更加复杂的业务需求,MyBatis 的设计者,提供了动态生成 SQL 的功能。
● 动态 SQL 必要性
- 比如我们查询Monster 时,如果程序员输入的age 不大于0, 我们的sql语句就不带age 。
- 更新 Monster 对象时,没有设置的新的属性值,就保持原来的值,设置了新的值,才更新
● 动态 SQL 常用标签
动态 SQL 提供了如下几种常用的标签,类似我们 Java 的控制语句:
- if [判断]
- where [拼接 where 子句]
- choose/when/otherwise [类似 java 的 switch 语句, 注意是单分支]
- foreach [类似 in ]
- trim [替换关键字/定制元素的功能] (用的少,没学)
- set [在 update 的 set 中,可以保证进入 set 标签的属性被修改,而没有进入 set 的,保持原来的值]
动态 SQL-案例演示
1 新建 Module dynamic-sql
1、在原来的项目中,新建 dynamic-sql 项目演示动态 SQL 的使用
2、新建 Module 后,先创建需要的包,再将需要的文件/资源拷贝过来(这里我们拷贝Monster.java、resources/jdbc.properties 和 mybatis-config.xml)
3、创建 MonsterMapper.java MonsterMapper.xml 和 MonsterMapperTest.java
if 标签应用实例
● 需求:请查询 age 大于 10 的所有妖怪,如果程序员输入的 age 不大于 0, 则输出所有的妖怪!
- 修改 MonsterMapper.java
/**
* @Author: GQLiu
* @DATE: 2024/3/7 22:29
* 接口, 用于生命操作monster表的方法
* 这些方法可以通过注解或xml文件来实现。
*/
public interface MonsterMapper {
// 根据age查询结果
// 加上 @Param(value = "age") 防止xml中想要取出age取不出。
public List<Monster> findMonsterByAge(@Param(value = "age") Integer age);
// 根据id和名字来查询结果
public List<Monster> findMonsterByIdAndName(Monster monster);
// 测试 choose标签的使用
// 如果name不为空,就按照name查找
// 如果id>0 就按照id查询妖怪
// 如果前两个条件都不满足,就默认查询salary > 100
public List<Monster> findMonsterByIdOrName_choose(Map<String, Object> map);
//测试foreach标签的使用
public List<Monster> findMonsterById_forEach(Map<String, Object> map);
// 测试Set标签
public void updateMonster_set(Map<String, Object> map);
}
- 修改 MonsterMapper.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 xml文件
该文件可以去实现对应的接口的方法。
namespace 指定该xml文件和哪个接口对应
-->
<mapper namespace="org.example.mapper.MonsterMapper">
<!--
查询age 大于 10 的所有妖怪, 如果程序员输入的age不大于0,则输出所有的妖怪
使用#{age} 表达式是取不出值的。 解决方法是使用@Param(value) = "age"
如果入参是Monster对象,则在test中直接使用对象的属性名即可。
where 标签会在组织动态sql时加上where。
mybatis 会去掉多余的 AND
-->
<select id="findMonsterByAge" parameterType="Integer" resultType="Monster">
SELECT * FROM `monster` WHERE 1 = 1
<if test="age >= 0">
AND age > #{age}
</if>
</select>
<!--会自动过滤掉多余的 AND-->
<select id="findMonsterByIdAndName" parameterType="Monster" resultType="Monster">
SELECT * FROM `monster`
<where>
<if test="id >= 0">
AND `id` > #{id}
</if>
<if test="name != null and name != ''">
AND `name` = #{name}
</if>
</where>
</select>
<!--// 如果name不为空,就按照name查找-->
<!--// 如果id>0 就按照id查询妖怪-->
<!--// 如果前两个条件都不满足,就默认查询salary > 100-->
<select id="findMonsterByIdOrName_choose" parameterType="map" resultType="Monster">
SELECT * FROM `monster`
<choose>
<when test="name != null and name!=''">
WHERE `name` = #{name}
</when>
<when test="id > 0">
WHERE `id` > #{id}
</when>
<otherwise>
WHERE `salary` > 100
</otherwise>
</choose>
</select>
<!--
测试forEach
map入参中应当由 ids-【10,12,14】-->
<select id="findMonsterById_forEach" parameterType="map" resultType="Monster">
SELECT * FROM `monster`
<!--
where标签
首先判断 ids是否为空——使用if
如果ids不为空,则使用foreach标签进行遍历
collection="ids" 对应你的入参map的key-ids 对应你的入参map的key
item="id" 表示在遍历ids集合(map集合对应的value的list。)时,每次取出的值对应的变量为id 在遍历map对应的value时每次取出的项。
open="(" 对应的就是sql 中IN后面的左括号 (
separator="," 表示使用 , 作为间隔符
close=")" 对应的就是sql 中IN后面的右括号 )
#{id} 对应的就是item="id"
-->
<if test="ids != null and ids !=''">
<where>
id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</where>
</if>
</select>
<!--
配置实现updateMonster_set
-->
<update id="updateMonster_set" parameterType="map">
update `monster`
<set>
<if test="age != null and age != ''">
`age` = #{age},
</if>
<if test="email != null and email != ''">
`email` = #{email},
</if>
<if test="birthday != null and birthday != ''">
`birthday` = #{birthday},
</if>
<if test="salary != null and salary != ''">
`salary` = #{salary},
</if>
<if test="name != null and name != ''">
`name` = #{name},
</if>
<if test="gender != null and gender != ''">
`gender` = #{gender},
</if>
</set>
WHERE id = #{id}
</update>
</mapper>
- 修改 MonsterMapperTest.java 并完成测试
public class MonsterMapperTest {
// 属性
private SqlSession sqlSession;
private MonsterMapper monsterMapper;
// 编写方法完成初始化
@Before // @Before标注的方法会在 JTest执行前就执行的方法
public void init() {
// 获取到sqlSession
sqlSession = MyBatisUtils.getSqlSession();
// 获取到MonsterMapper对象 (代理对象:MapperProxy)
// 底层使用了动态代理机制
monsterMapper = sqlSession.getMapper(MonsterMapper.class);
System.out.println("monsterMapper=" + monsterMapper);
}
@Test
public void findMonsterByAgeTest() {
// List<Monster> monsters = monsterMapper.findMonsterByAge(-1100);
List<Monster> monsters = monsterMapper.findMonsterByAge(1100);
for (Monster monster : monsters) {
System.out.println("monster=" + monster);
}
}
@Test
public void t1(){
System.out.println("t1()....");
}
@Test
public void t2(){
System.out.println("t2()....");
}
}
where 标签应用实例
● where 标签应用实例
需求:查询 id 大于 20 的,并且名字是 “牛魔王” 的所有妖怪, 注意,如果名字为空,或者输入的 id 小于 0, 则不拼接 sql 语句(老师梳理:如果名字为空 , 就不带名字条件, 如果输入的 id 小于 0, 就不带 id 的条件)
- 修改 MonsterMapper.java, 添加:
// 根据id和名字来查询结果
public List<Monster> findMonsterByIdAndName(Monster monster);
- 修改 MonsterMapper.xml, 添加:
<!--会自动过滤掉多余的 AND-->
<select id="findMonsterByIdAndName" parameterType="Monster" resultType="Monster">
SELECT * FROM `monster`
<where>
<if test="id >= 0">
AND `id` > #{id}
</if>
<if test="name != null and name != ''">
AND `name` = #{name}
</if>
</where>
</select>
- 修改 MonsterMapperTest.java 并完成测试
@Test
public void findMonsterByIdAndNameTest() {
Monster monster = new Monster();
monster.setName("松鼠精#1");
monster.setId(1);
List<Monster> monsters = monsterMapper.findMonsterByIdAndName(monster);
for (Monster monster1 : monsters) {
System.out.println("monster=" + monster1);
}
if(sqlSession != null) {
sqlSession.close();
}
}
choose/when/otherwise 应用实例
● 需求:如果给的 name 不为空,就按名字查询妖怪,如果指定的 id>0,就按 id 来查询妖怪, 要求使用 choose/when/otherwise 标签实现, 传入参数要求使用 Map
- 修改 MonsterMapper.java
//测试 choose 标签的使用
public List<Monster>
findMonsterByIdAndName_choose(Map<String, Object> map);
- 修改 MonsterMapper.xml
<!--// 如果name不为空,就按照name查找-->
<!--// 如果id>0 就按照id查询妖怪-->
<!--// 如果前两个条件都不满足,就默认查询salary > 100-->
<select id="findMonsterByIdOrName_choose" parameterType="map" resultType="Monster">
SELECT * FROM `monster`
<choose>
<when test="name != null and name!=''">
WHERE `name` = #{name}
</when>
<when test="id > 0">
WHERE `id` > #{id}
</when>
<otherwise>
WHERE `salary` > 100
</otherwise>
</choose>
</select>
- 修改 MonsterMapperTest.java 并完成测试
public void findMonsterByIdOrName_chooseTest() {
HashMap<String, Object> map = new HashMap<>();
map.put("name", "松鼠精#1");
map.put("id", 1100);
map.put("salary", 12112);
List<Monster> monsters = monsterMapper.findMonsterByIdOrName_choose(map);
for (Monster monster : monsters) {
System.out.println(monster);
}
if(sqlSession != null) {
sqlSession.close();
}
}
forEach 标签应用实例
● 需求:查询 monster_id 为 20, 22, 34 的妖怪
- 修改 MonsterMapper.java
//测试 foreach 的标签使用
public List<Monster>
findMonsterById_forEach(Map<String, Object> map);
- 修改 MonsterMapper.xml
<!--
测试forEach
map入参中应当由 ids-【10,12,14】-->
<select id="findMonsterById_forEach" parameterType="map" resultType="Monster">
SELECT * FROM `monster`
<!--
where标签
首先判断 ids是否为空——使用if
如果ids不为空,则使用foreach标签进行遍历
collection="ids" 对应你的入参map的key-ids
item="id" 表示在遍历ids集合时,每次取出的值对应的变量为id
open="(" 对应的就是sql 中IN后面的左括号 (
separator="," 表示使用 , 作为间隔符
close=")" 对应的就是sql 中IN后面的右括号 )
#{id} 对应的就是item="id"
-->
<if test="ids != null and ids !=''">
<where>
id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</where>
</if>
</select>
- 修改 MonsterMapperTest.java 并完成测试
@Test
public void findMonsterById_forEach() {
HashMap<String, Object> map = new HashMap<>();
map.put("ids", Arrays.asList(24, 25, 26));
List<Monster> monsters = monsterMapper.findMonsterById_forEach(map);
for (Monster monster : monsters) {
System.out.println("monster=" + monster);
}
if(sqlSession != null) {
sqlSession.close();
}
}
set 标签应用实例[重点]
● 需求: 请对指定 id 的妖怪进行 修改,如果没有设置新的属性,则保持原来的值
- 修改 MonsterMapper.java
//测试 Set 标签
public void
updateMonster_set(Map<String, Object> map);
- 修改 MonsterMapper.xml
<!--
配置实现updateMonster_set
-->
<update id="updateMonster_set" parameterType="map">
update `monster`
<set>
<if test="age != null and age != ''">
`age` = #{age},
</if>
<if test="email != null and email != ''">
`email` = #{email},
</if>
<if test="birthday != null and birthday != ''">
`birthday` = #{birthday},
</if>
<if test="salary != null and salary != ''">
`salary` = #{salary},
</if>
<if test="name != null and name != ''">
`name` = #{name},
</if>
<if test="gender != null and gender != ''">
`gender` = #{gender},
</if>
</set>
WHERE id = #{id}
</update>
- 修改 MonsterMapperTest.java 并完成测试
@Test
public void updateMonster_setTest() {
HashMap<String, Object> map = new HashMap<>();
map.put("name", "我是你aaabbba爹");
map.put("id", 28);
monsterMapper.updateMonster_set(map);
// 修改需要提交sqlSession
if(sqlSession != null) {
sqlSession.commit();
sqlSession.close();
}
System.out.println("操作成功");
}
注意事项
8 映射关系一对一
映射关系 1 对 1-基本介绍
● 基本介绍
- 项目中 1 对 1 的关系是一个基本的映射关系,比如:Person(人) — IDCard(身份证)
- 我们看看再 MyBatis 中如何实现 1 对 1 的处理.
1 对 1 ,我们这里就研究一下单向 1 对 1 即可
映射关系 1 对 1-映射方式
- 通过配置 XxxMapper.xml 实现 1 对 1 [配置方式]
- 通过注解的方式实现 1 对 1 [注解方式]
配置 Mapper.xml 的方式-应用实例
● 老韩说明:通过配置 XxxMapper.xml 的方式来实现下面的 1 对 1 的映射关系,实现级联查询,通过 person 可以获取到对应的 idencard 信息
- 创建 person 表和 idencard 表
-- 创建person表
CREATE TABLE person (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(32) NOT NULL DEFAULT '',
`card_id` INT, -- 对应idencard的主键id
FOREIGN KEY (card_id) REFERENCES idencard(id)
) CHARSET utf8;
-- 创建 idencard 表
CREATE TABLE idencard(
id INT PRIMARY KEY AUTO_INCREMENT,
card_sn VARCHAR(32) NOT NULL DEFAULT ''
) CHARSET utf8;
INSERT INTO idencard VALUES(1, '124124321432423');
INSERT INTO person VALUES(1, '张三', 3434);
SELECT * FROM person;
SELECT * FROM idencard;
注意事项和细节
- 表是否设置外键, 对 MyBatis 进行对象/级联映射没有影响
- 举例: 去掉 person 表的外键 , 进行测试, 依然可以获取相应的级联对象
9. 映射关系多对一
● 基本介绍
- 项目中多对 1 的关系是一个基本的映射关系, 多对 1, 也可以理解成是 1 对多.
- Dep —Emp : 一个部门可以有多个员工
- User — Pet: 一个用户可以养多只宠物
● 注意细节
- 我们直接讲双向的多对一的关系,单向的多对一比双向的多对一简单。
- 在实际的项目开发中, 要求会使用双向的多对一的映射关系
- 老韩说明:什么是双向的多对一的关系 : 比如通过 User 可以查询到对应的 Pet, 反过来,通过 Pet 也可以级联查询到对应的 User 信息.
- 多对多的关系,是在多对 1 的基础上扩展即可.
映射关系多对 1-映射方式
- 方式 1:通过配置 XxxMapper.xml 实现多对 1
- 方式 2:通过注解的方式实现 多对 1
配置Xml方式应用实例:
- 需求说明: 实现级联查询,通过 user 的 id 可以查询到用户信息,并可以查询到关联的 pet信息,反过来,通过 Pet 的 id 可以查询到 Pet 的信息,并且可以级联查询到它的主人 User对象信息
- 创建 mybatis_user 和 mybatis_pet 表