高级映射
在学习延迟加载之前,先来学习什么是高级映射吧;
高级映射其实就是数据库表与表之间的关系,那么具体的关系又可以分为三种(拿学生表和班级表做说明):
- 一对一关系;如学生之于班级
- 一对多关系;如班级至于学生
- 多对多关系;这个比如一个用户可以购买多个商品,而一个商品可以被多个用户购买,这样的用户表和商品表之间就是多对多关系;
一对一关联映射
使用到的两个表如下:
先创建两个bean类:
public class Student {
private int Sid;
private String Sname;
private int Cid;
//get、set和toString方法略
}
public class Classes {
private int Cid;
private String Cname;
//get、set和toString方法略
}
两个对应的dao层接口:
public interface StudentDao {
public StudentVo getById1(int id);
}
public interface ClassesDao {
public Classes getById1(int id);
}
方式一:resultType
这个方法需要创建一个新类,因为返回的是一个新的对象,要拿一个新的类让mybatis完成映射:
public class StudentVo {
private int Sid;
private String Sname;
private int Cid;
private String Cname;
//get、set和toString方法略
}
我是直接创建了一个新的类StudentVo,让后把对应的属性全部添加在里面,也可以通过继承Student、Classes两个类中的一个,再把缺的属性添加在这个新类里面就好,我这个例子里面属性比较少,就直接全写在里面了;
需要使用的StudentDao.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.tulun7.dao.StudentDao">
<select id="getById1" parameterType="java.lang.Integer" resultType="com.tulun7.bean.StudentVo">
select
student1.Sid,
student1.Sname,
classes.Cid,
classes.Cname
from
student1,classes
where
student1.Cid = classes.Cid
and Student1.Sid = #{id}
</select>
</mapper>
mybatis的全局配置文件mybatis.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>
<settings>
<!-- 打印查询语句 -->
<setting name="logImpl" value="LOG4J"/>
</settings>
<!--数据源配置-->
<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/mybatis?serverTimezone=GMT&useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!--映射-->
<mappers>
<mapper resource="延时加载/StudentDao.xml"/>
</mappers>
</configuration>
测试:
public class TestDemo1 {
@Test
public void test() throws IOException {
String resource = "延时加载/mybatis.xml";
//读取配置文件
InputStream asStream = Resources.getResourceAsStream(resource);
//创建sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(asStream);
//创建sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//通过动态代理产生StudentMapper对象
StudentDao mapper = sqlSession.getMapper(StudentDao.class);
StudentVo byId1 = mapper.getById1(1901);
System.out.println(byId1);
}
}
方式二:resultMap
这种实现方法,需要把Classes当做Student类中的一个属性,即需要改变Student类(我直接写了一个新的类Student1):
public class Student1 {
private int Sid;
private String Sname;
private int Cid;
private Classes classes; //把关联的这儿当属性
//get、set和toString方法略
}
在StudentDao接口里面添加一个方法:
public interface StudentDao {
public StudentVo getById1(int id);
public Student1 getById2(int id); //添加新方法
}
那他的StudentDao1.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.tulun7.dao.StudentDao">
<!-- 定义一个resultMap, 学生 > 班级 ,将查询的结果映射Student1-->
<resultMap id="student1Map" type="com.tulun7.bean.Student1">
<!-- 数据库表Student1对应 pojo中的Student1.java -->
<!-- id:唯一标识
result:普通标识
column:数据库的字段
property:对应bean类的属性-->
<id column="Sid" property="Sid"/>
<result column="Sname" property="Sname"/>
<result column="Cid" property="Cid"/>
<!-- 配置关联用户信息 -->
<!-- association:用于映射关联查询单个对象的信息
property:要将关联查询的班级信息映射到Student1中哪个属性
javaType:映射到哪个类-->
<association property="classes" javaType="com.tulun7.bean.Classes">
<id column="Cid" property="Cid"/>
<result column="Cname" property="Cname"/>
</association>
</resultMap>
<select id="getById2" resultMap="student1Map">
select
student1.Sid,
student1.Sname,
classes.Cid,
classes.Cname
from
student1,classes
where
student1.Cid = classes.Cid
and Student1.Sid = #{id}
</select>
</mapper>
在mybatis的全局配置文件mybatis.xml里面添加映射文件StudentDao1.xml(略),测试:
public class TestDemo2 {
@Test
public void test() throws IOException {
String resource = "延时加载/mybatis.xml";
//读取配置文件
InputStream asStream = Resources.getResourceAsStream(resource);
//创建sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(asStream);
//创建sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//通过动态代理产生StudentMapper对象
StudentDao mapper = sqlSession.getMapper(StudentDao.class);
Student1 byId2 = mapper.getById2(1902);
System.out.println(byId2);
}
}
一对一的两种实现就完了,总结:
- resultType:使用resultType实现较为简单,如果pojo中没有包括查询出来的列名,需要增加列名对应的属性,即可完成映射。如果没有查询结果的特殊要求建议使用resultType。
- resultMap:需要单独定义resultMap,实现有点麻烦,如果对查询结果有特殊的要求,使用resultMap可以完成将关联查询映射pojo的属性中。
resultMap可以实现延迟加载,resultType无法实现延迟加载。
一对多关联映射
方式一:resultType
这个和上面一对一关联映射的resultType实现差不多,也需要一个新类,因为返回的是一个新对象,需要注意的是一对多的时候一个班级对应多个学生,所以返回的是集合,重要的点我截图如下,其他的我就略了:
方式二:resultMap
重点来说通过resultMap如何实现吧,因为一对多的关联映射的resultMap实现和一对一的resultMap实现不一样:
他需要在班级类里面添加学生的集合属性(创建了一个新的类Classes1):
public class Classes1 {
private int Cid;
private String Cname;
private List<Student> list;
//get、set和toString方法略
}
在ClassesDao接口里面添加方法:
public interface ClassesDao {
public List<ClassesVo> getById1(int id);
public Classes1 getById2(int id); //添加方法
}
然后主要看的就是对应的ClassesDao1.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.tulun7.dao.ClassesDao">
<resultMap id="classesMap" type="com.tulun7.bean.Classes1">
<id column="Cid" property="Cid"/>
<result column="Cname" property="Cname"/>
<!-- 一对多采用collection集合映射
collection: 将关联查询到的多条记录映射到对象集合中
property: 对应类的属性
ofType:指定映射的类 -->
<collection property="list" ofType="com.tulun7.bean.Student">
<id column="Sid" property="Sid"/>
<result column="Sname" property="Sname"/>
<result column="Cid" property="Cid"/>
</collection>
</resultMap>
<select id="getById2" resultMap="classesMap">
select
classes.Cid,
classes.Cname,
student1.Sid,
student1.Sname,
student1.Cid
from
student1,classes
where
student1.Cid = classes.Cid
and classes.Cid = #{id}
</select>
</mapper>
改一下全局配置文件mybati.xml的映射文件的位置,测试:
public class TestDemo4 {
@Test
public void test() throws IOException {
String resource = "延时加载/mybatis3.xml";
//读取配置文件
InputStream asStream = Resources.getResourceAsStream(resource);
//创建sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(asStream);
//创建sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//通过动态代理产生StudentMapper对象
ClassesDao mapper = sqlSession.getMapper(ClassesDao.class);
Classes1 byId2 = mapper.getById2(1);
System.out.println(byId2);
}
}
多对多关联映射
这个给篇别人的博文的地址吧→https://blog.csdn.net/hefenglian/article/details/80699723
延迟加载
延迟加载或者也叫惰性加载,懒加载。使用延迟加载可以提高程序的运行效率。Java程序与数据库交互的频次越低,程序运行效率越高,所以我们应该尽量减少Java程序与数据库的交互次数,MyBatis延迟加载就很好的做到了这一点。
通过一个具体的业务场景来理解延迟加载:
班级(Classes)和学生(Student),当我们查询Student对象时,因为有级联关系,所以会将对应的Classes对象一并查询出来,这样就需要发送两条SQL语句,分别查询classes表和student表中的数据。
延迟加载的思路是:当我们查询Student的时候,如果没有调用classes属性,则只发送了一条SQL语句查询Student;如果需要调用classes属性,则发送两条SQL语句查询Student和Classes。所以延迟加载可以看做是一种优化机制,根据具体的代码,自动选择发送的SQL语句条数。
如果不使用延迟加载:
先创建两个bean类:
public class Student1 {
private int Sid;
private String Sname;
private int Cid;
private Classes classes; //把关联的这儿当属性
//get、set和toString方法略
}
public class Classes {
private int Cid;
private String Cname;
//get、set和toString方法略
}
两个对应的dao层接口:
public interface StudentDao {
public Student1 getById2(int id);
}
public interface ClassesDao {
public Classes getById3(int id);
}
当我们不使用延迟加载的时候,那么对应的ClassesDao2.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.tulun7.dao.ClassesDao">
<select id="getById3" parameterType="java.lang.Integer" resultType="com.tulun7.bean.Classes">
select * from classes where classes.Cid = #{id}
</select>
</mapper>
对应的StudentDao2.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.tulun7.dao.StudentDao">
<resultMap id="student1Map" type="com.tulun7.bean.Student1">
<id column="Sid" property="Sid"/>
<result column="Sname" property="Sname"/>
<result column="Cid" property="Cid"/>
<!--需要使用select属性 后面跟对应接口的全路径名加对应的方法名-->
<association column="Cid" property="classes" javaType="com.tulun7.bean.Classes"
select="com.tulun7.dao.ClassesDao.getById3">
</association>
</resultMap>
<select id="getById2" resultMap="student1Map">
select * from student1 where Student1.Sid = #{id}
</select>
</mapper>
全局配置文件如下mybatis4.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>
<settings>
<!-- 打印查询语句 -->
<setting name="logImpl" value="LOG4J"></setting>
</settings>
<!--数据源配置-->
<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/mybatis?serverTimezone=GMT&useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!--映射-->
<mappers>
<mapper resource="延时加载/ClassesDao2.xml"/>
<mapper resource="延时加载/StudentDao2.xml"/>
</mappers>
</configuration>
测试代码:
public class TestDemo5 {
@Test
public void test() throws IOException {
String resource = "延时加载/mybatis4.xml";
//读取配置文件
InputStream asStream = Resources.getResourceAsStream(resource);
//创建sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(asStream);
//创建sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//通过动态代理产生StudentMapper对象
StudentDao mapper = sqlSession.getMapper(StudentDao.class);
Student1 byId2 = mapper.getById2(1901);
System.out.println(byId2.getSname());
}
}
结果如图所示,我查询了一个学生表并且只需要一个学生姓名即可,而实际却有两条sql,分别查询了学生表、班级表,当前我在查询的时候,查询是学生表,可以看作我并不需要班级表的信息,而它这样的加载方式也叫积极加载,即把关联的表信息也查询一次,不管你需不需要,这样就会存在一定的效率问题,所以我们引进了延迟加载这种加载方式,也即按需加载;
如果使用延迟加载:
在上面的全局配置文件里面打开延迟加载开关、开启按需加载即可:
<?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>
<settings>
<!-- 打印查询语句 -->
<setting name="logImpl" value="LOG4J"></setting>
<!--================================================-->
<!-- 打开延迟加载的开关 -->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 将即时加载改为按需加载 他默认的是积极加载-->
<setting name="aggressiveLazyLoading" value="false"/>
<!--================================================-->
</settings>
<!--数据源配置-->
<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/mybatis?serverTimezone=GMT&useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!--映射-->
<mappers>
<mapper resource="延时加载/ClassesDao2.xml"/>
<mapper resource="延时加载/StudentDao2.xml"/>
</mappers>
</configuration>
再次测试(测试代码同上):
它就只有一条sql语句了,而当我们需要查询班级信息时,它会按需加载:
public class TestDemo7 {
@Test
public void test() throws IOException {
String resource = "延时加载/mybatis5.xml";
//读取配置文件
InputStream asStream = Resources.getResourceAsStream(resource);
//创建sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(asStream);
//创建sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//通过动态代理产生StudentMapper对象
StudentDao mapper = sqlSession.getMapper(StudentDao.class);
Student1 byId2 = mapper.getById2(1901);
System.out.println(byId2.getClasses());
}
}