您的位置:首页 > 数据库

【MyBatis】(三)MyBatis的SQL操作(MyBatis简单特性关联映射查询,缓存机制,MyBatis和Hibernate缓存对比)

2018-09-19 22:48 543 查看

MyBatis很重要,话不多,直接开始!!

八、MyBatis关联映射查询

关联查询是MyBatis一个很特殊的特性,MyBatis既可以使用SQL语句进行单表多表联查也可以做级联查询,而且效果比Hibernate显著的多,主要是因为在配置文件所标记实体类和数据表之间的关系非常明确.所以非常便于做关联映射查询,这样的缺点就是无法做级联增删改,不过我们的SQL语句完全可以独立完成这些,所以我们主要来说一下关联类型的级联查询即可.

    两张表的表关系无非有这么三种:

    一对一:(one to one)单纯的一对一关系映射关系有这四种:单向主键映射,单向外键映射,双向主键映射,双向外键映射,这里我们用的最多的,牵扯最少的,后期最便于维护的还是双向外键映射,那今天就以这个为例

    一对多(多对一):(one to many),可以用一方主键关联多方外键.

    多对多:(many to many),多对多这里涉及一个思想:所有的关联属于主外键联合关联,在下面我会详细解释这个问题的.

1.一对一(one to one)

关联类型 主键关联 外键关联
单向 单向主键关联 单向外键关联
双向 双向主键关联 双向外键关联

表设计:双向外键关联(两张表都需要添加外键)

案例:一个球队一个经理,一个经理执教一个球队

1>创建库表

[code]#经理表()

CREATE TABLE manager(

mid int primary key,

mname varchar(20),

t_id int # 外键,代表经理执教一个球队

);

#球队表()

CREATE TABLE team(

tid int primary key,

tname varchar(20),

location varchar(20),

t_id int #外键,代表经理执教的球队

);

#插入数据

INSERT INTO manager values (1,'bobo',1),(2,'ker',2),(3,'suoluo',3),(4,'labo',3);

INSERT INTO team VALUES (1,'yongshi','圣安东尼奥',1),(2,'qishi','金州',2),(3,'maci','克利夫兰',3),(4,'maci',''克利夫兰,3);

#本人已经离开球坛四五余年,对于那支球队那位明星一点都不了解,希望不要见怪#

2>实体类设计

在双方实体类中持有表示双方的实体类属性;

[code]manager:

--private int mid;

--private String mname;

// 在一方实体类中表示对方(一方)实体类的属性

--private TeamMapping team;

team:

--private int tid;

--private String tname;

--private String location;

// 在一方实体类中表示对方(一方)实体类的属性

--private ManagerMapping manager;

// 注意:这里省略了 空参构造方法,全参构造方法,getter and setter方法以及toString方法:注意在生成toString方法时去掉对方属性方法,否则在调用toString方法是会出现死循环

    注意:在生成toString方法时去掉对方属性方法,否则在调用toString方法是会出现死循环 

3>SQl映射文件

    3.1>借助resultMap标签做关联映射

        --property:当前实体类全限定名

        --id:当前resultMap映射结果id

    3.2>在resultMap标签中借助association标签来表示对方实体类属性名

        --property:当前实体类中表示对方实体类属性名

        --JavaType:对方实体类全限定名

    3.3>在select查询标签中使用

        resultType="映射结果id"来作为SQL语句的返回值类型(SQL语句当前表关系对方表外键)

[code]<!-- ManagerMapper.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
"http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">
<!-- SQL动态查询 -->
<mapper namespace="com.hekaikai666.dao.i.ManagerMapper">
<!-- 一对一的级联映射 -->
<!-- type:映射实体类全限定名 id:当前映射结果id -->
<resultMap type="com.hekaikai666.bean.Manager" id="managerBean">
<id property="mid" column="mid"/>
<result property="mname" column="mname"/>
<!-- 表示对方(一方)的实体类属性和表字段映射关系 -->
<!-- type:当前实体类中用于描述对方的实体类属性名 javaType:对方实体类全限定名 -->
<association property="team" javaType="com.hekaikai666.bean.Team">
<id property="tid" column="tid"/>
<result property="tname" column="tname"/>
<result property="location" column="location"/>
</association>
</resultMap>
<!-- 根据id查找一个Manager对象,以及他所关联的team对象 -->
<select id="findManagerById" parameterType="int" resultMap="managerBean">
SELECT manager.*,team.* FROM manager,team WHERE manager.mid = team.t_id AND manager.mid=#{mid}
</select>
</mapper>
[code]<!-- TeamMapper.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
"http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">
<mapper namespace="com.hekaikai666.dao.i.TeamMapper">
<resultMap type="com.hekaikai666.bean.Team" id="teamBean">
<id property="tid" column="tid" />
<result property="tname" column="tname" />
<result property="location" column="location" />
<association property="manager" javaType="com.hekaikai666.bean.Manager">
<id property="mid" column="mid" />
<result property="mname" column="mname" />
</association>
</resultMap>
<select id="findTeamById" parameterType="int" resultMap="teamBean">
SELECT manager.*,team.* FROM manager,team WHERE team.tid =
manager.t_id AND team.tid=#{tid}
</select>
</mapper>

4>编写映射接口

[code]/**
* 映射对象的接口
* @author hekaikai666
* @time 2018年9月18日下午5:14:34
**/
public interface TeamMapper {
public Team findTeamById(int id);
}
public interface ManagerMapper {
public Manager findManagerById(int id);
}

5>编写测试类测试接口

[code]import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.hekaikai666.bean.Emp;
import com.hekaikai666.bean.Manager;
import com.hekaikai666.bean.Team;
import com.hekaikai666.bean.User;
import com.hekaikai666.bean.User1;
import com.hekaikai666.dao.i.EmpMapper;
import com.hekaikai666.dao.i.ManagerMapper;
import com.hekaikai666.util.MyBatisUtil;

/**
*
* @author hekaikai666
* @time 2018年9月17日上午11:42:16
**/
public class TestManager {
SqlSessionFactory factory;
SqlSession session;

@Before
public void init() {
factory = MyBatisUtil.getFactory();
session = factory.openSession();
}

@After
public void destroy() {
session.commit();
session.close();
}

/**
* 关联映射查询管理员,获取管理员对象查询队伍
*/
@Test
public void test1() {
ManagerMapper mapper = session.getMapper(ManagerMapper.class);
Manager manager = mapper.findManagerById(2);
System.out.println(manager);
// 获取manager所关联的team对象
Team team = manager.getTeam();
System.out.println(team);
}
}

 

2.一对多,多对一(one to many,many to one)

表设计:在多方表建立外键来关联一方表主键

案例:一个用户有多张卡,多张卡对应一个用户

1>创建库表

[code]#创建库表

create table people(

pid int primary key,

username varchar(20),

password varchar(20),

address varchar(100)

);

#卡表

create table card(

cid int primary key,

cno varchar(16),

remark varchar(20),

p_id int #外键 当前卡属于哪个外键

);

#插入数据

insert into people values(1,'张三','123456','雁塔区');

insert into people values(2,'李四','123456','碑林区');

insert into people values(3,'王五','123456','高新区');

insert into card values(1,'42001','工商银行',1);

insert into card values(2,'42002','招商银行',1);

insert into card values(3,'42003','建设银行',2);

insert into card values(4,'42004','人民银行',2);

2>实体类设计

在一方实体类定义表示多方实体类的属性

[code]People:

--private int pid;

--private String username;

--private String address;

  // 当前用户拥有多张卡

--private List<Card> cards;

在多方实体类定义表示一方实体类的属性

Card:

--private int cid;

--private String cno;

--private String remark;

  // 表示多方实体类中的一方属性

--private People people;

// 注意:这里省略了 空参构造方法,全参构造方法,getter and setter方法以及toString方法:注意在生成toString方法时去掉对方属性方法,否则在调用toString方法是会出现死循环

3>映射文件

一方:在resultMap标签中定义collection

--property:当前实体类中定义表示多个对方的集合属性

--ofType 多方实体类全限定名

多方:在resultMap标签中定义association

--property:当前实体类中表示对方实体类属性名

--JavaType:对方实体类全限定名

 

[code]<!-- PeopleMapper.xml配置 -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
"http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">

<mapper namespace="com.hekaikai666.dao.i.PeopleMapper">
<!-- 开启当前mapper下的二级缓存 -->
<cache/>
<!-- 开启二级缓存可选属性配置
eviction:缓存回收策略 → LRU:最近最少使用(默认);FIFO:先进先出;WEAK:弱引用;SOFT:软引用
flushInterval:缓存刷新时间 → 每隔多久刷新一次,不指定时执行查询时刷新缓存,单位是毫秒
readOnly:只读 → 默认为true
size:指定缓存中可以缓存多少个数据对象,超出这个指定数据,就可以指定回收策略来对数据进行回收,不指定默认是1024个 →
-->
<cache eviction="" flushInterval="" readOnly="" size=""></cache>
<!-- 借助resultMap表示一对多的级联映射关系 -->
<resultMap type="com.hekaikai666.bean.People" id="peopleBean">
<id property="pid" column="pid" />
<result property="username" column="username"/>
<result property="address" column="address"/>
<!-- 表示多方实体类属性和表字段之间的映射关系 -->
<!-- property:当前实体类中用于表示多方集合的属性名
ofType:多方实体类全限定名
-->
<collection property="cards" ofType="com.hekaikai666.bean.Card">
<id property="cid" column="cid"/>
<result property="cno" column="cno" />
<result property="remark" column="remark" />
</collection>
</resultMap>
<!-- 根据id查找people对象,以及他所关联的cards集合 -->
<select id="findPeopleById" parameterType="int" resultMap="peopleBean">SELECT people.*,card.* FROM people,card WHERE people.pid=card.p_id AND people.pid=#{pid}</select>
</mapper>
[code]<!-- CardMapper.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
"http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">

<mapper namespace="com.hekaikai666.dao.i.CardMapper">
<!-- 借助resultMap表示多对一的级联映射关系 -->
<!-- 表示多方实体类属性和表字段之间的映射关系 -->
<!-- property:当前实体类中用于表示多方集合的属性名 ofType:多方实体类全限定名 -->
<resultMap type="com.hekaikai666.bean.Card" id="cardBean">
<id property="cid" column="cid" />
<result property="cno" column="cno" />
<result property="remark" column="remark" />

<!-- 表示一方实体类属性和表字段之间的映射关系 -->
<!-- property:当前实体类中用于表示对方集合的属性名 javaType:多方实体类全限定名 -->
<association property="people" javaType="com.hekaikai666.bean.People">
<id property="pid" column="pid" />
<result property="username" column="username" />
<result property="address" column="address" />
</association>
</resultMap>
<!-- 根据id查找card对象,以及他所关联的people集合 -->
<select id="findCardById" parameterType="int" resultMap="cardBean"><!-- SELECT
people.*,card.* FROM people,card WHERE people.pid=card.p_id AND
people.pid=#{pid} -->
SELECT people.*,card.* FROM people inner join card on people.pid=card.p_id WHERE card.cid=#{cid}
</select>
</mapper>

4>编写映射接口

[code]/**
* 映射器接口类
* @author hekaikai666
* @time 2018年9月19日上午10:33:50
**/
public interface PeopleMapper {
public People findPeopleById(int id);
}
public interface CardMapper {
public Card findCardById(int id);
}

5>编写测试类

[code]/**
*
* @author hekaikai666
* @time 2018年9月19日上午10:34:52
**/
public class TestPAC {
SqlSessionFactory factory;
SqlSession session;

@Before
public void init() {
factory = MyBatisUtil.getFactory();
session = factory.openSession();
}

@After
public void destroy() {
session.commit();
session.close();
}

/**
* 一对多的级联查询
*/
@Test
public void test1() {
// 通过session获取映射接口对象
PeopleMapper mapper = session.getMapper(PeopleMapper.class);
// 使用映射接口对象调用映射接口方法
People people = mapper.findPeopleById(2);
System.out.println(people);
// 使用获取到的对象调用级联查询映射的方法
List<Card> cards = people.getCards();
System.out.println(cards);
}

/**
* 一对多的级联查询
*/
@Test
public void test2() {
// 通过session获取映射接口对象
CardMapper mapper = session.getMapper(CardMapper.class);
// 使用映射接口对象调用映射接口方法
Card card = mapper.findCardById(2);
System.out.println(card);
// 使用获取到的对象调用级联查询映射的方法
People people = card.getPeople();
System.out.println(people);
}
}

 

3.多对多 many to many

表设计:双都是一对多,在双方表上都设置外键.

案例:一个老师对应多个学生,一个学生对应多个老师

思想:因为我们所建立的多对多映射表不含有第三张关系表(Hibernate中使用中间关系表),那我们应该怎样设立他们那的关系呢.第一张表的主键和第二张表的外键关联起来,这样就有一个一对多,同时,把第二张表的主键关联到第一张表的外键上,name两张表就有了多对多关联关系.第一假设有一个老师带数学,他名下有两个学生,在老师的表字段中加入一个学生标签,这样只能放一个,(1,2,3,...)这种不符合三大范式中的第一范式,所以这样是行不通的,但是我们的多对多级联映射查询时查询操作,对于表中的数据无法做任何操作,所以我们选用在学生的表的外键标记老师,如果需要修改老师,我们的操作是把之前的关系断开,再去连接新的关系,这样实行的是数据库的修改操作.与查询无关,所以我们可以采用这种方式 对数据库进行级联查询.

1>创建库表

[code]#老师表

create table teacher(

tid int primary key,

tname varchar(20),

xueke varchar(10),

s_id int #外键,该老师属于那个学生

);

#学生表

create table student(

sid int primary key,

sname varchar(20),

sex char(1),

t_id int #外键,该学生属于那个老师.

);

#插入数据

insert into teacher values (1,'姜老师','语文',null);

insert into teacher values (2,'仓老师','数学',null);

insert into teacher values (3,'马老师','英语',null);

insert into teacher values (4,'波老师','物理',null);

insert into student values (1,'杨雪峰','男',null);

insert into student values (2,'尚万成','男',null);

insert into student values (3,'王鑫','男',null);

insert into student values (4,'孙传昊','男',null);

2>实体类设计

在双方实体类都有定义用于表示多个对方实体类集合属性.

[code]Teacher

private int tid;

private String tname;

private String xueke;

// 表示当前Teacher关联多个Student

private List<Student> students;

Student

private int sid;

private String sname;

private String sex;

// 表示当前Student关联多个Teacher

private List<Teacher> teachers;

3>SQL映射文件

在resultMap标签中均定义Collection标签来表示对方(多方)的实体类和表字段之间的映射关系

--property:当前实体类中定义表示多个对方的属性集合

--ofType多方实体类全限定名

[code]<!-- StudentMapper.xml配置文件 -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
"http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">

<mapper namespace="com.hekaikai666.dao.i.StudentMapper">
<!-- 借助resultMap表示多对多的级联映射 -->
<resultMap type="com.hekaikai666.bean.Student" id="studentBean">
<id property="sid" column="sid" />
<result property="sname" column="sname" />
<result property="sex" column="sex" />
<!-- 定义多方(对方)实体类属性和字段之间的映射关系 -->
<collection property="teachers" ofType="com.hekaikai666.bean.Teacher">
<id property="tid" column="tid" />
<result property="tname" column="tname" />
<result property="xueke" column="xueke" />
</collection>
</resultMap>
<!-- 根据ID查找当前Teacher对象以及它所关联的Student对象 -->
<select id="findStudentById" parameterType="int" resultMap="studentBean">select
teacher.*,student.* from teacher,student where
student.sid=teacher.s_id and student.sid=#{sid}
</select>
</mapper>
[code]<!-- TeacherMapper.xml配置 -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
"http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">

<mapper namespace="com.hekaikai666.dao.i.TeacherMapper">
<!-- 借助resultMap表示多对多的级联映射 -->
<resultMap type="com.hekaikai666.bean.Teacher" id="teacherBean">
<id property="tid" column="tid" />
<result property="tname" column="tname" />
<result property="xueke" column="xueke" />
<!-- 定义多方(对方)实体类属性和字段之间的映射关系 -->
<collection property="students" ofType="com.hekaikai666.bean.Student">
<id property="sid" column="sid" />
<result property="sname" column="sname" />
<result property="sex" column="sex" />
</collection>
</resultMap>
<!-- 根据ID查找当前Teacher对象以及它所关联的Student对象 -->
<select id="findTeacherById" parameterType="int" resultMap="teacherBean">select
teacher.*,student.* from teacher,student where
teacher.tid=student.t_id and teacher.tid=#{tid}</select>
</mapper>

4>映射接口

[code]/**
* 映射接口对象
* @author hekaikai666
* @time 2018年9月19日下午3:26:51
**/
public interface TeacherMapper {
public Teacher findTeacherById(int id);
}
public interface StudentMapper {
public Student findStudentById(int id);
}

5>编写实体类测试接口

[code]public class TestPAC {
SqlSessionFactory factory;
SqlSession session;

@Before
public void init() {
factory = MyBatisUtil.getFactory();
session = factory.openSession();
}

@After
public void destroy() {
session.commit();
session.close();
}

/**
* 多对多的级联查询
*/
@Test
public void test3() {
// 通过session获取映射接口的对象
TeacherMapper mapper = session.getMapper(TeacherMapper.class);
// 使用接口的对象调用接口的方法
Teacher teacher = mapper.findTeacherById(4);
System.out.println(teacher);
// 使用获取到的对象调用吉利啊查询映射方法
List<Student> students = teacher.getStudents();
// 遍历获取到的数据
for (Student student : students) {
System.out.println("student:" + student);
}
}

/**
* 多对多的级联查询
*/
@Test
public void test4() {
// 通过session获取映射接口的对象
StudentMapper mapper = session.getMapper(StudentMapper.class);
// 使用接口的对象调用接口的方法
Student student = mapper.findStudentById(3);
System.out.println(student);
// 使用获取到的对象调用吉利啊查询映射方法
List<Teacher> teachers = student.getTeachers();
// 遍历获取到的数据
for (Teacher teacher : teachers) {
System.out.println("teacher:" + teacher);
}
}
}

九、缓存(MyBatis和Hibernate缓存对比)

1.一级缓存

一级缓存指的是SQLSession缓存,在每一个SQLSession被创建出来的时候,内部都会有一个HashMap区域,也就是一级缓存区域,HashMap的Key是SQL语句,value值式SQL语句的查询结果,每次执行查询时,都会拿SQL语句去一级缓存HashMap中执行查询,如果查到了数据直接返回,没有查询就去数据库执行查询,将查询到的结果以key-value键值对的形式存储在HashMap中,那么第二次就可以直接来一级缓存中取出数据,从而提高查询性能,一级缓存在MyBatis中时默认开启的,只有当session执行了flush()或者close()之后,一级缓存才会被清空.

测试:

[code]/**

* 测试一级缓存 相同的SqlSession执行了相同的sql语句

*/

@Test

public void testFirstLevelCache() {

PeopleMapper mapper = session.getMapper(PeopleMapper.class);

People people1 = mapper.findPeopleById(1);

session.close();

session = factory.openSession();

PeopleMapper mapper2 = session.getMapper(PeopleMapper.class);

People people2 = mapper2.findPeopleById(1);

System.out.println(people1);

System.out.println(people2);

2.二级缓存

二级缓存是SqlSessionFactory级别缓存,也是Mapper级别缓存,即同一个mapper下多个SqlSession可以共享二级缓存.二级缓存中也有一个HashMap区域,在开启二级缓存之后,第一次调用Sql语句,会去数据库执行查询,将查询到的数据以key-value键值对存储在对应的二级缓存区域,第二次如果还是同一个mapper下的SqlSession对象发送了相同的SQL语句,就回去二级缓存中执行查询取出数据.二级缓存总开关在MyBatis中也是默认开启的,如果哪一个mapper需要开启二级缓存,需要去对应的mapper映射文件中进行开启

使用步骤:

1>主配置文件中开启二级缓存总开关

主配置

[code]<!-- 全局系统设置 -->

<settings>

<!-- 开启二级缓存..延迟加载之类的. -->

<!-- 打开二级总开关:默认是true开启的 -->

<setting name="cacheEnabled" value="true"/>

</settings>

2>在需要开启二级缓存的mapper中对二级缓存进行开启

mapper.xml配置

[code]<!-- 开启当前mapper下的二级缓存 -->

<cache/>

<!-- 开启二级缓存可选属性配置

eviction:缓存回收策略 → LRU:最近最少使用(默认);FIFO:先进先出;WEAK:弱引用;SOFT:软引用

flushInterval:缓存刷新时间 → 每隔多久刷新一次,不指定时执行查询时刷新缓存,单位是毫秒

readOnly:只读 → 默认为true

size:指定缓存中可以缓存多少个数据对象,超出这个指定数据,就可以指定回收策略来对数据进行回收,不指定默认是1024个 →

-->

<cache eviction="" flushInterval="" readOnly="" size=""></cache>

测试:

[code]    /**
* 测试二级缓存
* 同一个Mapper下不同SQLsession所共享的不同的SQLsession对同一个mapper下的SQL语句执行查询会共享二级缓存
*/
@Test
public void testSecondLevelCache() {
SqlSession session1 = factory.openSession();
PeopleMapper mapper1 = session1.getMapper(PeopleMapper.class);
People people1 = mapper1.findPeopleById(1);
System.out.println(people1);
session1.close();

SqlSession session2 = factory.openSession();
PeopleMapper mapper2 = session2.getMapper(PeopleMapper.class);
People people2 = mapper2.findPeopleById(1);
System.out.println(people2);
session2.close();
}

阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐