文章目录
第十二章 什么是Mybatis
mybatis 是一个数据库持久层框架(主要是把内存中的数据写入到数据库中(文件)),通过mybatis能实现对数据库的访问(执行sql语句),mybatis建立在jdbc的基础之上,jdbc对代码耦合性太高。便有了mybatis的存在,mybatis把SQL语句写在xml文件中,达到SQL语句与业务代码的解耦。
mybatis与HIbernate的区别
MyBatis 是一个半自动映射的框架,因为 MyBatis 需要手动匹配 POJO 和 SQL 的映射关系,操作的数据库,执行操作的语言是SQL语言。
Hibernate 是一个全表映射的框架,只需提供 POJO 和映射关系即可,操作的是POJO对象,执行操作的是HQL语言(Hibernate Query Language)
12.1 快速入门
1,创建一个普通的maven项目。在pom.xml中添加依赖
pom.xml文件
<?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>org.example</groupId>
<artifactId>Mybatis3</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>
<!-- 数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<!-- 单元测试5-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
数据库文件
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(20) NOT NULL,
`name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`pwd` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, '李四', '12213');
INSERT INTO `user` VALUES (2, '张三', 'abcdef');
INSERT INTO `user` VALUES (3, 'test', '12213');
INSERT INTO `user` VALUES (6, 'llowwww', '123');
INSERT INTO `user` VALUES (7, '王先生', '123');
INSERT INTO `user` VALUES (8, '张先生', '123');
SET FOREIGN_KEY_CHECKS = 1;
User类
public class User{
// id
private Long id;
//用户名
private String name;
// 密码
private String pwd;
public Long getId() {
return id;
}
public void setId(Long 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 + '\'' +
'}';
}
}
UserMapper
public interface UserMapper {
/**
* 查询所有数据
* @return
*/
List<User> selectList();
}
MybatisUtils 工具类
public class MybatisUtils {
public static SqlSession getSqlSession(){
// 读取配置文件
InputStream resourceAsStream = null;
try {
resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
} catch (IOException e) {
e.printStackTrace();
}
// 创造SqlSessionFactory
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
// 返回session对象
return build.openSession();
}
}
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/localhost?useSSL=true&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
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 namespace="com.example.mapper.UserMapper">
<select id="selectList" resultType="com.example.pojo.User">
select * from user
</select>
</mapper>
MyBatisTest
public class MyBatisTest {
@Test
public void selectList(){
// 获取sqlSession对象
SqlSession mapper = MybatisUtils.getSqlSession();
// 获取UserMapper 对象
UserMapper userMapper = mapper.getMapper(UserMapper.class);
// 调用方法
List<User> users = userMapper.selectList();
for (User user: users) {
System.out.println(user);
}
// 关闭session
mapper.close();
}
}
到此全部全部结束,文件结构如下
运行测试方法, 结果如下
12.2 核心对象
MyBatis 有三个基本要素:
核心接口和类
MyBatis核心配置文件(mybatis-config.xml)
SQL映射文件(XxxMapper.xml)
流程
SqlSessionFactoryBuilder 对象 调用buider方法创建SqlSessionFactory对象,参数是mybatis-config.xml 文件, SQLSessionFactory对象调用openSession方法,获得SqlSession对象,SqlSession对象执行Mapper映射文件。
12.3 配置文件(mybatis-config.xml)
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 /><!-- 属性 -->
<settings /><!-- 设置 -->
<typeAliases /><!-- 类型命名 -->
<typeHandlers /><!-- 类型处理器 -->
<objectFactory /><!-- 对象工厂 -->
<plugins /><!-- 插件 -->
<environments><!-- 配置环境 -->
<environment><!-- 环境变量 -->
<transactionManager /><!-- 事务管理器 -->
<dataSource /><!-- 数据源 -->
</environment>
</environments>
<databaseIdProvider /><!-- 数据库厂商标识 -->
<mappers /><!-- 映射器 -->
</configuration>
12.3.1 properties标签
1导入外部文件属性,如数据库的连接信息
datasource.driver= com.mysql.jdbc.Driver
datasource.url= jdbc:mysql://localhost:3306/localhost?useSSL=true&useUnicode=true&characterEncoding=utf8
datasource.username= root
datasource.password= root
<?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="dataSource.properties"/>-->
<!-- 第二种配置 子属性-->
<properties>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/localhost?useSSL=true&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</properties>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<!-- 给一个类设置别名,不设置值。默认是类名小写-->
<typeAlias alias="student" type="com.example.pojo.User"/>
<!-- 以包下的类设置别名 别名类名首字母小写-->
<package name="com.example.pojo"/>
</typeAliases>
<!-- typeHandler 的作用就是承担 jdbcType 和 javaType 之间的相互转换-->
<typeHandlers>
</typeHandlers>
<!-- 数据库环境 default 属性指定一个默认数据源-->
<environments default="development">
<!-- 数据源配置-->
<environment id="development">
<!-- 配置事务 类型
JDBC 和 MANAGED
如果使用 JDBC 类型的事务管理器,则应用程序服务器负责事务管理操作,例如提交、回滚等。
如果使用 MANAGED 类型的事务管理器,则应用程序服务器负责管理连接生命周期
-->
<transactionManager type="JDBC"/>
<!-- 配置数据库的连接属性
1)UNPOOLED
没有数据库连接池,效率低下。需要每次打开和关闭每个数据库操作的连接。
2)POOLED
MyBatis 将维护一个数据库连接池。
3)JNDI
MyBatis 将从 JNDI 数据源中获取连接。
-->
<dataSource type="POOLED">
<!-- <property name="driver" value="${datasource.driver}"/>-->
<!-- <property name="url" value="${datasource.url}"/>-->
<!-- <property name="username" value="${datasource.username}"/>-->
<!-- <property name="password" value="${datasource.password}"/>-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!-- 指定Mapper文件所在的位置-->
<mappers>
<!-- 类路径资源引入-->
<mapper resource="mapper/UserMapper.xml"/>
<!-- Mapper文件必须与接口的类名一致,且在同一个包下-->
<mapper class="com.example.mapper.UserMapper"/>
<!-- 使用完全限定的资源定位符 -->
<mapper url="E:\testProject\Mybatis3\src\main\resources\mapper\UserMapper.xml"/>
<!-- Mapper文件必须与接口的类名一致,且在同一个包下-->
<package name="com.example.mapper"/>
</mappers>
</configuration>
12.3.2 settings标签
settings 标签用于配置 MyBatis 的运行时行为,红色为常用配置
12.3.3 配置SQL语句打印
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.14.0</version>
</dependency>
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
status="warn" 日志框架本身的输出日志级别
monitorInterval="5" 自动加载配置文件的间隔时间,不低于 5 秒
-->
<Configuration status="debug" monitorInterval="5">
<!--日志处理-->
<Appenders>
<!--控制台输出 appender-->
<Console name="Console" target="SYSTEM_ERR">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n" />
</Console>
</Appenders>
<!--logger 定义-->
<Loggers>
<Root level="debug">
<!--指定日志使用的处理器-->
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>
mybatis-config.xml
<setting name="logImpl" value="log4j2"/>
12.4 映射器(Mapper)
负责执行Sql语句的称为映射器,映射器由 Java 接口和 XML 文件(或注解)组成,
实现方式
- 通过 XML 文件方式实现,比如我们在 mybatis-config.xml 文件中描述的 XML 文件,用来生成 mapper。
- 通过注解的方式实现,使用 Configuration 对象注册 Mapper 接口。
12.4.1 配置映射器
XML实现映射器
public interface UserMapper {
/**
* 查询所有数据
* @return
*/
List<User> selectList();
}
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 namespace="com.example.mapper.UserMapper">
<select id="selectList" resultType="com.example.pojo.User">
select * from user
</select>
</mapper>
注册到mybatis配置文件中
<mapper resource="mapper/UserMapper.xml"/>
注解方式
public interface AnnotationUserMapper {
@Select("select * from user")
List<User> selectAll();
}
@Test
public void selectAll(){
try {
BufferedInputStream inputStream = new BufferedInputStream(
new FileInputStream("E:\\testProject\\Mybatis3\\src\\main\\resources\\mybatis-config.xml"));
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream);
Configuration configuration = build.getConfiguration();
// 添加注解的Mapper映射器到配置中
configuration.addMapper(AnnotationUserMapper.class);
// 打开session会话
SqlSession session = build.openSession();
UserMapper mapper = session.getMapper(AnnotationUserMapper.class);
List<User> users = mapper.selectAll();
for (User user: users
) {
System.out.println(user);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
12.4.2 映射器主要元素
不同的 mapper 文件中子元素的 id 可以相同,MyBatis 通过 namescape 和子元素的 id 联合区分。接口中的方法与映射文件中的 SQL 语句 id 应一 一对应。
12.4.3 执行SQL
MyBatis 有两种执行 SQL 语句的方式,如下:
通过 SqlSession 发送 SQL
通过 SqlSession 获取 Mapper 接口,通过 Mapper 接口发送 SQL
SqlSession 发送 SQL
检查 UserMapper是否配置到mybatis-config.xml
@Test
public void BySqlSession(){
// 获取sqlSession对象
SqlSession mapper = MybatisUtils.getSqlSession();
// 使用 SQLSession对象 内置的查询方法
List<User> users = mapper.selectList("com.example.mapper.UserMapper.selectList");
for (User user: users) {
System.out.println(user);
}
// 关闭session
mapper.close();
}
总结
SqlSession 对象内置了很多的SQL语句方法,方法的第一个参数代表接口类的路径加方法名,其他参数代表查询条件。
Mapper接口发送 SQL
// 获取sqlSession对象
SqlSession mapper = MybatisUtils.getSqlSession();
// 获取UserMapper 对象
UserMapper userMapper = mapper.getMapper(UserMapper.class);
// 调用方法
List<User> users = userMapper.selectList();
总结
通过SqlSession 获取自己定义的对象,使用的是自己定义的方法。
区别
上面分别讲解了 MyBatis 两种发送 SQL 的方式,一种用 SqlSession 直接发送,另外一种通过 SqlSession 获取 Mapper 接口再发送。笔者建议采用 Mapper 接口发送 SQL 的方式,理由如下:
-
使用 Mapper 接口编程可以消除 SqlSession 带来的功能性代码,提高可读性,而 SqlSession 发送 SQL,需要一个 SQL id 去匹配 SQL,比较晦涩难懂。
-
使用 Mapper 接口,类似 websiteMapper.getWebsite(1) 则是完全面向对象的语言,更能体现业务的逻辑。
使用 websiteMapper.getWebsite(1) 方式,IDE 会提示错误和校验,而使用 sqlSession.selectOne(“getWebsite”,1L) 语法,只有在运行中才能知道是否会产生错误。
12.5 SQL
12.5.1 select 标签
执行 SQL 语句时可以定义参数,参数可以是一个简单的参数类型,例如 int、float、String;也可以是一个复杂的参数类型,例如 JavaBean、Map 等。执行 SQL 后,会将结果集自动映射到 JavaBean 中。
MySQL 数据库和 JavaBean 采用同一套命名规则,即 Java 命名驼峰规则来达到结果自动映射,(数据库表字段名和属性名不一致时需要手动映射)。
参数的传递使用#{参数名},相当于告诉 MyBatis 生成 PreparedStatement 参数。
select标签常用属性
参数传递
- 使用Map传递参数
- 使用注解传递参数
- 使用JavaBean传递参数
使用Map传递参数
/**
* 使用map 传递参数
* @param map
* @return
*/
List<User> selectByMap(Map<String,String> map);
/**
* 使用Param注解, 如果参数名称与字段名称一样可省略
* @param name
* @param pwd
* @return
*/
List<User> selectByParam(@Param("name") String name,@Param("pwd") String pwd);
/**
* 使用 Javabean 传递参数
* @param user
* @return
*/
List<User> selectByJavabean(User user);
UserMapper.xml
<!-- 通过Map传递参数-->
<select id="selectByMap" resultType="user">
select * from user where name = #{name}
and pwd = #{pwd}
</select>
<!-- 通过@Param传递参数-->
<select id="selectByParam" resultType="user">
select * from user where name =#{name}
and pwd = #{pwd}
</select>
<!-- 通过Javabean传递参数-->
<select id="selectByJavabean" resultType="user">
select * from user where name = #{name}
and pwd = #{pwd}
</select>
测试
@Test
public void param(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//通过map方法
Map<String,String> map = new HashMap<String, String>();
map.put("name", "王先生");
map.put("pwd","123");
List<User> users = mapper.selectByMap(map);
for (User user: users) {
System.out.println(user);
}
System.out.println("---------------");
//通过注解
List<User> users1 = mapper.selectByParam("王先生", "123");
for (User user: users1) {
System.out.println(user);
}
System.out.println("--------------");
//通过Javabean对象
User javaBean = new User();
javaBean.setName("王先生");
javaBean.setPwd("123");
List<User> users2 = mapper.selectByJavabean(javaBean);
for (User user: users2) {
System.out.println(user);
}
// 关闭session
sqlSession.close();
}
运行结果
总结
- 使用 Map 传递参数会导致业务可读性的丧失,继而导致后续扩展和维护的困难,所以在实际应用中我们应该果断废弃该方式。
- 使用 @Param 注解传递参数会受到参数个数的影响。当 n≤5 时,它是最佳的传参方式,因为它更加直观;当 n>5 时,多个参数将给调用带来困难。
- 当参数个数大于 5 个时,建议使用 JavaBean 方式。
12.5.2 insert、update、delete标签
MyBatis insert 标签用来定义插入语句,执行插入操作。当 MyBatis 执行完一条插入语句后,就会返回其影响数据库的行数。
insert 标签常用属性
参数传递
单个对象,直接传递参数
使用 Map 传递参数
使用注解传递参数
使用 JavaBean 传递参数
Collection或数组:
如果是Collection(List、Set)类型或者是数组,也会特殊处理,也是把传入的Collection(List、Set)或者数组封装在map中。
key:对于单一参数Collection使用(collection、list)、数组使用(array)来获取,例如:Collection使用#{collection[0]},…#{collection[N]},List集合也可以使用#{list[0]},…#{list[N]},数组可以使用#{array[0]},…,#{array[N]}这种形式来获取键所对应的值。
value:集合或数组索引所对应的值。
主键(自动递增)回填
MySQL、SQL Server 等数据库表可以采用自动递增的字段作为其主键,当向这样的数据库表插入数据时,即使不指定自增主键的值,数据库也会根据自增规则自动生成主键并插入到表中。
一些特殊情况下,我们可能需要将这个刚刚生成的主键回填到请求对象(原本不包含主键信息的请求对象)中,供其他业务使用。此时,我们就可以通过在 insert 标签中添加 keyProperty 和 useGeneratedKeys 属性,来实现该功能。
示例
/**
* 增加一个用户
* @param user
* @return
*/
int addUser(User user);
使用useGeneratedKeys生成主键,前提确保主键是自增。
<!--成功后将主键值返回填给id(po的属性)-->
<insert id="addUser" useGeneratedKeys="true" keyProperty="id">
insert to user(name,pwd) values (#{name},#{pwd})
</insert>
测试
@Test
public void addUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setName("王二明");
user.setPwd("1234");
int i = userMapper.addUser(user);
System.out.println("添加成功了"+i+"记录 id:"+ user.getId());
// 提交事务
sqlSession.commit();
// 关闭会话
sqlSession.close();
}
测试结果
自定义主键
<!--成功后将主键值返回填给id(po的属性)-->
<insert id="addUser">
<selectKey keyProperty="id" resultType="Long" order="BEFORE">
select if(max(id) is null,1,max(id)+1) as newId from user
</selectKey>
insert into user(name,pwd) values(#{name},#{pwd})
</insert>
update、delete 语句
/**
* 增加一个用户
* @param user
* @return
*/
int deleteUser(User user);
/**
* 增加一个用户
* @param user
* @return
*/
int updateUser(User user);
<!-- 更新-->
<update id="updateUser" >
update user set name =#{name}, pwd =#{pwd} where id=#{id}
</update>
<!-- 删除-->
<delete id="deleteUser">
delete from user where id= #{id}
</delete>
测试代码
@Test
public void updateAndDelete(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setId(10L);
user.setName("王先生");
user.setPwd("321");
int i = mapper.updateUser(user);
System.out.println("修改成功了"+i+"记录");
int i1 = mapper.deleteUser(user);
System.out.println("修改成功了"+i1+"记录");
sqlSession.commit();
sqlSession.close();
测试结果
12.5.3 级联封装 (resultMap)
resultMap 是 MyBatis 中最复杂的元素,主要用于解决实体类属性名与数据库表中字段名不一致的情况,可以将查询结果映射成实体对象。
现有的 MyBatis 版本只支持 resultMap 查询,不支持更新或者保存,更不必说级联的更新、删除和修改。
resultMap元素的构成
<resultMap id="" type="">
<constructor><!-- 类再实例化时用来注入结果到构造方法 -->
<idArg/><!-- ID参数,结果为ID -->
<arg/><!-- 注入到构造方法的一个普通结果 -->
</constructor>
<id/><!-- 用于表示哪个列是主键 -->
<result/><!-- 注入到字段或JavaBean属性的普通结果 -->
<association property=""/><!-- 用于一对一关联 -->
<collection property=""/><!-- 用于一对多、多对多关联 -->
<discriminator javaType=""><!-- 使用结果值来决定使用哪个结果映射 -->
<case value=""/><!-- 基于某些值的结果映射 -->
</discriminator>
</resultMap>
< resultMap> 元素的 type 属性表示需要的 POJO,id 属性是 resultMap 的唯一标识。
- 子元素 < constructor> 用于配置构造方法。当一个 POJO 没有无参数构造方法时使用。
- 子元素 < id> 用于表示哪个列是主键。允许多个主键,多个主键称为联合主键。
- 子元素 < result> 用于表示 POJO 和 SQL 列名的映射关系。
Map存储结果集
List<Map<String,User>> selectListByMap();
<select id="selectListByMap" resultType="map">
select * from user
</select>
@Test
public void selectListByMap(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<Map<String, User>> users = mapper.selectListByMap();
for (Map<String,User> maps: users) {
Set<Map.Entry<String, User>> entries = maps.entrySet();
for (Map.Entry<String, User> key: entries) {
String key1 = key.getKey();
Object value = key.getValue();
System.out.print(key1+":"+value+"");
}
System.out.println();
}
sqlSession.close();
}
运行结果
POJO存储结果集
List<User> selectList();
<select id="selectList" resultType="user">
select * from user
</select>
<!-- 如果pojo对象的属性与数据库的字段名称不一样,则使用resultMap 进行映射-->
<resultMap id="selectList" type="user">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="pwd"/>
</resultMap>
<select id="selectList" resultMap="selectList">
select * from user
</select>
resultMap 元素的属性 id 代表这个 resultMap 的标识,type 标识需要映射的 POJO。我们可以使用 MyBatis 定义好的类的别名或自定义类的全限定名。
这里使用 property 元素指定 user的属性名称 name,column 表示数据库中 user表的 SQL 列名 name,将 POJO 和 SQL 的查询结果一 一对应。
测试代码
@Test
public void selectList(){
// 获取sqlSession对象
SqlSession mapper = MybatisUtils.getSqlSession();
// 获取UserMapper 对象
UserMapper userMapper = mapper.getMapper(UserMapper.class);
// 调用方法
List<User> users = userMapper.selectList();
for (User user: users) {
System.out.println(user);
}
// 关闭session
mapper.close();
}
MyBatis 的每一个查询映射的返回类型都是 resultMap,只是当我们提供的返回类型是 resultType 时,MyBatis 会自动把对应的值赋给 resultType 所指定对象的属性,而当我们提供的返回类型是 resultMap 时,MyBatis 会将数据库中的列数据复制到对象的相应属性上,可用于复制查询。
需要注意的是,resultMap 和 resultType 不能同时使用。
联表查询
连表关系:
- 一对一关系 只能使用 association
- 一对多关系 使用collection
- 多对多关系 使用 collection
association与collection 查询方式
- 分步查询 相当于SQL中的子查询
- 单步查询 相当于SQL中的联表查询
数据库准备
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for stu_tea
-- ----------------------------
DROP TABLE IF EXISTS `stu_tea`;
CREATE TABLE `stu_tea` (
`id` bigint(20) NOT NULL COMMENT 'id',
`stu_id` bigint(20) NOT NULL COMMENT '学生ID',
`tea_id` bigint(20) NULL DEFAULT NULL COMMENT '教室ID',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of stu_tea
-- ----------------------------
INSERT INTO `stu_tea` VALUES (1, 1, 1);
INSERT INTO `stu_tea` VALUES (2, 1, 2);
INSERT INTO `stu_tea` VALUES (3, 1, 3);
INSERT INTO `stu_tea` VALUES (4, 1, 2);
INSERT INTO `stu_tea` VALUES (5, 2, 2);
INSERT INTO `stu_tea` VALUES (6, 3, 2);
INSERT INTO `stu_tea` VALUES (7, 3, 3);
INSERT INTO `stu_tea` VALUES (8, 4, 4);
-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` int(10) NOT NULL,
`name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`tid` int(10) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `fktid`(`tid`) USING BTREE,
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES (1, '小明', 1);
INSERT INTO `student` VALUES (2, '小红', 2);
INSERT INTO `student` VALUES (3, '小张', 2);
INSERT INTO `student` VALUES (4, '小李', 3);
INSERT INTO `student` VALUES (5, '小王', 1);
INSERT INTO `student` VALUES (6, '小赵', 1);
INSERT INTO `student` VALUES (7, '小梁', 2);
-- ----------------------------
-- Table structure for teacher
-- ----------------------------
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (
`id` int(10) NOT NULL,
`name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of teacher
-- ----------------------------
INSERT INTO `teacher` VALUES (1, '秦老师');
INSERT INTO `teacher` VALUES (2, '王老师');
INSERT INTO `teacher` VALUES (3, '张老师');
INSERT INTO `teacher` VALUES (4, '梁老师');
SET FOREIGN_KEY_CHECKS = 1;
12.5.3.1 一对一关联查询 (association)
查询需求: 查询出一个学生与一个老师的信息
public class Student {
private Integer id;
private String stuName;
private Integer teaId;
private Teacher teacher;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
public Integer getTeaId() {
return teaId;
}
public void setTeaId(Integer teaId) {
this.teaId = teaId;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", stuName='" + stuName + '\'' +
", teaId=" + teaId +
", teacher=" + teacher +
'}';
}
}
package com.example.pojo;
class Teacher {
private Integer teaId;
private String teaName;
public Integer getTeaId() {
return teaId;
}
public void setTeaId(Integer teaId) {
this.teaId = teaId;
}
public String getTeaName() {
return teaName;
}
public void setTeaName(String teaName) {
this.teaName = teaName;
}
@Override
public String toString() {
return "Teacher{" +
"teaId=" + teaId +
", teaName='" + teaName + '\'' +
'}';
}
}
public interface StudentMapper {
/**
* 查询一个学生与一个老师的信息
* @param id
* @return
*/
Student getStuAndTea(Integer id);
}
使用原生< resultMap> 查询一对一
StudentMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.StudentMapper">
<resultMap id="getStuAndTea" type="student">
<id column="id" property="id"/>
<result column="name" property="stuName"/>
<result column="tid" property="teaId"/>
<result column="tea_id" property="teacher.teaId"/>
<result column="tea_name" property="teacher.teaName"/>
</resultMap>
<select id="getStuAndTea" resultMap="getStuAndTea">
select s.* ,t.id tea_id ,t.`name` tea_name from student s ,teacher t
where t.id = s.tid and s.id = #{id}
</select>
</mapper>
测试代码
@Test
public void resultTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student stuAndTea = mapper.getStuAndTea(1);
System.out.println(stuAndTea);
sqlSession.close();
}
运行结果
association 单步查询
property:指定映射到实体类的对象属性。
column:指定表中对应的字段(即查询返回的列名)。
javaType:指定映射到实体对象属性的类型。
ofType:: 指定映射到集合中的属性中的泛型。
select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询。
public interface StudentMapper {
Student getStuAndTeaByAssociation(Integer id);
}
<resultMap id="byAssociation" type="student">
<id column="id" property="id"/>
<result column="name" property="stuName"/>
<result column="tid" property="teaId"/>
<association property="teacher">
<result column="tea_id" property="teaId"/>
<result column="tea_name" property="teaName"/>
</association>
</resultMap>
<select id="getStuAndTeaByAssociation" resultMap="byAssociation">
select s.* ,t.id tea_id ,t.`name` tea_name from student s ,teacher t where t.id = s.tid and s.id = #{id}
</select>
测试方法
@Test
public void resultByAssociationTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student stuAndTea = mapper.getStuAndTeaByAssociation(1);
System.out.println(stuAndTea);
sqlSession.close();
}
association 分步查询
public interface TeacherMapper {
/**
* 查询教师
* @param id
* @return
*/
public Teacher getTeacher(Integer id);
}
TeacherMapper.xml
<select id="getTeacher" resultType="teacher">
select t.id tea_id, t.name tea_name from teacher t where t.id = #{id}
</select>
StudentMapper
/**
* 查询一个学生与一个老师的信息
* @param id
* @return
*/
Student getStuAndTeaByAssociationFenBu(Integer id);
StudentMapper.xml
<resultMap id="byAssociation1" type="student">
<id column="id" property="id"/>
<result column="name" property="stuName"/>
<result column="tid" property="teaId"/>
<association property="teacher" column="tid"
javaType="com.example.pojo.Teacher"
select="com.example.mapper.TeacherMapper.getTeacher">
</association>
</resultMap>
<select id="getStuAndTeaByAssociationFenBu" resultMap="byAssociation1">
select * from student s where s.id = #{id}
</select>
12.5.3.2 一对多关联查询(collection)
查询需求:查询一个老师有多少个学生
collection 单步查询
修改Teacher.java 加入如下属性
private List<Student> listStu;
TeacherMapper.java
public Teacher getAllStudentByTeaId(Integer id);
<resultMap id="allStudentByCollection" type="teacher">
<id column="tea_id" property="teaId"/>
<result column="tea_name" property="teaName"/>
<collection property="listStu" ofType="com.example.pojo.Student">
<id column="id" property="id"/>
<result column="name" property="stuName"/>
<result column="tid" property="teaId"/>
<association property="teacher" column="tea_id"
select="com.example.mapper.TeacherMapper.getTeacher"
javaType="com.example.pojo.Teacher"/>
</collection>
</resultMap>
<select id="getAllStudentByTeaId" resultMap="allStudentByCollection">
select t.id tea_id ,t.`name` tea_name ,s.*
from student s ,teacher t where t.id = s.tid and t.id = #{id}
</select>
测试方法
@Test
public void collectionTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher allStudentByTeaId = mapper.getAllStudentByTeaId(2);
System.out.println(allStudentByTeaId);
sqlSession.close();
}
运行结果
collection 分步
StudentMapper.java
Student getStudentById(Integer id);
TeacherMapper.java
Teacher getAllStudentByTeaIdFenBU(Integer id);
StudentMapper.xml
<select id="getStudentById" resultType="student">
select t.id,t.name stu_name, t.tid tea_id from student t where t.tid=#{id}
</select>
TeacherMapper.xml
<resultMap id="allStudentByCollectionFenBu" type="teacher">
<id column="id" property="teaId"/>
<result column="name" property="teaName"/>
<collection property="listStu" column="stu_id" ofType="com.example.pojo.Student"
select="com.example.mapper.StudentMapper.getStudentById"/>
</resultMap>
<select id="getAllStudentByTeaIdFenBU" resultMap="allStudentByCollectionFenBu">
select t.*, st.stu_id from teacher t, stu_tea st where st.tea_id = t.id and t.id = #{id}
</select>
测试方法
@Test
public void collectionTestFenBu(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher allStudentByTeaId = mapper.getAllStudentByTeaIdFenBU(1);
System.out.println(allStudentByTeaId);
sqlSession.close();
}
12.5.3.3 多对多关联查询 (collection)
实际应用中,由于多对多的关系比较复杂,会增加理解和关联的复杂度,所以应用较少。MyBatis 没有实现多对多级联,推荐通过两个一对多级联替换多对多级联,以降低关系的复杂度,简化程序。
可以使用一个中间表(学生与老师表)将多对多级联转换成两个一对多的关系。
多对多不能指定条件查询,如果指定,就变成了一对多。
collection 单步查询
Student.java 完整属性。
private Integer id;
private String stuName;
private Integer teaId;
private List<Teacher> teacher;
Teacher完整属性。
private Integer teaId;
private String teaName;
private List<Student> listStu;
TeacherMapper.java
List<Teacher> SelectByManyToMany();
TeacherMapper.xml
<resultMap id="ManyToMany" type="teacher">
<id column="tea_id" property="teaId"/>
<result column="tea_name" property="teaName"/>
<collection property="listStu" ofType="com.example.pojo.Student">
<id column="id" property="id"/>
<result column="name" property="stuName"/>
<result column="tid" property="teaId"/>
</collection>
</resultMap>
<select id="SelectByManyToMany" resultMap="ManyToMany">
SELECT s.*,t.id tea_id ,t.`name` tea_name
FROM student s ,teacher t , stu_tea st WHERE s.id =st.stu_id AND st.tea_id = t.id
</select>
测试方法
@Test
public void ManyToMany(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
List<Teacher> allStudentByTeaId = mapper.SelectByManyToMany();
System.out.println(allStudentByTeaId);
sqlSession.close();
}
12.5.3.4 鉴别器(discriminator)
鉴别器:如果结果为真,就执行对应的结果映射。相当于Switch语句,
必要的属性
对于< discriminator>来说,它的两个必要的属性就是javaType和column,它们相当于指定了switch(x)中x的类型,以及它在结果集的哪列。
对于< case>来说,它也有两个必要的属性,value,相当于switch-case中的case :。第二个属性是resultMap或者resultType
<resultMap id="getStudentByDiscriminator" type="student">
<id column="id" property="id"/>
<result column="name" property="stuName"/>
<result column="tid" property="teaId"/>
<discriminator javaType="int" column="tid">
<case value="1" resultMap="getStudentByDiscriminator"/>
<case value="2" resultType="student"/>
</discriminator>
</resultMap>
<select id="getStudent" resultMap="getStudentByDiscriminator">
select * from student s where s.id = #{id}
</select>
当discriminator 中的column的值等于 case 的 值时,就使用 resultMap值的值进行映射。
需求信息
当学生的tid值等于2时 查询老师的所有信息
<resultMap id="getStudentByDiscriminator" type="student">
<id column="id" property="id"/>
<result column="name" property="stuName"/>
<!-- <result column="tid" property="teaId"/>-->
<discriminator javaType="int" column="tid">
<case value="1" resultMap="getStudentByDiscriminator"/>
<case value="2" resultType="student">
<association property="teacher" column="tid" select="com.example.mapper.TeacherMapper.getTeacher"/>
</case>
</discriminator>
</resultMap>
<select id="getStudent" resultMap="getStudentByDiscriminator">
select * from student s where s.id = #{id}
</select>
测试代码
@Test
public void testStudent(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
// 等于1 不查询老师的信息,等于2 查询出老师的信息
Student student = mapper.getStudent(2);
System.out.println(student);
sqlSession.close();
}
测试结果, 测试id等于1时
测试结果, 测试id等于2时
12.5.3.5 sql片段
这个元素可以用来定义可重用的 SQL 代码片段,以便在其它语句中使用。 参数可以静态地(在加载的时候)确定下来,并且可以在不同的 include 元素中定义不同的参数值。比如:
<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>
<select id="selectUsers" resultType="map">
select
<include refid="userColumns"><property name="alias" value="t1"/></include>,
<include refid="userColumns"><property name="alias" value="t2"/></include>
from some_table t1 cross join some_table t2
</select>
总结
整个SQL的语句中,都可以使用sql片段,达到可以在多长引用的目的。
12.6 动态SQL
解决需要手动拼接 SQL 语句容易出现问题的麻烦。如去除空格,逗号等。
12.6.1 if标签
表示 如果传进来的属性值不为空,值执行对应的条件。
<select id="selectIf" resultType="student">
select * from student where
<if test="id!=null" >
id = #{id}
</if>
<if test="teaId!=null" >
and tid = #{teaId}
</if>
<if test="stuName!=null" >
and name like #{stuName}
</if>
</select>
但是,以上有一个缺陷,如果第一个条件为空,那么where 与and语句就是一个错误的语句。解决办法是在where后面加一个 必成立的条件。
<select id="selectIf" resultType="student">
select * from student where 1=1
<if test="id!=null" >
id = #{id}
</if>
<if test="teaId!=null" >
and tid = #{teaId}
</if>
<if test="stuName!=null" >
and name like #{stuName}
</if>
</select>
上面的问题解决了,但是,发现不怎么友好,最好的解决办法是加一个where标签
<select id="selectIf" resultType="student">
select s.id, s.name stu_name,s.tid tea_id from student s
<where>
<if test="id!=null" >
and id = #{id}
</if>
<if test="teaId!=null" >
and tid = #{teaId}
</if>
<if test="stuName!=null" >
and name like #{stuName}
</if>
</where>
</select>
update 更新
<update id="update" >
update student
<set>
<if test="id!=null" >
id = #{id}
</if>
<if test="teaId!=null" >
tid = #{teaId}
</if>
<if test="stuName!=null" >
name = #{stuName}
</if>
</set>
where id = #{id}
</update>
12.6.2 trim 标签
支持在条件前或后增加拼接字符串,如,在查询 条件前增加where字符串,第一个条件会有where,如果到了最后一个where,则去除后面的逗号。
<!--
trim:
prefix:给拼串后的整个字符串加一个前缀
suffixOverrides:去掉整个字符串后面多余的字符,支持或(|)
prefixOverrides:去掉整个字符串前面多余的字符,支持或(|)
suffix:给拼串后的整个字符串加一个后缀
-->
<select id="selectIf1" resultType="student">
select s.id, s.name stu_name,s.tid tea_id from student s
<trim prefix="where" suffixOverrides="and|RO">
<if test="id!=null" >
id = #{id} and
</if>
<if test="teaId!=null" >
tid = #{teaId} and
</if>
<if test="stuName!=null" >
name like #{stuName} and
</if>
</trim>
</select>
更新中的Set方法如下
<update id="update" >
update student
<trim prefix="set" suffixOverrides=",">
<if test="id!=null" >
id = #{id} ,
</if>
<if test="teaId!=null" >
tid = #{teaId},
</if>
<if test="stuName!=null" >
name = #{stuName},
</if>
</trim>
where id = #{id}
</update>
12.6.3 choose (when, otherwise)
跟Switch语句一样,满足什么条件就去查询什么条件,有默认值,跟if不同的是,if没有默认值。
<select id="selectIf2" resultType="student">
select s.id, s.name stu_name,s.tid tea_id from student s
<trim prefix="where" suffixOverrides="and|RO">
<choose>
<when test="id!=null" >
s.id = #{id}
</when>
<when test="teaId!=null" >
s.tid = #{teaId}
</when>
<when test="stuName!=null" >
s.name like #{stuName}
</when>
<otherwise>
s.name = #{name}
</otherwise>
</choose>
</trim>
</select>
12.6.4 foreach标签
代表迭代器,如,SQL中的子查询 in 语句。
foreach 标签主要有以下属性,说明如下。
item:表示集合中每一个元素进行迭代时的别名。
index:指定一个名字,表示在迭代过程中每次迭代到的位置。
open:表示该语句以什么开始(既然是 in 条件语句,所以必然以(开始)。
separator:表示在每次进行迭代之间以什么符号作为分隔符(既然是 in 条件语句,所以必然以,作为分隔符)。
close:表示该语句以什么结束(既然是 in 条件语句,所以必然以)开始)。
使用 foreach 标签时,最关键、最容易出错的是 collection 属性,该属性是必选的,但在不同情况下该属性的值是不一样的,主要有以下 3 种情况:
如果传入的是单参数且参数类型是一个 List,collection 属性值为 list。
如果传入的是单参数且参数类型是一个 array 数组,collection 的属性值为 array。
如果传入的参数是多个,需要把它们封装成一个 Map,当然单参数也可以封装成 Map。Map 的 key 是参数名,collection 属性值是传入的 List 或 array 对象在自己封装的 Map 中的 key。
StudentMapper.java
List<Student> forEach(List<Integer> list);
<select id="forEach" resultType="student">
select s.id, s.name stu_name,s.tid tea_id from student s
<where> s.tid in
<foreach collection="list" index="index" item="tid" open="(" separator="," close=")">
#{tid}
</foreach>
</where>
</select>
测试
@Test
public void forEach(){
StudentMapper studentMapper = MybatisUtils.getSqlSession().getMapper(StudentMapper.class);
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
List<Student> list1 = studentMapper.forEach(list);
for (Student s:list1) {
System.out.println(s);
}
}
运行结果
12.6.5 两个内置参数
mybatis默认还有两个内置参数:
- _parameter:代表整个参数
单个参数:_parameter就是这个参数
多个参数:参数会被封装为一个map,_parameter就是代表这个map
_databaseId:如果配置了databaseIdProvider标签,那么databaseId就是代表当前数据库的别名,比如:oracle
<select id="forEach1" resultType="student">
<bind name="stuName" value="'%'+stuName+'%'"/>
select s.id, s.name stu_name,s.tid tea_id from student s
<where>
<if test="_parameter!=null">
s.name like #{stuName}
</if>
</where>
</select>
12.6.6 分页
MyBatis 的分页功能是基于内存的分页,即先查询出所有记录,再按起始位置和页面容量取出结果。
SQL 语句分页
List<Student> page(@Param("indexPage") Integer pageNum, @Param("pageSize")Integer pageSize);
<select id="page" resultType="student" >
select s.id, s.name stu_name,s.tid tea_id from student s
ORDER BY s.id limit #{indexPage},#{pageSize}
</select>
RowBounds
List<Student> rowBounds(RowBounds rowBounds);
<select id="rowBounds" resultType="student">
select s.id, s.name stu_name,s.tid tea_id from student s
</select>
@Test
public void rowBounds(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
RowBounds rowBounds = new RowBounds(3,3);
List<Student> page = mapper.rowBounds(rowBounds);
for (Student s:page) {
System.out.println(s);
}
}
12.7缓存
MyBatis系统中默认定义了两级缓存,它们分别是:一级缓存和二级缓存。
12.7.1 一级缓存
级缓存(local cache),即本地缓存,作用域默认为 sqlSession。当 Session flush 或 close 后,该 Session 中的所有 Cache 将被清空。
本地缓存不能被关闭,但可以调用 clearCache() 来清空本地缓存或者改变缓存的作用域。
12.7.2 二级缓存
二级缓存(second level cache),它是基于namespace级别的全局作用域缓存,二级缓存默认不开启,需要手动配置,要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:
<!--
cache:
eviction:缓存的回收策略。
• LRU – 最近最少使用:移除最长时间不被使用的对象。(默认值)
• FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
• SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
• WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
flushInterval:刷新间隔,该属性可以被设置为任意的正整数,单位为毫秒, 默认情况是不设置。
size:引用数目,该属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源,默认值是 1024。
readOnly:只读,该属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。
type:指定自定义缓存的全类名,实现Cache接口即可。
-->
<cache eviction="LRU"
flushInterval="60000"
size="1024"
readOnly="true"
type="" />
<!-- 引用另一个命名空间的缓存-->
<cache-ref namespace="com.someone.application.data.SomeMapper"/>
12.7.3 和缓存有关的设置
- mybatis-config.xml的settings标签中的配置:
< setting name=“localCacheScope” value=“SESSION”/>,本地缓存(一级缓存)作用域,当前会话的所有数据保存在会话缓存中,如果值为STATEMENT可以禁用一级缓存
< setting name=“cacheEnabled” value=“true”/>,二级缓存开启和关闭的开关,如果为true则开启二级缓存
- xxxxxMapper.xml的每个select标签中的配置:
useCache=“true”:默认值为true,如果值为false代表select标签不使用缓存(一级缓存可使用,二级缓存不使用)
flushCache=“false”:默认值为false,如果值为false代表select标签不清除缓存
- xxxxxMapper.xml的每个insert、update、delete标签中的配置:
flushCache=“true”:默认值为true,如果值为true代表清除缓存(一级二级都会清除)
- sqlSession的调用代码中:
sqlSession.clearCache():只是清除当前session的一级缓存,而二级缓存不会清除
12.8 代码生成
代码生成器依赖
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.4.0</version>
</dependency>
生成器配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!-- j驱动包绝对路径 -->
<classPathEntry
location="E:\Program Files (x86)\maven\apache-maven-3.8.1\repository\org\mybatis\generator\mybatis-generator-core\1.4.0\mybatis-generator-core-1.4.0.jar"/>
<context id="default" targetRuntime="MyBatis3">
<property name="javaFileEncoding" value="UTF-8"/>
<!-- 不输出注释 -->
<commentGenerator>
<property name="suppressDate" value="true"/>
<property name="suppressAllComments" value="true"/>
<property name="addRemarkComments" value="true"/>
</commentGenerator>
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/localhost"
userId="root"
password="root">
</jdbcConnection>
<!-- 不强制把所有的数字类型转化为BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<javaModelGenerator targetPackage="com.example.entity" targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
</javaModelGenerator>
<sqlMapGenerator targetPackage="mappers" targetProject="src/main/resources">
<property name="enableSubPackages" value="true"/>
</sqlMapGenerator>
<javaClientGenerator type="XMLMAPPER" targetPackage="com.example.dao" targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<table tableName="student"
enableCountByExample="false"
enableDeleteByExample="false"
enableSelectByExample="false"
enableUpdateByExample="false"
domainObjectName="Student"
mapperName="StudentMapper">
<generatedKey column="id" sqlStatement="MySql"/>
</table>
</context>
</generatorConfiguration>
驱动包路径查看f方式
运行代码生成器
package com.example.utils;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* @Author: lyf
* @CreateTime: 2022-12-08
* @description:
*/
public class MybatisGeneratorUtils {
public static void main(String[] args) throws Exception {
List<String> warnings = new ArrayList<>();
// 如果已经存在生成过的文件是否进行覆盖
boolean overwrite = true;
File configFile = new File("E:\\testProject\\Mybatis3\\src\\main\\resources\\mybatis-generator-config.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator generator = new MyBatisGenerator(config, callback, warnings);
generator.generate(null);
}
}
12.9 注解开发
配置文件
<?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="dataSource.properties"/>-->
<!-- 第二种配置 子属性-->
<properties>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/localhost?useSSL=true&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</properties>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- <setting name="logImpl" value="log4j2"/>-->
</settings>
<typeAliases>
<!-- 给一个类设置别名,不设置值。默认是类名小写-->
<typeAlias alias="user" type="com.example.pojo.User"/>
<!-- 以包下的类设置别名 别名类名首字母小写-->
<package name="com.example.pojo"/>
</typeAliases>
<!-- typeHandler 的作用就是承担 jdbcType 和 javaType 之间的相互转换-->
<typeHandlers>
</typeHandlers>
<!-- 数据库环境 default 属性指定一个默认数据源-->
<environments default="development">
<!-- 数据源配置-->
<environment id="development">
<!-- 配置事务 类型
JDBC 和 MANAGED
如果使用 JDBC 类型的事务管理器,则应用程序服务器负责事务管理操作,例如提交、回滚等。
如果使用 MANAGED 类型的事务管理器,则应用程序服务器负责管理连接生命周期
-->
<transactionManager type="JDBC"/>
<!-- 配置数据库的连接属性
1)UNPOOLED
没有数据库连接池,效率低下。需要每次打开和关闭每个数据库操作的连接。
2)POOLED
MyBatis 将维护一个数据库连接池。
3)JNDI
MyBatis 将从 JNDI 数据源中获取连接。
-->
<dataSource type="POOLED">
<!-- <property name="driver" value="${datasource.driver}"/>-->
<!-- <property name="url" value="${datasource.url}"/>-->
<!-- <property name="username" value="${datasource.username}"/>-->
<!-- <property name="password" value="${datasource.password}"/>-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!-- 指定Mapper文件所在的位置-->
<mappers>
<package name="com.example.mapper"/>
</mappers>
</configuration>
数据准备
#清空表
TRUNCATE TABLE employee;
TRUNCATE TABLE department;
#添加数据向employee表
INSERT INTO employee(id,last_name,email,gender,dep_id)
VALUES(NULL,"张三","123@qq.com","男",1);
INSERT INTO employee(id,last_name,email,gender,dep_id)
VALUES(NULL,"李四","123@qq.com","男",2);
INSERT INTO employee(id,last_name,email,gender,dep_id)
VALUES(NULL,"王五","123@qq.com","女",3);
INSERT INTO employee(id,last_name,email,gender,dep_id)
VALUES(NULL,"小六","123@qq.com","男",1);
INSERT INTO employee(id,last_name,email,gender,dep_id)
VALUES(NULL,"小七","123@qq.com","男",2);
INSERT INTO employee(id,last_name,email,gender,dep_id)
VALUES(NULL,"老八","123@qq.com","女",3);
INSERT INTO employee(id,last_name,email,gender,dep_id)
VALUES(NULL,"老九","123@qq.com","男",1);
#添加数据向department表
INSERT INTO department(dep_id,dep_name) VALUES(1,"运营部");
INSERT INTO department(dep_id,dep_name) VALUES(2,"开发部");
INSERT INTO department(dep_id,dep_name) VALUES(3,"产品部");
实体类对象按数据表创建或者用代码生成即可。
12.9.1 增删改查注解
public interface EmployeeMapper {
@Select(value = "select * from employee where id=#{id}")
List<Employee> getListEmp(Integer id);
/**
* 插入员工
*
* @param employee
*/
@Insert(value = "insert into employee(id,last_name,email,gender,dep_id) values(null,#{lastName},#{email},#{gender},#{dep.id})",databaseId="mysql")
void insertOne(Employee employee);
@Select(value = "select * from employee where id =#{id}",databaseId = "mysql")
Employee selectOne(Integer id);
@Select(value = "select * from employee")
List<Employee> selectList();
/**
* 更新员工信息
* @param emp
* @Update:更新员工信息
* value:更新语句,如果只有一个数据源,可以直接@Update("update ...")
* databaseId:数据库别名,如果只有一个数据源,可以省略,@since 3.5.5
*/
@Update(value = "update employee set last_name=#{lastName},email=#{email},gender=#{gender},dep_id=#{dep.id} where id = #{id}", databaseId = "mysql")
public void updateEmp(Employee emp);
/**
* 删除员工信息
* @param id
* @Delete:删除员工信息
* value:
*/
@Delete(value = "delete from employee where id=#{id}", databaseId = "mysql")
public void deleteEmpById(Integer id);
}
12.9.2 一对一注解
需求信息: 查询员工所在的部门
EmployeeMapper
/**
* 根据员工的部门id查询出部门信息
* @param depId
* @return
* @Results:代替的是<resultMap>标签,注解中可以使用单个@Result注解,也可以使用@Result集合
* @Result:代替的是<id>标签和<result>标签
* id:是否为主键,是为true,否为false
* column:数据库的列名
* property:实体类的属性名
* @Many:一对多配置,代替的是<collection>标签,是多表查询的关键,在注解中用来指定子查询返回对象集合
* select:指定用来多表查询的sqlmapper,它是对应接口方法的全限定方法名
* fetchType:懒加载配置,会覆盖全局的配置参数 lazyLoadingEnabled,一对多一般是延迟加载
* FetchType.LAZY:延迟加载
* FetchType.EAGER:立即加载
*/
@Select(value = "select * from employee where id =#{id}")
@Results({
@Result(id = true,column = "id",property = "id"),
@Result(column = "last_name",property = "lastName"),
@Result(column = "email",property = "email"),
@Result(column = "gender",property = "gender"),
@Result(column = "dep_id",property = "dep",one = @One(select = "com.lyf.mybatis.mapper.DepartmentMapper.getById",fetchType = FetchType.EAGER))
})
Employee getEmpById(Integer id);
DepartmentMapper
@Select(value = "select * from department where dep_id =#{id}" ,databaseId = "mysql")
Department getById(Integer id);
12.9.3 一对多注解
需求信息: 根据部门id查询出所有的员工信息
EmployeeMapper
@Select(value = "select * from employee where id =#{id}",databaseId = "mysql")
Employee selectOne(Integer id);
DepartmentMapper
/**
* 根据部门编号查询部门级联查询该部门下所有员工
* @param depId
* @return
* @Results:代替的是<resultMap>标签,注解中可以使用单个@Result注解,也可以使用@Result集合
* @Result:代替的是<id>标签和<result>标签
* id:是否为主键,是为true,否为false
* column:数据库的列名
* property:实体类的属性名
* @Many:一对多配置,代替的是<collection>标签,是多表查询的关键,在注解中用来指定子查询返回对象集合
* select:指定用来多表查询的sqlmapper,它是对应接口方法的全限定方法名
* fetchType:懒加载配置,会覆盖全局的配置参数 lazyLoadingEnabled,一对多一般是延迟加载
* FetchType.LAZY:延迟加载
* FetchType.EAGER:立即加载
*/
@Select(value = "select * from department where dep_id = #{id}")
@Results({
@Result(id = true,column = "dep_id",property = "id"),
@Result(column = "dep_name",property = "depName"),
@Result(column = "dep_id",property = "employeeList",many = @Many(select = "com.lyf.mybatis.mapper.EmployeeMapper.getListEmp",fetchType = FetchType.EAGER))
})
Department getDep(Integer id);
总结
@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result一起使用,封装多个结果集
@ResultMap:实现引用@Results定义的封装
@One:实现一对一结果集封装
@Many:实现一对多结果集封装
在注解中使用动态SQL看起来比较乱。