MyBatis
MyBatis是一个持久层框架 ORM --对象关系映射
ORM:将java程序中的对象自动持久化到关系数据库中
ORM的实现思想:
将关系数据库中表中的记录映射成为对象,以对象的形式展现,程序员可以把对数据库的操作转化为对对象的操作。 因此ORM的目的是为了方便开发人员以面向对象的思想来实现对数据库的操作。 ORM 采用元数据来描述对象-关系映射细节: 元数据通常采用 XML 格式,并且存放在专门的对象-关系映射文件中。
目前流行的ORM框架:
1.JPA:本身是一种ORM规范,不是ORM框架.由各大ORM框架提供实现. 2.Hibernate:目前最流行的ORM框架.设计灵巧,性能一般,文档丰富. 3.MyBatis:本是apache的一个开源项目iBatis,提供的持久层框架包括SQL Maps和DAO,允许开发人员直接编写SQL.等
MyBatis入门
什么是 MyBatis ?
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
1 引入依赖
<!--MyBatis依赖-->
2
<dependency>
3
<groupId>org.mybatis</groupId>
4
<artifactId>mybatis</artifactId>
5
<version>3.4.4</version>
6
</dependency>
7
<!--引入mysql依赖-->
8
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
9
<dependency>
10
<groupId>mysql</groupId>
11
<artifactId>mysql-connector-java</artifactId>
12
<version>5.1.46</version>
13
</dependency>
2 创建链接数据源和事务管理的配置文件 mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
2
<!DOCTYPE configuration
3
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
4
"http://mybatis.org/dtd/mybatis-3-config.dtd">
5
<configuration>
6
<environments default="development">
7
<environment id="development">
8
<transactionManager type="JDBC"/>
9
<dataSource type="POOLED">
10
<property name="driver" value="com.mysql.jdbc.Driver"/>
11
<property name="url" value="jdbc:mysql://localhost:3306/lanqiao"/>
12
<property name="username" value="root"/>
13
<property name="password" value="root"/>
14
</dataSource>
15
</environment>
16
</environments>
17
<mappers>
18
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
19
</mappers>
20
</configuration>
3 建立数据库
4 编写表所对应的实体类
1
package org.lanqiao.pojo;
2
3
import java.util.Objects;
4
5
public class Student {
6
7
private int sid;
8
private String sname;
9
private int age;
10
private String gender;
11
private String province;
12
private int tuition;
13
14
public Student() {
15
}
16
17
public Student(String sname, int age, String gender, String province, int tuition) {
18
this.sname = sname;
19
this.age = age;
20
this.gender = gender;
21
this.province = province;
22
this.tuition = tuition;
23
}
24
25
public Student(int sid, String sname, int age, String gender, String province, int tuition) {
26
this.sid = sid;
27
this.sname = sname;
28
this.age = age;
29
this.gender = gender;
30
this.province = province;
31
this.tuition = tuition;
32
}
33
34
35
}
36
5 建立映射文件
映射文件一般和接口放在一起
<?xml version="1.0" encoding="UTF-8" ?>
2
<!DOCTYPE mapper
3
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
4
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
5
<mapper namespace="org.lanqiao.dao.IStudentDao">
6
<!--sql语句-->
7
<insert id="addStudent" parameterType="org.lanqiao.pojo.Student">
8
insert into stu(sname,age,gender,province,tuition) values (#{sname},#{age},#{gender},#{province},#{tuition})
9
</insert>
10
</mapper>
6 dao接口
public class StudentDaoImpl implements IStudentDao {
2
@Override
3
public void addStu(Student stu) throws IOException {
4
//0 读取配置文件
5
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
6
// 1 建立SqlSessionFactory 对象
7
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
8
//2 获取SqlSession对象
9
SqlSession sqlSession = sqlSessionFactory.openSession();
10
//3 执行sql
11
sqlSession.insert("addStudent",stu);
12
sqlSession.commit();
13
//4 关闭sqlsession
14
if(sqlSession!=null){
15
sqlSession.close();
16
}
17
}
7 测试
@Test
2
public void addStuTest() throws IOException {
3
IStudentDao dao = new StudentDaoImpl();
4
Student stu = new Student("小张",23,"男","山西",4500);
5
dao.addStu(stu);
6
}
8解决maven项目中找不到mybatis的映射文件的问题
<!--解决找不到mybatis的映射文件的问题-->
2
<build>
3
<resources>
4
<resource>
5
<directory>src/main/java</directory>
6
<includes>
7
<include>**/dao/*Mapper.xml</include>
8
</includes>
9
</resource>
10
</resources>
11
</build>
9解决插入数据中文乱码的问题
<property name="url" value="jdbc:mysql://localhost:3306/lanqiao?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC"/>
10 将数据库链接属性单独提出一个properties属性文件
driver=com.mysql.jdbc.Driver
2
url=jdbc:mysql://localhost:3306/lanqiao?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
3
username=root
4
password=root
11 如何让mybatis读取到properties属性文件
properties这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。例如:
<properties resource="jdbc.properties"> </properties>
MyBatis的优点
优点: MyBatis 是一个支持普通 SQL查询,存储过程和高级映射的优秀持久层框架。 MyBatis 消除了几乎所有的JDBC代码和手工设置参数以及结果集的检索。 MyBatis 使用简单的 XML或注解用于配置和原始映射,将接口和 Java 的POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
提取mybatis的工具类
1
package org.lanqiao.utils;
2
3
import org.apache.ibatis.io.Resources;
4
import org.apache.ibatis.session.SqlSession;
5
import org.apache.ibatis.session.SqlSessionFactory;
6
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
7
import org.lanqiao.pojo.Student;
8
9
import java.io.IOException;
10
import java.io.InputStream;
11
import java.util.List;
12
13
public class MybatisUtils {
14
15
public static SqlSession getSqlSession(String mybatisConfigPath){
16
//0 读取配置文件
17
InputStream is = null;
18
List<Student> allStu=null;
19
try {
20
is = Resources.getResourceAsStream(mybatisConfigPath);
21
} catch (IOException e) {
22
e.printStackTrace();
23
}
24
// 1 建立SqlSessionFactory 对象
25
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
26
//2 获取SqlSession对象
27
SqlSession sqlSession = sqlSessionFactory.openSession();
28
return sqlSession;
29
}
30
public static void realeaseSource(SqlSession sqlSession){
31
if(sqlSession !=null){
32
sqlSession.close();
33
}
34
}
35
}
idea安装mybatis插件
mybatis引入日志
1 需要引入所使用的日志实现框架的依赖 log4j
1
<!--添加log4j日志框架-->
2
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
3
<dependency>
4
<groupId>log4j</groupId>
5
<artifactId>log4j</artifactId>
6
<version>1.2.17</version>
7
</dependency>
日志的级别
为了方便对于日制信息的输出显示,对日志内容进行了分级管理。日志级别又高到低,共分为6个级别:fatal(致命的)、error、warn、info、debug、trace(堆栈)。
Log4j建议只使用四个级别,优 先级从高到低分别是ERROR、WARN、INFO、DEBUG。
Log4j的日志输出控制文件,主要有三部分组成: 日志信息的输出位置:控制日志信息将要输出的位置,是控制台还是文件等。 日志信息的输出格式:控制日志信息的显示格式,即怎样的字符串形式显示 日志信息的输出级别:控制日志信息的显示内容,即显示那些级别的日志信息。 有了日志输出控制文件,代码中只要设置好日志信息内容及其级别即可,通过控制文件便可控制这些日志信息的输出了。
在要输出日志的类中创建日志对象Logger,并通过Logger的方法在代码中加入日志输出语句。在Java代码中记性日志输出,需要用到Logger类的静态方法getLogger();
2 添加日志框架的配置文件
### 设置###
2
log4j.rootLogger = debug,stdout,D,E
3
4
### 输出信息到控制抬 ###
5
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
6
log4j.appender.stdout.Target = System.out
7
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
8
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
9
10
### 输出DEBUG 级别以上的日志到=E://logs/error.log ###
11
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
12
log4j.appender.D.File = E://logs/log.log
13
log4j.appender.D.Append = true
14
log4j.appender.D.Threshold = DEBUG
15
log4j.appender.D.layout = org.apache.log4j.PatternLayout
16
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
17
18
### 输出ERROR 级别以上的日志到=E://logs/error.log ###
19
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
20
log4j.appender.E.File =E://logs/error.log
21
log4j.appender.E.Append = true
22
log4j.appender.E.Threshold = ERROR
23
log4j.appender.E.layout = org.apache.log4j.PatternLayout
24
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
#和符的区别
在sql语句中 如果使用的使用的时#
<insert id="addStudent" parameterType="org.lanqiao.pojo.Student">
2
insert into stu(sname,age,gender,province,tuition) values (#{sname},#{age},#{gender},#{province},#{tuition})
3
</insert>
在mybatis进行处理的时候 ,会将#{}的内容转换为?
<insert id="addStudent" parameterType="org.lanqiao.pojo.Student">
2
insert into stu(sname,age,gender,province,tuition) values (?,?,?,?,?)
3
</insert>
如果使用的时$符
<insert id="addStudent" parameterType="org.lanqiao.pojo.Student">
2
insert into stu(sname,age,gender,province,tuition) values ('${sname}',${age},'${gender}','${province}',${tuition})
3
</insert>
转化后的sql语句
insert into stu(sname,age,gender,province,tuition) values ('小张',23,'男','山西',4500)
直接将#{}的内容进行了替换
主配置文件
主配置文件名可以随意命名,其主要完成一下几个功能:
- 注册存放DB链接四要素的属性文件;
- 注册实体类的全限定性类名的别名;
- 配置MyBatis运行环境,及数据源和事务管理
- 注册映射文件
properties
这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递
<!--读取数据库链接的属性文件-->
2
<properties resource="jdbc.properties"> </properties>
typeAliases
类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。例如:
<typeAliases>
2
<typeAlias type="org.lanqiao.pojo.Student" alias="stu"/>
3
</typeAliases>
还可以使用pakcage 使用pakcage指定别名,则意味着改包下的所有类 都使用其简单类名来作为该类的别名
<typeAliases>
2
<!--<typeAlias type="org.lanqiao.pojo.Student" alias="stu"/>-->
3
<package name="org.lanqiao.pojo"></package>
4
</typeAliases>
MyBatis还提供了内置的类型别名
基本 类型
常用包装类型
配置MyBatis运行环境
配置MyBatis的运行环境,主要是配置数据源和事务管理器 A 标签 在中可以包含多个运行环境,但其default属性指定了当前MyBatis运行时所选择使用的环境
B、标签
该标签用于指定MyBatis所使用的事务管理器,MyBatis支持两种事务管理器:jdbc与Managed jdbc:使用JDBC事务管理机制,及通过Connection的commit()方法提交,通过rollback()方法回滚,但默认情况下,MyBatis将自动提交管理,改为了手动提交。及程序需要显式的对事务进行提交或回滚,从日志的输出信息中可以看到。
Managed:由容器来管理事务的整个生命周期(如spring容器)
C、标签 该标签用于配置MyBatis使用的数据源类型与数据库链接基本属性,常见的类型有: UNPOOLED,POOLED,JDNI等。 UNPOOLED:不使用连接池,即每次请求,都会为其创建一个DB链接,使用完毕后,会马上将此连接关闭。 POOLED:使用数据库连接池来维护连接 JNDI【Java 命名和目录接口(Java Naming and Directory Interface,JNDI)的简称】:数据源可以定义到应用的外部,通过JNDI容器获取数据库链接。
D、指定映射文件 class属性的值为DAO接口的全类名
<mappers>
2
<mapper class="org.lanqiao.dao.StudentMapper"></mapper>
3
</mappers>
该方式的使用,需要满足一下几个要求 (1)映射文件名要与Dao接口名称相同; (2)映射文件要与接口在同一个包中 (3)映射文件中的的namespace属性值为dao接口的全类名 也可以使用别名
<mappers>
2
<package name="org.lanqiao.dao"></package>
3
</mappers>
E、指定映射文件 当映射文件较多时,也可以使用如下形式,其中package的name属性指定一声文件所存放的包
但,这种方式的使用需要满足一下几个条件 (1)dao使用mapper动态代理机制实现;(后面讲) (2)映射文件名要与Dao接口名称相同; (3)映射文件要与接口在同一个包中; (4)映射文件中的的namespace属性值为dao接口的全类名
MyBatis的核心API
Resources Resources类,顾名思义就是资源,用于读取资源文件
SqlSessionFactory
public interface SqlSessionFactory {
2
SqlSession openSession();
3
4
SqlSession openSession(boolean var1);
5
6
SqlSession openSession(Connection var1);
7
8
SqlSession openSession(TransactionIsolationLevel var1);
9
10
SqlSession openSession(ExecutorType var1);
11
12
SqlSession openSession(ExecutorType var1, boolean var2);
13
14
SqlSession openSession(ExecutorType var1, TransactionIsolationLevel var2);
15
16
SqlSession openSession(ExecutorType var1, Connection var2);
17
18
Configuration getConfiguration();
19
}
SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
使用mybatis完成crud
删除
@Override
2
public void deleteStuById(int sid) {
3
SqlSession sqlSession = MybatisUtils.getSqlSession("mybatis-config.xml");
4
sqlSession.delete("deleteStuById",sid);
5
sqlSession.commit();
6
MybatisUtils.realeaseSource(sqlSession);
7
8
}
修改
<update id="updateStuBySid" parameterType="Student">
2
update stu set sname=#{sname} where sid = #{sid}
3
4
</update>
@Override
2
public void updateStuBySid(Student student) {
3
SqlSession sqlSession = MybatisUtils.getSqlSession("mybatis-config.xml");
4
sqlSession.update("updateStuBySid",student);
5
sqlSession.commit();
6
MybatisUtils.realeaseSource(sqlSession);
7
}
查询所有对象 返回map
<select id="findAll" resultType="Student">
2
select * from stu
3
</select>
实现
@Override
2
public Map<String, Student> findAllMap() {
3
4
SqlSession sqlSession = MybatisUtils.getSqlSession("mybatis-config.xml");
5
Map<String,Student> stuMap = sqlSession.selectMap("findAll","sname");//第一参数 执行的sql语句 第二个参数 map中的key
6
return stuMap;
7
}
查询单个对象
<select id="findStuBySid" parameterType="int" resultType="Student">
2
select * from stu where sid=#{sid}
3
4
</select>
@Override
2
public Student findStuBySid(int sid) {
3
SqlSession sqlSession = MybatisUtils.getSqlSession("mybatis-config.xml");
4
Student stu = sqlSession.selectOne("findStuBySid",sid);
5
return stu;
6
}
模糊查询
<select id="findStuLikeSname" parameterType="string" resultType="Student">
2
-- select * from stu where sname like concat('%',#{sname},'%')
3
4
select * from stu where sname like '%' #{sname} '%'
5
</select>
1
@Override
2
public List<Student> findStuLikeSname(String sname) {
3
SqlSession sqlSession = MybatisUtils.getSqlSession("mybatis-config.xml");
4
List<Student> stuList= sqlSession.selectList("findStuLikeSname",sname);
5
return stuList;
6
}
解决字段名和实体的属性名不一致的情况
1 在查询的时候 为字段使用别名
<select id="findStuBySid" parameterType="int" resultType="org.lanqiao.pojo.Student">
2
select sid id,sname name,age,gender,province,tuition from stu where sid=#{aaa}
3
4
</select>
2 使用resultMap
<resultMap id="stu" type="org.lanqiao.pojo.Student">
2
<!--主键列-->
3
<id column="sid" property="id"></id>
4
<!--其余非主键列 使用result进行映射 column 是数据库的字段名 propertiy 实体类的属性名 jdbcType对应字段的数据库中的类型 javaType 实体类中的属性的类型 -->
5
<result column="sname" jdbcType="VARCHAR" property="name" javaType="string"></result>
6
<result column="age" property="age"></result>
7
<result column="gender" property="gender"></result>
8
<result column="province" property="province"></result>
9
<result column="tuition" property="tuition"></result>
10
11
</resultMap>
1
<select id="findStuBySid" parameterType="int" resultMap="stu">
2
select sid ,sname ,age,gender,province,tuition from stu where sid=#{aaa}
3
4
</select>
Mapper的动态代理
通过之前的操作,我们发现dao的实现类其实并没有做什么实质性的工作,仅仅是通过sqlSession的相关API定位到StudentMapper映射文件中的ID中的sql语句,其实真正操作DB的是mapper中的sql。所以mybatis就抛开了dao层的实现类,可以直接定位到mapper中的sql!然后执行sql对DB进行操作!这种对dao的实现方式我们称为Mapper的动态代理方式!
Mapper接口开发需要遵循以下规范: 1、 Mapper.xml文件中的namespace与mapper接口的类路径相同。 2、 Mapper接口方法名和Mapper.xml中定义的每个statement的id相同 3、 Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同 4、 Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
在查询是进行多条件查询
1 将查询的参数封装为一个Map 封装成map的时候 map的key 必须和sql语句中的#{名称保持一致}
public List<Student> findStuBySnameAndAge(Map<String,Object> paramMap);
1
<select id="findStuBySnameAndAge" resultMap="stu">
2
select * from stu where age > #{age} and sname like '%' #{sname} '%'
3
</select>
1
@Test
2
public void findStuBySnameAndAgeTest(){
3
Map<String ,Object> paramMap = new HashMap<>();
4
paramMap.put("sage",20);
5
paramMap.put("sname","张");
6
List<Student> stuList = dao.findStuBySnameAndAge(paramMap);
7
for(Student stu : stuList){
8
System.out.println(stu);
9
}
10
}
2 将查询参数封装成一个对象
public List<Student> findStuByStudent(Student student);
1
<select id="findStuByStudent" resultMap="stu">
2
select * from stu where age > #{age} and sname like '%' #{name} '%'
3
</select>
1
@Test
2
public void findStuByStudentTest(){
3
Student student = new Student();
4
student.setAge(20);
5
student.setName("张");
6
List<Student> stuList = dao.findStuByStudent(student);
7
for(Student stu : stuList){
8
System.out.println(stu);
9
}
10
}
3 使用占位符
public List<Student> findStuByAgeAndName(int age,String name);
1
<select id="findStuByAgeAndName" resultMap="stu">
2
select * from stu where age > #{param1} and sname like '%' #{param2} '%'
3
select * from stu where age > #{arg0} and sname like '%' #{arg1} '%'
4
</select>
1
@Test
2
public void findStuByAgeAndNameTest(){
3
List<Student> stuList = dao.findStuByAgeAndName(20,"张");
4
for(Student stu : stuList){
5
System.out.println(stu);
6
}
7
}
动态SQL
动态SQL,即通过MyBatis提供的各种标签对条件做出判断以实现动态拼接sql语句。常用的动态SQL标签有,,,等。
if
<where>
2
3
<if test="arg0 != 0">
4
AND sid = #{arg0}
5
</if>
6
<if test="arg1 !=null">
7
AND sname like '%' #{arg1} '%'
8
</if>
9
</where>
<where>
2
<choose>
3
<when test="arg0 == 0">
4
AND sname like '%' #{arg1} '%'
5
6
</when>
7
<when test="arg1 ==null">
8
AND sid = #{arg0}
9
</when>
10
</choose>
11
12
</where>
<select id="findStudentByInCondition" resultMap="stu">
2
select * from stu
3
<where>
4
sid in
5
<foreach collection="array" item="sid" open="(" separator="," close=")">
6
#{sid}
7
</foreach>
8
9
</where>
10
</select>
<select id="findStudentByInCondition" resultMap="stu">
2
select * from stu
3
<where>
4
sid in
5
<foreach collection="list" item="sid" open="(" separator="," close=")">
6
#{sid}
7
</foreach>
8
9
</where>
10
</select>
<select id="findStudentByInCondition" resultMap="stu">
2
select * from stu
3
<where>
4
sid in
5
<foreach collection="list" item="student" open="(" separator="," close=")">
6
#{student.id}
7
</foreach>
8
9
</where>
10
</select>
<!--sql片段-->
2
<sql id="baseSql" >
3
select * from stu
4
5
</sql>
6
<select id="findStudentByInCondition" resultMap="stu">
7
<!--包含sql片段-->
8
<include refid="baseSql"></include>
9
<where>
10
sid in
11
<foreach collection="list" item="student" open="(" separator="," close=")">
12
#{student.id}
13
</foreach>
14
15
</where>
16
</select>
多表关联查询
一对多
多表链接查询
public class Cls {
2
private int cid;
3
private String cname;
4
private Set<Student> stuSet;
<resultMap id="clsAndStu" type="org.lanqiao.pojo.Cls">
2
<id column="cid" property="cid"></id>
3
<result column="cname" property="cname"></result>
4
<collection property="stuSet" ofType="org.lanqiao.pojo.Student">
5
<id column="sid" property="id"></id>
6
<result column="sname" property="name"></result>
7
<result column="gender" property="gender"></result>
8
</collection>
9
</resultMap>
10
<select id="findClsByCid" resultMap="clsAndStu" >
11
select c.cid,c.cname,s.sid,s.sname,s.gender from cls c ,stu s where c.cid = s.cid and c.cid=#{cid};
12
</select>
单表查询
ClsMapper.xml
<select id="findClsByCid" resultMap="clsAndStu">
2
select * from cls where cid =#{cid}
3
</select>
4
5
<resultMap id="clsAndStu" type="org.lanqiao.pojo.Cls">
6
<id column="cid" property="cid"></id>
7
<result column="cname" property="cname"></result>
8
<collection property="stuSet" ofType="org.lanqiao.pojo.Student" select="org.lanqiao.mapper.StudentMapper.findStuByCid" column="cid">
9
<id column="sid" property="id"></id>
10
<result column="sname" property="name"></result>
11
<result column="gender" property="gender"></result>
12
</collection>
13
</resultMap>
ClsMapper.java
1
public interface ClsMapper {
2
public Cls findClsByCid(int cid);
3
}
StudentMapper.xml
<mapper namespace="org.lanqiao.mapper.StudentMapper">
2
<select id="findStuByCid" resultType="org.lanqiao.pojo.Student">
3
select * from stu where cid = #{cid}
4
</select>
5
</mapper>
StudentMapper.java
public interface StudentMapper {
2
public List<Student> findStuByCid();
3
}
4
多对一
多表关联查询
实体
student
public class Student {
2
3
private int id;
4
private String name;
5
private int age;
6
private String gender;
7
private String province;
8
private int tuition;
9
private int cid;
10
private Cls cls;//关联班级信息
Cls
public class Cls {
2
private int cid;
3
private String cname;
StudenMaper.java
public Student findStuBySid(int sid);
StudenMaper.xml
resultMap id="stuAndCls" type="org.lanqiao.pojo.Student">
2
<id column="sid" property="id"></id>
3
<result column="sname" property="name"></result>
4
<result column="age" property="age"></result>
5
<result column="gender" property="gender"></result>
6
<association property="cls" javaType="org.lanqiao.pojo.Cls">
7
<id column="cid" property="cid"></id>
8
<result column="cname" property="cname"></result>
9
</association>
10
</resultMap>
11
12
<select id="findStuBySid" resultMap="stuAndCls">
13
14
select s.sid,s.sname,s.age,s.gender ,c.cname from stu s,cls c where s.cid = c.cid and
15
sid = #{sid}
16
</select>
单表查询
studenMapper.xml
<resultMap id="stuToCls" type="org.lanqiao.pojo.Student">
2
<id column="sid" property="id"></id>
3
<result column="sname" property="name"></result>
4
<result column="age" property="age"></result>
5
<result column="gender" property="gender"></result>
6
<result column="cid" property="cid"></result>
7
<association property="cls" javaType="org.lanqiao.pojo.Cls" select="org.lanqiao.mapper.ClsMapper.findClsByCid" column="cid">
8
<id column="cid" property="cid"></id>
9
<result column="cname" property="cname"></result>
10
</association>
11
</resultMap>
12
<select id="findStuBySid1" resultMap="stuToCls">
13
select sid,sname,age,gender,cid from stu where sid=#{sid}
14
</select>
clsMapper.xml
<select id="findClsByCid" resultType="org.lanqiao.pojo.Cls">
2
select * from cls where cid =#{cid}
3
</select>
多对多
实体
student.java
1
public class Student {
2
3
private int id;
4
private String name;
5
private int age;
6
private String gender;
7
private String province;
8
private int tuition;
9
private Set<Teacher> teacherSet= new HashSet<>();
Teacher.java
1
public class Teacher {
2
private int tid;
3
private String tname;
4
private Set<Student> studentSet = new HashSet<>();
studentMapper.java
public interface StudentMapper {
3
public Student findStudentAndTeacherBySid(int sid);
4
5
}
StudentMapper.xml
<resultMap id="stuAndTea" type="org.lanqiao.pojo.Student">
2
<id column="sid" property="id"></id>
3
<result column="sname" property="name"></result>
4
<result column="age" property="age"></result>
5
<result column="gender" property="gender"></result>
6
<collection property="teacherSet" ofType="org.lanqiao.pojo.Teacher">
7
<id column="tid" property="tid"></id>
8
<result column="tname" property="tname"></result>
9
</collection>
10
</resultMap>
11
12
13
<select id="findStudentAndTeacherBySid" resultMap="stuAndTea">
14
select s.sid,sname ,age,gender ,t.tid,tname from stu s ,teacher t ,stu_tea st
15
where s.sid = st.sid and t.tid = st.tid and s.sid =#{sid}
16
</select>
延迟加载
MyBatis中的延迟加载,也称为懒加载(lazy load),是指在进行关联查询时,按照设置延迟加载规则推迟对关联对象的select查询。延迟加载可以有效的减少数据库压力。延迟加载机制是为了避免一些无谓的性能开销而提出来的。 所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作。 延迟加载,可以简单理解为,只有在使用的时候,才会发出sql语句进行查询。
需要注意的是,MyBatis的延迟加载只是对关联对象的查询有延迟设置,对于主加载对象是直接执行查询语句的。
MyBatis根据对关联对象查询的select语句的执行时机,分为3种类型: 1.直接加载 2.侵入式延迟加载 3.深度延迟加载 注意:延迟加载的应用要求: 关联对象的查询与主加载对象的查询必须是分别进行的select语句,不能是使用多表连接所进行的select查询。 因为,多表连接查询,实质是对一张表的查询,对由多个表连接后形成的一张表的查询。会一次性将多张表的所有信息查询出来。
resultMap可以实现高级映射(使用association、collection实现一对一及一对多映射),association、collection具备延迟加载功能。
需求:如果查询订单并且关联查询用户信息。如果先查询订单信息即可满足要求,当我们需要查询用户信息时再查询用户信息。把对用户信息的按需去查询就是延迟加载。 延迟加载:先从单表查询、需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。
如何配置延迟加载:
MyBatis-config.xml
<!--mybatis的延迟加载的总开关 -->
2
<setting name="lazyLoadingEnabled" value="true"></setting>
1直接加载
<setting name="lazyLoadingEnabled" value="false"></setting>
2 深度延迟加载
只有当代码执行到Student对象详情时,底层才执行了select语句对stu表进行了查询,这已经将查询推迟到了不能在推的时间,故称为深度延迟加载。
<setting name="lazyLoadingEnabled" value="true"></setting>
test
@Test
2
public void findStuBySid1Test(){
3
Student student = studentMapper.findStuBySid1(2);
4
}
当使用到关联查询的对象的时候 才会执行关联查询 才会发出关联查询的sql'语句
test
public void findStuBySid1Test(){
2
Student student = studentMapper.findStuBySid1(2);
3
System.out.println(student.getName());//使用主查询的内容
4
System.out.println(student.getCls().getCname());//访问关联查询的对象的属性
5
}
3 侵入式延迟加载
行对主加载对象的查询时,不会执行对关联对象的查询。但是当要访问主加载对象的详情时(此处的详情 可以只是主加载对象本身的一个属性),就会马上执行关联对象的select查询。
<!--开启侵入式延迟加载-->
2
<setting name="aggressiveLazyLoading" value="true"></setting>
test
@Test
2
public void findStuBySid1Test(){
3
Student student = studentMapper.findStuBySid1(2);
4
System.out.println(student.getName());//使用主查询的内容
5
// System.out.println(student.getCls().getCname());//访问关联查询的对象的属性
6
}
fetchType
查询缓存
为什么要使用查询缓存? 查询缓存的使用,主要是为了提高查询访问速度,将用户对同一数据的重复查询过程简化,不再每次均从数据库中查询获取结果数据,从而提高访问速度
正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的支持 一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空。
- 二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。
- 对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear。
一级缓存
MyBatis一级缓存是基于org.apache.ibatis.cache.impl.PerpetualCache类的HashMap本地缓存,其作用域是SqlSession。在同一个SqlSession中两次执行相同的sql查询语句,第一次执行完毕后,会将查询结果写入缓存中,第二次会从缓存中直接获取数据,而不在查询数据库。 当一个sqlsession结束后,该SqlSession中的依据缓存也就不存在了。MyBatis默认一级缓存是开启的状态,且不能关闭
证明一级缓存存在
@Test
2
public void findStuBySid1Test(){
3
Student student = studentMapper.findStuBySid1(2);
4
System.out.println(student);//使用主查询的内容
5
Student student1 = studentMapper.findStuBySid1(2);
6
System.out.println(student1);
7
8
}
二级缓存
MyBatis查询缓存的作用域是根据映射文件的mapper的namespace划分的,相同的namespace的mapper查询数据存放在同一个缓存区域,不同的namespace下的数据互不干扰。
无论是一级缓存还是二级缓存,都是按照namespace进行分别存放的。
但是一、二及缓存的不同之处在于,sqlSession一旦关闭,则SqlSession中的数据将不存在,即一级缓存就不复存在。而二级缓存的生命周期会与整个应用同步,与SqlSession是否关闭无关。
二级缓存的用法
(1) 实体序列化--实现Serializable序列化接口
二级缓存的常用设置 <cache
eviction="FIFO" //回收策略为先进先出
flushInterval="60000" //自动刷新时间60s size="512" //最多缓存512个引用对象 readOnly="true"/> //只读 eviction:回收策略。当二级缓存中的对象达到最大值时,就需要通过回收策略将缓存中的对象移除缓存,默认为LRU,常用的策略有: FIFO:first in first out 先进先出 LRU:Least recently Userd 未被使用时间最长的 补充说明
- 映射语句文件中的所有select语句将会被缓存。
- 映射语句文件中的所有insert,update和delete语句会刷新缓存。
- 缓存会使用Least Recently Used(LRU,最近最少使用的)算法来收回。
- 缓存会根据指定的时间间隔来刷新。
- 缓存会存储1024个对象
使用ehcache来作为二级缓存
引入依赖
<!--引入ehcache缓存-->
2
<dependency>
3
<groupId>org.ehcache</groupId>
4
<artifactId>ehcache</artifactId>
5
<version>3.5.2</version>
6
</dependency>
7
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
8
<!--mybatis整合ehcache-->
9
<dependency>
10
<groupId>org.mybatis.caches</groupId>
11
<artifactId>mybatis-ehcache</artifactId>
12
<version>1.1.0</version>
13
</dependency>
添加ehcache的配置
<ehcache>
2
3
4
<!--
5
磁盘存储:将缓存中暂时不使用的对象,转移到硬盘,类似于Windows系统的虚拟内存
6
path:指定在硬盘上存储对象的路径
7
-->
8
<diskStore path="java.io.tmpdir" />
9
10
11
<!--
12
defaultCache:默认的缓存配置信息,如果不加特殊说明,则所有对象按照此配置项处理
13
maxElementsInMemory:设置了缓存的上限,最多存储多少个记录对象
14
eternal:代表对象是否永不过期
15
timeToIdleSeconds:最大的空闲时间
16
timeToLiveSeconds:最大的存活时间
17
overflowToDisk:是否允许对象被写入到磁盘
18
-->
19
<defaultCache maxElementsInMemory="10000" eternal="false"
20
timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" />
21
22
<!--
23
cache:为指定名称的对象进行缓存的特殊配置
24
name:指定对象的完整名
25
-->
26
<cache name="org.lanqiao.pojo.Student" maxElementsInMemory="10000" eternal="false"
27
timeToIdleSeconds="300" timeToLiveSeconds="600" overflowToDisk="true" />
28
29
30
</ehcache>
切换默认的二级缓存的实现
<!--开启二级缓存-->
2
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
二级缓存的使用原则
01. 很少被修改的数据
2
02. 不是很重要的数据,允许出现偶尔并发的数据
3
03. 不会被并发访问的数据
4
04.多个namespace不能操作同一张表
5
由于二级缓存中的数据是基于namespace的,即不同的namespace中若均存在对同一个表的操作,那么这多个namespace中的数据可能就会出现不一致的现象。
6
05.不能在关联关系表上执行增删改操作
获取新增数据的自动增长的主键列
1
<insert id="addStu" parameterType="Student" keyProperty="id" keyColumn="sid" useGeneratedKeys="true">
2
insert into stu(sname,age,gender,province,tuition) values ('${name}',${age},'${gender}','${province}',${tuition})
3
</insert>
Student stu = new Student("小张",23,"男","山西",4500);
2
studentMapper.addStu(stu);
3
System.out.println(stu.getId());
MyBatis Generator
引入依赖
<dependency>
2
<groupId>org.mybatis.generator</groupId>
3
<artifactId>mybatis-generator-core</artifactId>
4
<version>1.3.7</version>
5
</dependency>
Generator配置文件
1
<?xml version="1.0" encoding="UTF-8" ?>
2
<!DOCTYPE generatorConfiguration PUBLIC
3
"-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
4
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
5
<generatorConfiguration>
6
7
<classPathEntry location="E:\maven\repo\mysql\mysql-connector-java\5.1.46\mysql-connector-java-5.1.46.jar"/>
8
9
<context id="context" targetRuntime="MyBatis3Simple">
10
<commentGenerator>
11
<property name="suppressAllComments" value="false"/>
12
<property name="suppressDate" value="true"/>
13
</commentGenerator>
14
15
<jdbcConnection userId="root" password="root" driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/lanqiao?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC"/>
16
17
<javaTypeResolver>
18
<property name="forceBigDecimals" value="false"/>
19
</javaTypeResolver>
20
21
<javaModelGenerator targetPackage="org.lanqiao.domain" targetProject="Mybatis02/main/java">
22
<property name="enableSubPackages" value="false"/>
23
<property name="trimStrings" value="true"/>
24
</javaModelGenerator>
25
26
<sqlMapGenerator targetPackage="org.lanqiao.dao.mapper" targetProject="Mybatis02/main/java">
27
<property name="enableSubPackages" value="false"/>
28
</sqlMapGenerator>
29
30
<javaClientGenerator targetPackage="org.lanqiao.dao" type="XMLMAPPER" targetProject="Mybatis02/main/java">
31
<property name="enableSubPackages" value="false"/>
32
</javaClientGenerator>
33
34
<table schema="general" tableName="stu" enableCountByExample="false" enableDeleteByExample="false"
35
enableSelectByExample="false" enableUpdateByExample="false"/>
36
</context>
37
</generatorConfiguration>
1
<?xml version="1.0" encoding="UTF-8" ?>
2
<!DOCTYPE generatorConfiguration PUBLIC
3
"-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
4
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
5
<generatorConfiguration>
6
7
<classPathEntry location="E:\maven\repo\mysql\mysql-connector-java\5.1.46\mysql-connector-java-5.1.46.jar"/>
8
9
<context id="context" targetRuntime="MyBatis3Simple">
10
<commentGenerator>
11
<property name="suppressAllComments" value="false"/>
12
<property name="suppressDate" value="true"/>
13
</commentGenerator>
14
15
<jdbcConnection userId="root" password="root" driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/lanqiao?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC"/>
16
17
<javaTypeResolver>
18
<property name="forceBigDecimals" value="false"/>
19
</javaTypeResolver>
20
21
<javaModelGenerator targetPackage="org.lanqiao.domain" targetProject=".">
22
<property name="enableSubPackages" value="false"/>
23
<property name="trimStrings" value="true"/>
24
</javaModelGenerator>
25
26
<sqlMapGenerator targetPackage="org.lanqiao.dao.mapper" targetProject=".">
27
<property name="enableSubPackages" value="false"/>
28
</sqlMapGenerator>
29
30
<javaClientGenerator targetPackage="org.lanqiao.dao" type="XMLMAPPER" targetProject=".">
31
<property name="enableSubPackages" value="false"/>
32
</javaClientGenerator>
33
34
<table schema="general" tableName="stu" enableCountByExample="false" enableDeleteByExample="false"
35
enableSelectByExample="false" enableUpdateByExample="false"/>
36
</context>
37
</generatorConfiguration>