MyBatis框架

MyBatis框架封面

MyBatis框架,基础库框架

[TOC]

参考网站
mybatis中文文档
mybatis源码中文注释

MyBatis框架

1 MyBatis框架引言

1-1 MyBatis框架的基本概念

MyBatis是一个持久层框架,完成对数据库的访问(CRUD)

1-2 MyBatis框架与JDBC对比

jdbc访问和操作数据库存在的问题:

  • 大量的代码冗余
  • 手工处理ORM(麻烦)
  • 没有对数据库进行优化(Cache)

解决JDBC访问和操作数据库存在 的问题,是对原有JDBC技术的封装。

1-3 MyBatis框架的核心编程思路

MyBatis的核心开发思路.png

在原来的JDBC三层模型汇总,先进行书写DAO接口后实现DAO实现类,MyBatis的解决思路是使用mapper xml类型文件代替DAO实现类的功能。

2 第一个MyBatis程序的开发(HelloWorld)

2-1 环境搭建(OpenSource)

i.Ibatis是MyBatis的前身,apache组织
ii.IBatis从3.0开始改名为MyBatis google code
iii.MyBatis 属于GitHub

2-1-1 导入jar包

  • MyBatis的核心jar包
  • 第三方依赖jar
  • 相关数据库的驱动jar

2-1-2 引入配置文件

2-1-2-1 log4j.properties[可选]

log4日志文件放置在src目录下

2-1-2-2 MyBatis-config.xml

建议放置在src目录下
配置的是MyBatis运行的相关参数

2-1-2-3 Mapper文件

实现DAO接口
位置建议xxxDao接口放在同一目录下

2-1-3 初始化配置

MyBatis-config.xml配置

oracle数据库配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "mybatis-3-config.dtd" >
<configuration>
<environments default="oracle">
<environment id="oracle">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="oracle.jdbc.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
<property name="username" value="hr" />
<property name="password" value="hr" />
</dataSource>
</environment>
</environments>

<mappers>
<mapper resource="com/baizhi/dao/UserMapper.xml" />
</mappers>

</configuration>

< environments default=”oracle”>默认环境
< environment id=”oracle”>环境的唯一标识
< transactionManager type=”JDBC”>采用JDBC的事务控制
< dataSource type=”POOLED”>MyBatis原生的连接池
< mapper resource=”com/baizhi/dao/UserMapper.xml” />注册Mapper文件

2-1-3-1 补充MyBatis-config中的配置信息

别名TypeAlias

实体的权限定名

1
2
3
<typeAlias>
<typeAlias type="com.hibiscidai.entity.User" alias="A" />
</typeAlias>

1
2
3
<select id="selectByNameAndPassword" resultType="A">
select * from t_user where username=#{username} and password=#{passowrd}
</select>

配置内容参数化
问题:在mybatis-config文件中有经常需要修改的字符串相关(数据库相关)
解决方案:把在mybatis-config配置文件中经常需要修改的字符串信息提取到小配置文件中

i.读取小配置文件

1
2
3
<!-- 读取小配置文件 -->
<properties resource="jdbc.properties">
</properties>

resource=”jdbc.properties”——小配置文件存放路径

ii.${key}获取小配置文件中对应的值

1
2
3
4
5
6
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/
<property name="password" value="${password}"/>
</dataSource>
1
2
3
4
driver = oracle.jdbc.OracleDriver
url = jdbc:oracle:thin:@localhost:1521:xe
username = hr
password = hr

2-2 MyBatis API

三个重要的类

2-2-1 Resources

读取mybaitis-fonfig配置文件,打开IO

2-2-2 SqlSessionFactory

通过SqlSessionFactory这个工厂创建SqlSession对象

2-2-3 SqlSession

作用:

  • 根据程序员书写的Mapper文件,自动创建mapper文件对应的java类(DAO接口的实现类)
  • SqlSession内部封装了Connection对象
  • SqlSession控制事务

SqlSession.commit——Connection.commit
SqlSession.rollback——Connection.rollback

注意:实战开发中,增删改需要控制事务 查询不需要控制事务

2-3 MyBatis 的开发步骤

  • 建表
  • 写实体
  • DAO接口
  • Mapper文件做DAO的实现
1
public User selectByID(Integer id);
1
2
3
<select id="selectByID" parameterType="int" resultType="com.hibiscidai.entity.User">
select * from t_user where id=#{id}
</select>

UserresultType=”com.hibiscidai.entity.User”
selectByIDid=”selectByID”
Integer idparameterType=”int”

  • Mapper文件的注册
1
2
3
<mappers>
<mapper resource = "com/hibiscidai/dao/UserMapper.xml" />
</mappers>
  • MyBatis核心API测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
public void testMybatis() throws Exception {
// 读取mybatis-config配置文件
InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
// 创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(stream);
// 创建SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 创建DAO的实现类
UserDAO userDAO = sqlSession.getMapper(UserDAO.class);
// 调用DAO中相关方法
User user = userDAO.selectByID(1);
System.out.println(user);
}

3 参数绑定

3-1 Mybaits代码中形参名与mapper文件中的#{}的名字没有必然联系

1
public User selectByID(Integer id);
1
2
3
<select id="selectByID" parameterType="int" resultType="com.hibiscidai.entity.User">
select * from t_user where id=#{hibiscidai}
</select>

Integer idhibiscidai

3-2 Mapper文件中可以通过下标的形式,代表接口中的形参

1
public User selectByID(Integer id);
1
2
3
<select id="selectByID" parameterType="int" resultType="com.hibiscidai.entity.User">
select * from t_user where id=#{0}
</select>

Integer id0

下标默认从0开始

3-3 多参数形式的查询解决方案

注意:在mybatis进行查询时,paramterType是可以省略的,不能随意指定#{},名字

3-3-1 可以通过下标的形式进行操作

1
public User selectByNameAndPassword(String username, String password);
1
2
3
<select id="selectByNameAndPassword" resultTpe="com.baizhi.entity.User">
select * from t_user where username=#{0} and password=#{1}
</select>

[email protected]注解进行参数绑定[建议]

注:

1
<![CDATA[]]>

mybaits中消除xml大小写

1
public User selectByNameAndPassword(@Param("u")String username, @Param("p") String password);
1
2
3
<select id="selectByNameAndPassword" resultType="com.hibiscidai.entuty.User">
select * from t_user where username=#{u} and password=#{p}
</select>

3-3-3 使用集合(map)进行传参[早期]

1
2
3
4
Map<String, String> map = new HashMap<String, String>();
map.put("username", "hibiscidai");
map.put("passowrd", "123456");
User user = userDAO.selectByNameAndPassword(map);
1
select * from t_user where username=#{username} and password=#{passowrd}
1
public User selectByNameAndPassword(Map<String, String> params);

4 MyBatis中CUD(增删改)

注意:在MyBatis应用的过程中,处理CUD时,必须要手工控制事务,否则操作不执行

1
2
sqlSession.commit();
sqlSession.rollback();

4-1 删除

1
public void delete(Integer id);
1
2
3
<delete id="delete">
delete from t_user where id=#{id}
</delete>

4-2 修改

1
public void update(User user);
1
2
3
4
5
User {
id
username
password
}
1
2
3
<update id="update">
update t_user set username=#{username}, password=#{password} where id=#{id}
</update>

4-3 插入

Sequence序列进行主键插入[Oracle]

1
public void insert(User user);
1
2
3
4
5
User {
id = null
username = hibiscidai
passowrd = 123456
}
1
2
3
4
5
6
<insert id="insert">
<selectKey keyProperty="id" order="BEFORE" resultType="int">
select z.newxval from dual
</selectKey>
inset int t_user (id, username, password) values(#{id}, #{usename}, #{password})
</insert>

案例
serviceimpl层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public User regist(User user) {
SqlSession session = MyBatisUtil.openSqlSession();
UserDAO ud = (UserDAO) session.getMapper(UserDAO.class);
User u = null;
try {
ud.insertSelective(user);
u = ud.selectByEmail(user.getdEmail());
session.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
session.close();
}
return u;
}

注意从session获取Mapper执行后注意commit

5 MyBatisUtil工具类的封装

对于MyBatis三种类对象的分析

Resources类

特点:读取MyBatis-config配置文件 IO

建议:一次性通过IO读取所有的数据

SqlSessionFactory类

特点:创建SqlSession 重量级的资源 内存占用多 功能多

建议:每一个应用只创建一个 线程安全

SqlSession类

特点:清零及资源 不能被多线程共享

  1. 创建DAO接口的实现类(动态代理设计思想 动态字节码技术)
  2. SqlSession内部封装Connnection 一个SqlSession对应一个Connection
  3. 事务控制
1
```SqlSession.rollback
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class mybatisutils {
public static SqlSessionFactory sqlSessionFactory;
public static ThreadLocal<SqlSession> tl = new ThreadLocal<SqlSession>();

/* 静态代码块加载基础信息 */
static {
InputStream stream = null;
try {
// 读取mybatis-config配置文件
stream = Resources.getResourceAsStream("mybatis-config.xml");
// 创建SqlSessionFactory对象
sqlSessionFactory = new SqlSessionFactoryBuilder().build(stream);
} catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
throw new RuntimeException("读取配置文件失败");
} finally { // 关闭流
try {
stream.close();
} catch (Exception e2) {
// TODO: handle exception
e2.printStackTrace();
}
}
}

/* 获取SqlSession对象 */
public static SqlSession opSqlSession() {
SqlSession sqlSession = tl.get();

// 非空判断
if (null == sqlSession) {
sqlSession = sqlSessionFactory.openSession();
tl.set(sqlSession);
}

return sqlSession;
}

/* 关闭SqlSession对象 */
public static void closeSqlSession() {
// 获取SqlSession
SqlSession sqlSession = opSqlSession();
sqlSession.close();
tl.remove();
}

/* 事务提交 */
public static void commit() {
SqlSession sqlSession = opSqlSession();
sqlSession.commit();
closeSqlSession();
}

/* 事务回滚 */
public static void rollback() {
SqlSession sqlSession = opSqlSession();
sqlSession.rollback();
closeSqlSession();
}

/* 获取DAO实现 */
public static Object getMapper(Class clazz) {
SqlSession sqlSession = opSqlSession();
return sqlSession.getMapper(clazz);
}

}

6 ResultMap(结果映射,结果对应关系)

作用:解决了实体类与数据库中的表不匹配

6-1 定义ResultMap

1
2
3
4
5
<resultMap type="Customer" id="CustomerResultMap">
<id property="id" column="t_id" />
<result property="username" column="t_username" />
<result property="password" column="t_password" />
</resultMap>

< id >标签表示对主键的处理
< result >标签表示对费主键的处理

property – 代表实体的名字
column – 代表数据库中的列名

6-2 使用ResultMap

1
2
3
4
5
6
7
8
<resultMap type="Customer" id="CustomerResultMap">
<id property="id" column="t_id" />
<result property="username" column="t_username" />
<result property="password" column="t_password" />
</resultMap>
<select id="selectByID" resultType="CustomerResultMap" >
select * from t_customer where t_id=#{id}
</select>

注意:如果在开发中,只解决列名不一致的问题,有简化的方式,起别名

1
2
3
4
5
6
<typeAliases>
<typeAlias type="com.baizhi.entity.Customer" alias="Customer"/>
</typeAliases>
<select id="selectByID" resultType="Customer" >
select t_id as id,t_username as username,t_password as password from t_customer where t_id=#{id}
</select>

7 MyBatis框架进行多表操作

7-1 实体之间的关系分类

i.实体之间有关联关系(有 从属 has a)【重点】

a) 1 : 1
b) 1 :
c)
: *
ii.实体之间继承关系(is a)

7-2 MyBatis中如何处理管理关系

7-2-1 entity处理

关系属性:把关系另一方作为本方属性进行存储

  • 关系的方向性:
  • 单向关系:只能从关系其中一方 找到关系的另一方
  • 双向关系:从关系的双方都可以找到关系的另一方

级联查询:

1
2
PersonDAO.query(id) ---> Person(passport)
PassportDAO.query(id) ---> Passport(person)

看似是单表处理 实际完成多张表的操作

7-2-2 表

表中如何简历关联关系:外键

  • 建外键规则:
  • 一对一 随便建
  • 一对多 建在多的一方
  • 多对多 建在第三张表中

表中关系的方向行:天生的双向关系

注意

  1. 父没有外键 先插入
  2. 子表没有外键 先删除

###7-2-3 DAO接口

有几个DAO, 对应写几个DAO

###7-2-4 Mapper文件处理

自定义结果映射

#8 关联关系的实战开发

##8-1 1:1关联关系

1
2
3
4
5
6
7
8
9
10
<resultMap type="Person" id="PersonResultMap">
<id column="pid" property="id"/>
<result column="pusername" property="username"/>
<result column="page" property="age"/>
<association property="passport" javaType="Passport">
<id column="sid" property="id"/>
<result column="sserial" property="serial"/>
<result column="splace" property="place"/>
</association>
</resultMap>

##8-2 1:*关联关系(重点)

例:
部门(Department)与职员(Employee)关系

1
2
3
4
5
6
Employee
id
username
age
salary
department
1
2
3
4
5
Department
id
departmentname
departmentnumber
List<Employee> employees

t_employee
| id | usename | age | salary | did |
| :–: | :–: | :–: | :–: | :–: |
| 1 | hibiscidai | 18 | 10000| 1 |
| 1 | laowang| 18 | 100| 1 |

t_department
| id | epartmentname | epartmentnumber |
| :–: | :–: | :–: |
| 1 | 研发部 | 1111 |

一对多关联关系,集合作为关系属性,需要对集合进行初始化操作。

##8-3 : 关联关系(了解)

例:
学生(Student)与课程(Course)关系

1
2
3
4
5
6
Student
id
studentnumber
studentname
age
List<Course>

1
2
3
4
5
Course
id
coursename
score
List<Student>

t_student | 学生表

id studentnumber studentname age
1 1111 hibiscidai 18
1 2222 hibiscizhang 28
1 3333 hibisciwang 38

t_sc | 多对多映射表

id sid cid
1 1 1
2 1 1
3 2 1
4 2 2

t_course | 课程表

id coursename score
1 java 3
2 oracle 2

8-4 关联关系总结

8-4-1 Entity 关系属性

a)一个对象
b)集合 进行初始化

8-4-2 Table外键

一对一 随便建
一对多 建在多的一方
多对多 建在第三张表中

8-4-3 Mapper文件

ResultMap 一个对象

1
<association property="" javaType="">

多个对象

1
<collection property="" oftype="">

9 动态SQL

作用:简化mapper文件的配置

9-1 Sql片段

1
2
3
4
5
6
7
8
9
<sql id="selectAll">
select * from t_teacher
</sql>
<select id="selectByID" resutlType="Teacher">
<include refid="selectAll" />where id=#{id}
</select>
<select id="selectByName" resultType="Teacher">
<include refid="selectAll" />where teachername=#{name}
</select>

9-2 where语句的动态Sql

作用:同样的表,根据不同列的内容查询,通过where的动态sql

TeacherDAO文件重构
未使用where

1
2
3
4
5
6
7
8
//根据教师的ID查询该教师
public Teacher selectByID(Integer id);
//根据教师的姓名查询该教师
public Teacher selectByName(String name);
//根据教师的序列号查询
public Teacher selectByNumber(String number);
//根据教师的id及姓名查询
public Teacher selectByIDAndName(@Param("id")Integer ID,@Param("name")String name);

使用where后

1
public Teacher selectByWhere(Integer id, String name, String number)

Mapper文件重构

未使用where

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<sql id="selectAll">
select * from t_teacher
</sql>
<select id="selectByID" resutlType="Teacher">
<include refid="selectAll" />where id=#{id}
</select>
<select id="selectByName" resultType="Teacher">
<include refid="selectAll" />where teachername=#{name}
</select>
<select id="selectByNumber" resultType="Teacher">
<include refid="selectAll" />where teachernumber=#{number}
</select>
<select id="selectByIDAndName" resultType="Teacher">
<include refid="selectAll" />where id=#{id} and teachername=#{name}
</select>

使用where后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<sql id="selectAll">
select * from t_teacher
</sql>
<select id="selectByWhere" resultType="Teacher">
<include refid="selectAll" />

<where>
<if test="id!=null">
id=#{id}
</if>
<if test="name!=null">
and name=#{name}
</if>
<if test="number!=null">
and number=#{number}
</if>
select * from t_teacher where id=#{id}
select * from t_teacher where name=#{name}
select * from t_teacher where number=#{number}
select * from t_teacher where id=#{id} and name=#{name}
</where>
</select>

xml逻辑映射:

1
2
3
4
name=null, number=null	where id=#{id}
id=null, number=null where name=#{name}
id=null, name=null where number=#{number}
number=null where id=#{id} and number=#{number}
1
2
3
4
5
6
7
8
9
10
11
12
13
<select id="selectByDynamic" resultType="Teacher">
<include refid="selectAll" />
<where>
<if test="id!=null">
id=#{id}
</if>
<if test="teachername!=null">
and teachername=#{teachername}
</if>
<if test="teachernumber!=null">
and teachernumber=#{teahcernumber}
</if>
</where>

注意:用where动态sql时候,多参数做参数绑定时,要与数据库中的列名保持一致。
如果起始条件为null则忽略and号

新的写法:

1
2
3
4
5
6
7
8
9
10
11
<trim prefix="where" prefixOverrides="and/or">
<if test="id!=null">
id=#{id}
</if>
<if test="teachername!=null">
and teachername=#{teachername}
</if>
<if test="teachernumber!=null">
and teachernumber=#{teachernumber}
</if>
</trim>

prefix 忽略前缀

prefixOverrides 忽略前缀and或者or

9-3 修改操作的动态Sql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<update id="updateTeacher">
update t_teacher
<set>
<if test="teachername!=null">
teachername=#{teachername},
</if>
<if test="teachernumber!=null">
teachernumber=#{teachernumber},
</if>
<if test="age!=null">
age=#{age}
</if>
</set>
where id=#{id}
</update>

新的写法

1
2
3
4
5
6
7
8
9
10
11
12
<trim prefix="set" suffixOverrides=",">
<if test="teachername!=null">
teachername=#{teachername},
</if>
<if test="teachernumber!=null">
teachernumber#={teachernumber},
</if>
<if test="age!=null">
age=#{age}
</if>
</trim>
where id=#{id}

9-4 批量操作(批量删除,批量查询)

1
2
3
4
5
6
<select id="selectByIDs" resyltType="Teacher">
select * from t_teacher where id in
<foreach collection="array" open="(" items="ids" separator="," close=")">
#{ids}
</froeach>
</select>
1
2
3
select * from t_teacher where id in
<foreach collection="list" open="(" item="ids" separator=","close=")">
</foreach>

如果参数是list集合 collection属性值设置为list

10 MyBatis的缓存(Cache)

10-1 缓存简述

  • 作用:
    在应用程序与数据库之间建立内存缓冲,存储用户曾经查询过的数据

  • 好处:

  1. 减少与数据库之间的频繁通信
  2. 内存中随机读写效率高于硬盘
  • 问题:
  1. 内存造价高,内存物理上线不大 无法缓存海量数据
  2. 数据安全性差
  • 原则:
    缓存中存储的数据一定是经常需要查询的数据,而且不是经常需要修改的数据

10-2 缓存开启步骤

10-2-1 开启MyBatis的全局缓存

Mybatis-config.xml配置

1
2
3
4
<!-- 激活全局缓存 -->
<settings>
<setting name="cacheEnabled" value="true" />
</setting>

10-2-2 设置需要存储在缓存中的数据

1
2
3
4
5
6
<!-- cache标签的作用:本mapper文件中的所有有关查询的操作都会放入mybatis的缓存中 -->
<cache>
</cache>
<select id="selectByID" resultType="Person">
select * from t_person where id=#{id}
</select>

注意实体类起别名

10-2-3 放置在缓存中的实体对象都需要实现serializable接口

1
2
3
4
5
6
7
8
public class Person implements Serializable {
private Integer id;
private String username;
private String password;
private Integer age;
public Integer getId() {}
...
}

序列化目的:唤出策略(用少的数据写入硬盘中)

  • 注意:
  • 只有session关闭时,MyBatis才会把数据存储在缓存中
  • 事务提交时(CUD),MyBatis会自动清空缓存
  • 建议:
  • 查询方法不需要控制,但是一定要关闭SQLsession
  • CUD必须要控制事务
  • 单表查询建议采用MyBatis缓存
  • 第三方缓存:
    eacache,redis,memcache
文章作者: HibisciDai
文章链接: http://hibiscidai.com/2017/11/19/2017-11-19-MyBatis框架/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 HibisciDai
支付宝打赏
微信打赏