Mybatis-学习笔记
Mybatis
一 、简介
-
什么是Mybatis
-
MyBatis 是一款 优秀的持久层框架
-
它支持自定义 SQL、存储过程以及高级映射。
-
MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
-
MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口、
和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
-
-
导入Mybatis
-
maven 仓库
<!--Mybatis 依赖--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency>
-
二、Mybatis 使用
思路:搭建Maven环境 --> 导入 Mybatis 依赖 --> 编写 Mybatis.xml 文件配置 --> 编写代码 --> 测试
1、搭建Maven及其导入依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<!--Mybatis 依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!--mysql 驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
2、编写 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",默认使用的环境-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--连接数据库的 4 个必填属性-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&charterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="970699"/>
</dataSource>
</environment>
<!--第二套环境-->
<!--
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
-->
</environments>
<!--每一个Mapper.xml都需要在Mybatis核心配置文件中
这里需要使用Maven解决资源过滤问题,因为UserMapper.xml不会自动打包,解决办法在pom.xml导入资源导出问题!!
-->
<mappers>
<!--这里必须使用/表示目录,不能用.-->
<mapper resource="com/wyx/dao/UserMapper.xml"/>
</mappers>
</configuration>
<!--使用时出现编码,报错,请将IDEA的文件编码改成 UTF-8 -->
3、编写工具类,获取 SqlSession
SqlSession:可以直接执行 SQL 语句。
package com.wyx.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.*;
import java.io.IOException;
import java.io.InputStream;
//获取 SqlSessionFactory --> sqlSession
public class Mybyatis {
private static SqlSessionFactory sqlSessionFactory;
//类一加载就需要获得
static {
try {
//使用Mybatis第一步,获取SqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//返回sqlSession对象,,,openSession(true) 这里填写参数后,就会自动提交事务,但是在开发中一般不设置为true
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
4、编写代码
-
实体类
package com.wyx.pojo; public class User { private Integer id; private String userName; private String passWord; public User() { } public User(Integer id, String userName, String passWord) { this.id = id; this.userName = userName; this.passWord = passWord; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassWord() { return passWord; } public void setPassWord(String passWord) { this.passWord = passWord; } @Override public String toString() { return "User{" + "id=" + id + ", userName='" + userName + '\'' + ", passWord='" + passWord + '\'' + '}'; } }
-
Dao接口
package com.wyx.dao; import com.wyx.pojo.User; import java.util.List; public interface UserDao { List<User> getUserList(); }
-
接口实现类,由原来的 UserDaoImpl 转换为 Mapper.xml 配置文件(文件名尽量和接口名相同,但是一般以Mapper结尾)。
<?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"> <!--namespace 对应相应的接口--> <mapper namespace="com.wyx.dao.UserMapper"> <!--id 对应方法名字,resultType 对应返回值类型--> <select id="getUserList" resultType="com.wyx.pojo.User"> <!--sql 语句,记得表的时候加上数据库名--> select * from mybatis.user </select> </mapper>
在**mybatis-config.xml** 添加类的映射(**本程序上面已经添加,这里不用复制上去**)
```xml
<mappers>
<mapper resource="UserMapper.xml"/>
</mappers>
-
编写测试类
package com.wyx.dao; import com.wyx.pojo.User; import com.wyx.utils.Mybatis; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import java.util.List; public class UserDaoTest { @Test public void test(){ // 第一步获取 SqlSession 对象 SqlSession sqlSession = Mybatis.getSqlSession(); // 执行SQL UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> userList = userMapper.getUserList(); for (User user:userList) { System.out.println(user); } // 第二种方式,不推荐使用 // List<User> userList2 = sqlSession.selectList("com.wyx.dao.UserMapper.getUserList()"); // for (User user:userList2) { // System.out.println(user); // } sqlSession.close(); } }
-
可能遇见的问题
- 配置文件没有注册
- 接口绑定错误
- 方法名不对
- 返回值类型不对
- Maven资源导出问题
三、Maybatis 的 CRUD(增删改查)
第一步注册好后,只需要编写,接口中的方法,然后在对应Mapper.xml中进行配置即可。
namespace: 中的包名要和Dao/Mapper 接口的包名一致(全路径)
id: 对应接口的方法名;
resultType: sql语句的返回值类型
parameterType: 参数类型
**编写接口方法 --》 编写Mapper.xml的sql语句 --》编写测试类 **
1、基础使用
-
select
package com.wyx.dao; import com.wyx.pojo.User; public interface UserMapper { // List<User> getUserList(); User getUserId(int id); }
记得以下标签是放在
这里面 <select id="getUserId" resultType="com.wyx.pojo.User" parameterType="int"> select * from mybatis.user where id=#{id} </select>
@Test public void test1(){ SqlSession sqlSession = Mybatis.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User userId = mapper.getUserId(3); System.out.println(userId); sqlSession.close(); }
-
Insert
//在接口中 int addUser(User user);
记得以下标签是放在
这里面 <insert id="addUser" parameterType="com.wyx.pojo.User"> insert into mybatis.user (id,userName,passWord) values (#{id},#{userName},#{passWord}) </insert>
@Test public void addUser(){ SqlSession sqlSession = Mybatis.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); mapper.addUser(new User(4,"网红","000123")); //提交事务,不然数据库提交不成功 sqlSession.commit(); sqlSession.close(); }
-
update
int updateUser(User user);
<update id="updateUser" parameterType="com.wyx.pojo.User"> update mybatis.user set userName= #{userName},passWord = #{passWord} where id = #{id}; </update>
@Test public void updateUser(){ SqlSession sqlSession = Mybatis.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); mapper.updateUser(new User(2,"张超","145623")); //提交事务,不然数据库提交不成功 sqlSession.commit(); sqlSession.close(); }
-
delete
int deleteUser(int id);
<delete id="deleteUser" parameterType="int"> delete from mybatis.user where id = #{id}; </delete>
@Test public void deleteUser(){ SqlSession sqlSession = Mybatis.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); mapper.deleteUser(3); //提交事务,不然数据库提交不成功 sqlSession.commit(); sqlSession.close(); }
注意:增删改需要提交事务
sqlSession.commit();
2、使用Map
假设我们写的实体类,或者数据库的表,字段或参数较多,我们使用Map能不用写那么多参数,所以会更好用
map:传递参数,直接在sql取key,
对象传递参数:直接在sql中取对象的属性即可!
只有一个参数,可以直接取。不用加参数类型也可以
多个参数使用Map,或者 注解(后面学)
int addUser2(Map<String,Object> map);
<insert id="addUser2" parameterType="map">
insert into mybatis.user (id,passWord) values (#{id},#{pwd})
</insert>
@Test
public void addUser2(){
SqlSession sqlSession = Mybatis.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("id",5);
map.put("pwd","214124");
mapper.addUser2(map);
sqlSession.commit();
sqlSession .close();
}
3、模糊查询
模糊查询怎么写?
-
在Java代码中,传递通配符%值%
List<User> userList = mapper.getUserLike("%李%")
-
在Sql 语句中使用通配符!
select * from mybatis.user where id="%"#{id}"%"
四、配置解析
配置优化
1、核心配置文件
- mybatis.xml
- Mybatis 的配置文件包含了 Mybatis 行为的设置和属性信息。
2、环境配置(environments)
MyBatis 可以配置成适应多种环境,
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
Mybatis 默认的事务管理器就是JDBC,连接池:POOLED(有池连接)
3、属性(properties)
我们可以通过 properties 属性来实现应用配置文件
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。【db.properties】
编写一个配置文件 db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8
username=root
password=970699
在核心配置文件中引入以上资源
<!--
在 configuration 第一个添加这个标签,property,可以配置用户名和密码,但是优先使用 db.properties 中的,
使用前请删掉注释,不然会报错
-->
<properties resource="db.properties">
<property name="userName" value="root"/>
<property name="password" value="970699"/>
</properties>
<!--
用了以上的配置,那么配置环境时就可以,使用以下配,
使用前请删掉注释,不然会报错
-->
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
4、类型别名(typeAliases)
别名优化
- 类型别名可为 Java 类型设置一个缩写名字。
- 存在的意在降低冗余的全限定类名书写。
- 第一种方式
<!--
注意书写的位置
取别名,可以防止写全类名,减少使用全限定名,
使用前请删掉注释,不然会报错
-->
<typeAliases>
<typeAlias alias="User" type="com.wyx.pojo.User"/>
</typeAliases>
- 第二种方式:也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean
<!--
扫描实体包,他的默认别名就是这个类的类名,首字母小写
使用前请删掉注释,不然会报错
-->
<typeAliases>
<package name="com.wyx.dao"/>
</typeAliases>
实体类少使用第一种,实体类多,使用第二种
第一种可以自定义别名,第二种不行,如果非要改,可以在实体类上使用注解取别名
@Alias("author")
public class Author {
...
}
5、设置
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
更多设置查看官网,一般不使用。
6、其他设置
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- mybatis-plus
- 通用mapper
- mybatis-generator-core
7、映射器(mappers)
- 推荐使用第一种注册方式,
- 第二种和第三种,必须保证接口名和mapper文件同名,并且需要在同一个包下面
8、生命周期和作用域
生命周期类别是至关重要的,因为错误的使用会导致非常严重的 并发问题。
五、解决属性名和字段名不匹配的问题
1、问题
由于在创建实体类时,因为属性名和数据库中的字段名不一样导致,在执行过程中会查出空值。
- 解决方式(简单暴力)
<select id="getUserId" resultType="com.wyx.pojo.User" parameterType="int">
select id,pwd as password from mybatis.user where id=#{id}
</select>
2、resultMap
结果集映射,解决以上问题
<!--
resultMap="UserMap" 必须对应 id="UserMap" id和resultMap的值必须相同,
column="id" 对应数据库中的字段,property="id" 对应实体类中的属性名
字段名和是属性名一样的地方可以不用写 <result column="id" property="id"/>
-->
<resultMap id="UserMap" type="User">
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
<select id="getUserList" resultMap="UserMap">
select * from mybatis.user
</select>
resultMap
元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBCResultSets
数据提取代码中解放出来- ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
- 如果这个世界总是这么简单就好了。
六、日志
1、日志工厂
如果一个数据库操作,出现了异常,我们需要排错,日志就是最好的选择。
日志工厂:
- SLF4J
- LOG4J 【掌握】
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING 【掌握】
- NO_LOGGING
在Mybatis中具体使用那一个日志实现,在设置中设定!
- STDOUT_LOGGING 标准日志输出
配置日志:
<!--不能有空格,名字大小写也不能有问题!-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
2、Log4j
什么是Log4j:
- Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
- 我们也可以控制每一条日志的输出格式
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
- 可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码
- 先导入Log4j 的包
<!--Log4j 依赖-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
-
创建
log4j.properyies
文件#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码 log4j.rootLogger=DEBUG,console,file #控制台输出的相关设置 log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.Threshold=DEBUG log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=[%c]-%m%n #文件输出的相关设置 log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/wyx.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n #日志输出级别 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sq1.PreparedStatement=DEBUG
-
配置Log4j 为日志实现
<settings> <setting name="logImpl" value="LOG4J"/> </settings>
3、Log4j 简单使用
-
在要使用Log4j 的类中导入包
import org.apache.log4j.Logger;
-
日志对象参数为当前的 class
static Logger logger = Logger.getLogger(UserDaoTest.class);
-
**log4j ** 调试常用级别
logger.info("-info:进入了 testLog4j"); logger.debug("-debug:进入了 testLog4j"); logger.error("-error:进入了 testLog4j");
七、数据库分页查询
为什么使用分页?
1、简单分页实现
- 减少数据的处理量
sql 使用 Limit 分页
# 2 代表重第几个开始,3代表,每页查询几个
select * from user limit 2,3
# 只要一个参数时,代表,重第0个开始,每页查询 3(参数) 个任
select * from user limit 3
在 java 中使用分页查询,使用 Map 集合来实现,实现过程参考下面
List<User> limitGetUser(Map<String,Integer> map);
<select id="limitGetUser" resultType="User" parameterType="map">
select * from mybatis.user limit #{Indexparams},#{Lastparams}
</select>
@Test
public void limitGetUser(){
SqlSession sqlSession = Mybatis.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("Indexparams",2);
map.put("Lastparams",3);
List<User> users = mapper.limitGetUser(map);
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
2、RowBounds分页(了解)
List<User> limitGetUserRowBounds();
<select id="limitGetUserRowBounds" resultType="User">
select * from mybatis.user
</select>
@Test
public void limitGetUserRowBounds(){
RowBounds rowBounds = new RowBounds(2, 3);
SqlSession sqlSession = Mybatis.getSqlSession();
List<User> users = sqlSession.selectList("com.wyx.dao.UserMapper.limitGetUserRowBounds", null, rowBounds);
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
3、分页插件
Mybatis pageHelper 插件使用,详细看官网,很简单:https://pagehelper.github.io/
八、使用注解开发
对于像 BlogMapper 这样的映射器类来说,还有另一种方法来完成语句映射。 它们映射的语句可以不用 XML 来配置,而可以使用 Java 注解来配置。
使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。
- 在接口的方法上添加注解(里面填写Sql语句)
@Select("select * from user")
List<User> getUser();
- 在
mybatis-config.xml
中配置映射
<mappers>
<mapper class="com.wyx.dao.UserMapper"/>
</mappers>
- 编写测试类
@Test
public void getUser(){
SqlSession sqlSession = Mybatis.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.getUser();
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
1、Mybatis 执行流程解析
2、注解的增删改查
- 在接口的方法上添加注解(里面填写Sql语句)(其他的增、删、改,使用 对应的注解即可,记得提交事务)
@Select("select * from user where id = #{uid}")
User getUserById(@Param("uid") int id);
- 在
mybatis-config.xml
中配置映射
<mappers>
<mapper class="com.wyx.dao.UserMapper"/>
</mappers>
- 编写测试类
@Test
public void getUserById(){
SqlSession sqlSession = Mybatis.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User userId = mapper.getUserById(3);
System.out.println(userId);
sqlSession.close();
}
-
关于@Param()注解
- 基本引用类型的参数或者String类型的参数,需要加上
- 引用类型不需要加
- 如果只有一个基本类型可以忽略,建议加上
- 在SQL中引用的就是我们这里的 @Param(“uid”) 设置的属性名
-
同时在
<mappers>
中配置映射时,类路径,和资源路径,必须类路径在前面,如下<mappers> <mapper class="com.wyx.dao.UserMapper"/> <mapper resource="com/wyx/dao/UserMapper.xml"/> </mappers>
-
(#{}和${}取值问题)
(#{}是预编译处理,${}是字符串替换。)
mybatis处理#{}时,将#{}替换成?,调用PreparedStatement的set方法赋值,这种预编译的机制可以很大程度防止SQL注入;处理${}时,直接进行字符串的替换,无法防止SQL注入。
(#{}解析后会将String类型的数据自动加上引号,${}不会。例如:)select * from student where sname = #{sname} select * from student where sname = ‘${value}’
九、LomBok
介绍:
Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.
Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more.翻译如下
Lombok项目是一个Java库,它会自动插入您的编辑器和构建工具中,从而使您的Java更加生动有趣。 永远不要再写另一个getter或equals方法,带有一个注释的您的类有一个功能全面的生成器,自动化您的日志记录变量等等。
1、使用步骤
-
在IDEA中安装LomBok插件
-
在项目中导入 LomBok 的包
<!--lombok 依赖 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.18</version> </dependency>
@Getter and @Setter【学习】
@FieldNameConstants
@ToString 【学习】
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor 【学习】
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data 【学习,使用最多:无参构造,get,set,toString,hashCode,equals】
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
- 在实体类上加注解
package com.wyx.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
// @Data 【学习,使用最多:无参构造,get,set,toString,hashCode,equals】
// @AllArgsConstructor 所有参数的构造器,默认会去掉 @Data 生成的无参构造,需要的话,还需要添加这个注解 @NoArgsConstructor
// @Getter and @Setter【学习】 加在方法上,对一个字段实现get或set 方法
// @ToString 【学习】
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String userName;
private String passWord;
}
十、多对一查询结果处理(学生 --> 教师)
测试环境搭建,创建学生表和教师表。让学生表中存在教师表中的主键属性,做外键。
实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private int id;
private String teacher_name;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
//一个学生对应一个老师
private int id;
private String student_name;
private Teacher teacher;
}
1、按照查询结果嵌套
association 多对一
List<Student> getStudent();
<!--
思路:
1、查询所有的学生信息
2、根据查出来的学生tid 寻找对应的老师!!
-->
<!--结果集映射-->
<resultMap id="StudentTeacher" type="Student">
<result property="id" column="id"/>
<result property="student_name" column="student_name"/>
<!--复杂属性 Teacher 我们单独处理
对象使用:association
集合使用:collection
property="teacher":对应实体类的属性名
column="tid":对应数据库的字段名
javaType="Teacher" 对应的Java类型
select="getTeacher" 根据查询出来的 tid 然后要跳转查询的方法 id 在下面的地方
-->
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<!--查询方法入口:
结果集映射在上面
-->
<select id="getStudent" resultMap="StudentTeacher">
select * from student
</select>
<!--第一次查询后,需要跳转查询老师的表
id = #{tid} 这里最好对应跳转查询的 column="tid"一样。
但是,不一样也可以,因为mybatis做了优化,连表查询基本就是一个字段连接,所以大多数都能查询成功。
连表查询多个时必须写清楚,不然会报错
-->
<select id="getTeacher" resultType="Teacher">
select * from teacher where id = #{tid}
</select>
@Test
public void test01(){
SqlSession sqlSession = Mybatis.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> studentList = mapper.getStudent();
for (Student student : studentList) {
System.out.println(student);
}
sqlSession.close();
}
2、按结果嵌套查询
List<Student> getStudent();
<!--按结果嵌套查询-->
<!--使用别名来设计数据表,与正常的sql语句没有区别-->
<select id="getStudent" resultMap="StudentTeacher">
select s.id,s.student_name,s.tid,t.teacher_name
from student s, teacher t
where s.tid=t.id
</select>
<resultMap id="StudentTeacher" type="Student">
<result property="id" column="id"/>
<result property="student_name" column="student_name"/>
<!--对于复杂类,只需要将复杂类型的Java类型写出来,然后在其标签下,继续为他的属性一一添加放回结果集映射-->
<association property="teacher" javaType="Teacher">
<result property="id" column="id"/>
<result property="teacher_name" column="teacher_name"/>
</association>
</resultMap>
@Test
public void test01(){
SqlSession sqlSession = Mybatis.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> studentList = mapper.getStudent();
for (Student student : studentList) {
System.out.println(student);
}
sqlSession.close();
}
十一、一对多查询结果处理(教师 -->学生 )
搭建环境,和刚才一样,改变实体类即可
collection 一对多
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
//一个老师可以有多名学生
private int id;
private String teacher_name;
private List<Student> students;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private int id;
private String student_name;
}
1、按结果嵌套查询
//查寻 id 为 n 的老师下面有多少个学生
List<Teacher> getTeacher(@Param("id") int id);
<select id="getTeacher" resultMap="TeacherStudent">
select t.id,t.teacher_name,s.tid,s.id,s.student_name
from student s,teacher t
where s.tid = #{tid} and s.tid = t.id
</select>
<resultMap id="TeacherStudent" type="Teacher">
<result property="id" column="id"/>
<result property="teacher_name" column="teacher_name"/>
<collection property="students" ofType="Student">
<result property="id" column="id"/>
<result property="student_name" column="student_name"/>
</collection>
</resultMap>
@Test
public void test(){
SqlSession sqlSession = Mybatis.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
List<Teacher> teachers = mapper.getTeacher(1);
for (Teacher teacher : teachers) {
System.out.println(teacher);
}
sqlSession.close();
}
2、按照查询结果嵌套
//查寻 id 为 n 的老师下面有多少个学生
List<Teacher> getTeacher(@Param("id") int id);
<select id="getTeacher" resultMap="TeacherStudent">
select *
from teacher
where id = #{id}
</select>
<resultMap id="TeacherStudent" type="Teacher">
<result property="id" column="id"/>
<result property="teacher_name" column="teacher_name"/>
<collection property="students" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId" column="id"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
select *
from student
where tid = #{id}
</select>
@Test
public void test(){
SqlSession sqlSession = Mybatis.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
List<Teacher> teachers = mapper.getTeacher(2);
for (Teacher teacher : teachers) {
System.out.println(teacher);
}
sqlSession.close();
}
**小结: **
- 关联 - association 【多对一】
- 集合 - collection 【一对多】
- javaType & ofType
- javaType 用来指定实体类中的属性的类型
- ofType 用来指定映射的 List 或者pojo 类型,泛型的约束类型!
十二、动态SQL
动态sql:根据不同的条件生成不同的SQL语句
1. 搭建环境
create table `blog`(
`id` varchar(50) not null comment '博客id',
`title` varchar(100) not null comment '博客标题',
`author` varchar(30) not null comment '博客作者',
`create_time` datetime not null comment '创建时间',
`views` int(30) not null comment '浏览量'
)ENGINE=InnoDB DEFAULT CHARSET=utf8
实体类
package com.wyx.pojo;
import lombok.Data;
import java.util.Date;
@Data
public class Blog {
private String id;
private String title;
private String author;
private Date createTime;
private int views;
}
核心配置
<settings>
<!--开启数据库对应pojo的驼峰命名-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
Mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wyx.mapper.BlogMapper">
<insert id="addBlog" parameterType="Blog">
insert into mybatis.blog (id, title, author, create_time, views) values
(#{id}, #{title}, #{author}, #{create_time}, #{views});
</insert>
</mapper>
新建随机生成ID包
package com.wyx.utils;
import org.junit.Test;
import java.util.UUID;
@SuppressWarnings("all")
public class IDUtiles {
public static String getId(){
return UUID.randomUUID().toString().replaceAll("-","");
}
@Test
public void test(){
System.out.println(getId());
}
}
测试类:添加数据
import com.wyx.mapper.BlogMapper;
import com.wyx.pojo.Blog;
import com.wyx.utils.IDUtiles;
import com.wyx.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.Date;
public class MyTest {
@Test
public void addBlog(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
Blog blog = new Blog();
blog.setId(IDUtiles.getId());
blog.setAuthor("houdongun");
blog.setCreateTime(new Date());
blog.setViews(999);
blog.setTitle("first");
blogMapper.addBlog(blog);
blog.setId(IDUtiles.getId());
blog.setTitle("second");
blogMapper.addBlog(blog);
blog.setId(IDUtiles.getId());
blog.setTitle("third");
blogMapper.addBlog(blog);
blog.setId(IDUtiles.getId());
blog.setTitle("forth");
blogMapper.addBlog(blog);
sqlSession.close();
}
}
2. if
<select id="queryBlogIF" parameterType="map" resultType="Blog">
select * from mybatis.blog where 1=1
<if test="title != null">
and title = #{title}
</if>
<if test="author != author">
and author = #{author}
</if>
</select>
test
@Test
public void queryBlogIF(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap();
// map.put("title", "second");
map.put("author", "houdongun");
List<Blog> list = blogMapper.queryBlogIF(map);
for (Blog blog : list) {
System.out.println(blog);
}
sqlSession.close();
}
3. choose、when、otherwise
<select id="queryBlogchoose" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<choose>
<when test="title != null">
title = #{title}
</when>
<when test="author != null">
and author = #{author}
</when>
<otherwise>
and views = #{views}
</otherwise>
</choose>
</where>
</select>
4. trim、where、set
<update id="updateBlog" parameterType="map">
update mybatis.blog
<set>
<if test="title != null">
title = #{title},
</if>
<if test="author != null">
author = #{author}
</if>
</set>
where id = #{id}
</update>
trim 可以自定义
SQL片段
有些时候我们有一些公共部分
- 使用sql便签抽取公共部分
- 在使用的地方使用include标签
<sql id="if-title-author">
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
<select id="queryBlogIF" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<include refid="if-title-author"></include>
</where>
</select>
注意:
- 最好基于单表
- sql里不要存在where标签
5. for-each
<!--ids是传的,#{id}是遍历的-->
<select id="queryBlogForeach" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<foreach collection="ids" item="id" open="and ("
close=")" separator="or">
id=#{id}
</foreach>
</where>
</select>
test
@Test
public void queryBlogForeach(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap();
ArrayList<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(3);
map.put("ids",ids);
List<Blog> list = blogMapper.queryBlogForeach(map);
for (Blog blog : list) {
System.out.println(blog);
}
sqlSession.close();
}
十三、缓存(了解)
1、一级缓存
- 开启日志
- 测试一个session中查询两次相同记录。
缓存失效:
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 查询不同的mapper.xml
- 手动清除缓存
一级缓存默认开启,只在一次sqlseesion中有效
2. 二级缓存
- 开启全局缓存
<setting name="cacheEnabled" value="true"/>
- 在当前mapper.xml中使用二级缓存
<cache eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
test
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
SqlSession sqlSession1 = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.queryUserByid(1);
System.out.println(user);
sqlSession.close();
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
User user1 = userMapper1.queryUserByid(1);
System.out.println(user1);
System.out.println(user==user1);
sqlSession1.close();
}
只用cache时加序列化
<cache/>
实体类
package com.wyx.pojo;
import lombok.Data;
import java.io.Serializable;
@Data
public class User implements Serializable {
private int id;
private String name;
private String pwd;
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
}
小结:
- 只有开启了二级缓存,在Mapper下有效
- 所有数据都会先放在一级缓存
- 只有当回话提交,或者关闭的时候,才会提交到二级缓存
3. 自定义缓存-ehcache
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.0</version>
</dependency>
ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<!--
diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
user.home – 用户主目录
user.dir – 用户当前工作目录
java.io.tmpdir – 默认临时文件路径
-->
<diskStore path="java.io.tmpdir/Tmp_EhCache"/>
<!--
defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
-->
<!--
name:缓存名称。
maxElementsInMemory:缓存最大数目
maxElementsOnDisk:硬盘最大缓存个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
overflowToDisk:是否保存到磁盘,当系统当机时
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
FIFO,first in first out,这个是大家最熟的,先进先出。
LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
-->
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"/>
<cache
name="cloud_user"
eternal="false"
maxElementsInMemory="5000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>