mybatis 学习笔记

MyBatis

概念:

框架是一个半自动映射的框架。这里所谓的“半自动”是相对于 Hibernate 框架全表映射而言的,MyBatis 框架需要手动匹配提供 POJO、SQL 和映射关系,而 Hibernate 框架只需提供 POJO 和映射关系即可。

比较:

相比Hibernate ,使用 MyBatis 框架手动编写 SQL 要比使用 Hibernate 框架的工作量大,

但 MyBatis 框架可以配置动态 SQL 并优化 SQL、通过配置决定 SQL 的映射规则,以及支持存储过程等。对于一些复杂的和需要优化性能的项目来说,使用 MyBatis框架更加合适。
就目前的情形来说,SSM比SSH更加合适
java目前就业形势。(截止至笔记时刻)
学会啦mybatis hibernate就非常的简单和容易理解啦

mybatis基于JDBC并整合了其功能。
所以可以说完全是替代JDBC的上位应用。
传统JDBC使用内部DriverManager的getConnection() 实现与数据库接通的功能。
通过PreparedStatement pstmt = connection.prepareStatement(sql);放入sql语句,并还需要使用executeQuery()来运行并接到list类型的结果。

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/learn_mybatis? characterEncoding=utf-8";
String userName = "root";
String password = "mysql";
Connection connection = DriverManager.getConnection(url, userName, password);
String sql = "select * from user where id = ?";
PreparedStatement pstmt = connection.prepareStatement(sql); pstmt.setString(1, "1");
ResultSet resultSet = pstmt.executeQuery();
List<User> userList = new ArrayList<User> (10);
while (resultSet.next())
{ int id = resultSet.getInt("id");
String name = resultSet.getString("username");
User user = new User();
user.setId(id);
user.setUsername(name);
userList.add(user);
}
System.out.println(userList); }
1
2


conclusion:
1。JDBC内的使用方法会照成硬编码,如果需要改动sql语句和预留statement会需要同时改动结果集。
2.JDBC每次执行都需要调用一次connection来创建链接,多次执行sql会造成资源浪费的情况。

mybatis 配置文件 config.xml:
注释读懂就够了,平时就用这么多配置而已。

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
<?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>

<!--加载外部的properties文件-->
<properties resource="jdbc.properties"></properties>

<!--给实体类的全限定类名给别名-->
<typeAliases>
<!--批量起别名:该包下所有的类的本身的类名:别名还不区分大小写,这里不使用起别名,只是使用package指定其搜索Javabean(不完全等于pojo)的包路径-->
<package name="com.zhengyao.mybatis.pojo"/>
</typeAliases>

<!--environments:运行环境-->
<environments default="development">
<environment id="development">
<!--当前事务交由JDBC进行管理-->
<transactionManager type="JDBC"></transactionManager>
<!--当前使用mybatis提供的连接池-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>

<!--引入映射配置文件-->
<mappers>
<mapper resource="UserMapper.xml"></mapper>
<mapper resource="OrderMapper.xml"></mapper>
</mappers>

</configuration>

这里的jdbc.properties 是额外编写的,指定好写下:

1
2
3
4
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/learn_mybatis?useUnicode=true&characterEncoding=utf-8
jdbc.username=root
jdbc.password=lansheng1

配置如上,一般Driver要和数据库版本与maven包的依赖配置版本一致或者兼容。
mappers映射集合内写上各个映射器文件的所在地,在resource内可以直接使用名字调用。

提及一下:一般情况下会有单纯POJO(plain old java object)类来预留与数据库表字段对应的对象与对应pojo映射sql方法的DAO(数据访问对象)接口类

重要部分

映射器文件内部写的是mybatis里的最重要的部分,sql语句的标签和组件。就是会执行的sql语句储存的地方。
使用#{field}可以传入pojo类内对应的field值。
由于mybatis拥有 where、trim foreach等普通编写无法简单做到的方便标签语句,还有
resultMap、association、collection等可以接受多个返回实体的组件来映射到pojo对应类和属性中。

resultMap

是最强大的反射结果集合组件,可以对应多个反射实体,内部可以放置result、association、collection等组件来体现结果集的类型与关系。
实例: 结果集合是一个双实体、某实体结果为集合的集合时:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<select id="userToOrders" resultMap="userToOrdersMap">
<!--根据用户id获取用户信息-->
select * from user where id=#{id}
</select>

<resultMap id="userToOrdersMap" type="com.zhengyao.mybatis.pojo.User">
<result column="id" property="id"></result>
<!--构建User的Order多实体查询、实体 orderlist,对比列:id,链接语句:selectOrdersByUser-->
<collection property="orderList" column="id" select="com.zhengyao.mybatis.dao.IOrderDao.selectOrdersByUser">
</collection>

</resultMap>
<--另一个OrderMappers内:-->
<select id="selectOrdersByUser" resultType="com.zhengyao.mybatis.pojo.Order">
select * from orders where uid=#{id}
</select>

resultType 只能传入单一的实体类型, 使用result在resultMap内可以直接提出单一的结果内的字段值使用(property)保留/定义至结果集
而其他类型会在比较双列的同时把比较列(column)只保留一列,即原字段会被剔除。

其他内容我觉得导师总结的比我自己领悟的还多,这里就cp了。

Mybatis学习笔记

JDBC问题分析

传统JDBC代码;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void main(String[] args) throws ClassNotFoundException, SQLException {

Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/learn_mybatis?characterEncoding=utf-8";
String userName = "root";
String password = "mysql";
Connection connection = DriverManager.getConnection(url, userName, password);
String sql = "select * from user where id = ?";
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setString(1, "1");
ResultSet resultSet = pstmt.executeQuery();
List<User> userList = new ArrayList<User> (10);
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("username");
User user = new User();
user.setId(id);
user.setUsername(name);
userList.add(user);
}
System.out.println(userList);
}

使用JDBC存在的问题分析:

  • 数据库连接创建,释放频繁造成系统资源浪费,影响系统性能,连接数据库存在重复代码,导致重复编码
  • sql语句在代码中存在硬编码,导致代码不易维护,实际应用中sql变化的可能性较大,sql改动需要改动java代码
  • 使用PreparedStatement也存在硬编码的问题,导致代码不易维护
  • 对结果集的解析也存在硬编码的问题,sql变化也会导致解析代码变化,系统不容易维护,如果每次返回成pojo对象解析比较方便

JDBC问题解决方案

解决方案:

  • 使用连接池进行连接数据库,并把连接数据库的硬编码配置写入配置文件
  • sql语句也使用配置文件进行配置
  • 使用反射或者内省自动对结果集进行返回

自定义框架设计

使用端:

  • 提供核心配置sqlMapConfig.xml文件,配置数据源信息以及引入sql配置文件
  • 提供所有的Sql配置文件 mapper.xml文件

框架端:

image-20200426112403919

Mybatis框架的基础介绍

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

Mybatis官网

configuration(配置)

properties属性

mybatis的配置文件可以引入外部的properties文件来进行赋值

1
2
3
4
5
6
7
<properties resource = ""></properties>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>

类型别名(typeAliases)

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:

1
2
3
4
5
6
7
8
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
<typeAlias alias="Comment" type="domain.blog.Comment"/>
<typeAlias alias="Post" type="domain.blog.Post"/>
<typeAlias alias="Section" type="domain.blog.Section"/>
<typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>

当这样配置时,Blog 可以用在任何使用 domain.blog.Blog 的地方。

也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:

1
2
3
<typeAliases>
<package name="domain.blog"/>
</typeAliases>

每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。见下面的例子:

1
2
3
4
@Alias("author")
public class Author {
...
}

而Mybatis也默认内置了一些别名,用于用户便利操作

别名 映射的类型
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
object Object
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator

环境配置(environments)

image-20200426142603077

其中事务管理器分两种:

  • JDBC:使用JDBC的提交和回滚设置,依赖于从数据源得到的连接来管理事务作用域

  • MANGAGED:这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为

    如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。

数据源类型:

  • UNPOOLED: 这个数据源的实现会每次请求时打开和关闭连接
  • POOLED:这个数据源的实现会每次请求时打开和关闭连接
  • JNDI:这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用

映射器(mappers)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>

这些配置会告诉 MyBatis 去哪里找映射文件

动态Sql

动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

常用的动态sql有如下几种:

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach
    `

    if 类型的动态sql

1
2
3
4
5
6
7
8
9
10
11
<select id="findByCondition" parameterType="user" resultType="user">
select * from User
<where>
<if test="id!=0">
and id=#{id}
</if>
<if test="username!=null">
and username=#{username}
</if>
</where>
</select>

choose、when、otherwise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>

trim、where、set

使用where标签可以动态的舍去第一个and

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:

1
2
3
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>

foreach

对集合进行遍历

其中item就是循环的每个参数,index就是索引值,collection就是集合的参数,open就是传入循环开始的参数,close就是循环结束的参数,separator就是使用对应标识符来切割

1
2
3
4
5
6
7
8
9
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>

抽取通用sql

1
2
3
4
5
6
7
8
9
10
11
12
抽取通用的sql出来
<sql id="Base_Column_List">
id, corp_id, org_id, bill_code, borrow_begin_date, borrow_end_date,
reason, department, borrower, contact, apply_date, applyer_id,
status, return_date, comments, create_time,busi_type,borrow_type,k2_id,k2_form_url,k2_status, ts
</sql>
使用include进行引用通用的sql
<select id="queryRecBorrowList" resultMap="BaseResultMap" parameterType="hashmap">
select
<include refid="Base_Column_List" />
from rec_borrow
</select>

作业

完成订单对用户的一对一查询、用户对订单的多对一查询

1、用户与订单是一对多的关系,订单与用户是一对一的关系.通过订单查询出关联的用户

2、用户与订单是一对多的关系,订单与用户是一对一的关系.通过订单查询出关联的用户

声明一个用户订单实体

1
2
3
4
5
6
7
8
public class Order {

private Integer id;
private String orderTime;
private Double total;
// 表明该订单属于哪个用户
private User user;
}

一个用户实体

1
2
3
4
5
6
public class User implements Serializable {
private Integer id;
private String username;
//表示用户关联的订单
private List<Order> orderList = new ArrayList<>();
}

resultMap是Mybatis最强大的元素,它可以将查询到的复杂数据(比如查询到几个表中数据)映射到一个结果集当中。

resultMap包含的元素:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!--column不做限制,可以为任意表的字段,而property须为type 定义的pojo属性-->
<resultMap id="唯一的标识" type="映射的pojo对象">
<id column="表的主键字段,或者可以为查询语句中的别名字段" jdbcType="字段类型" property="映射pojo对象的主键属性" />
<result column="表的一个字段(可以为任意表的一个字段)" jdbcType="字段类型" property="映射到pojo对象的一个属性(须为type定义的pojo对象中的一个属性)"/>
<association property="pojo的一个对象属性" javaType="pojo关联的pojo对象">
<id column="关联pojo对象对应表的主键字段" jdbcType="字段类型" property="关联pojo对象的主席属性"/>
<result column="任意表的字段" jdbcType="字段类型" property="关联pojo对象的属性"/>
</association>
<!-- 集合中的property须为oftype定义的pojo对象的属性-->
<collection property="pojo的集合属性" ofType="集合中的pojo对象">
<id column="集合中pojo对象对应的表的主键字段" jdbcType="字段类型" property="集合中pojo对象的主键属性" />
<result column="可以为任意表的字段" jdbcType="字段类型" property="集合中的pojo对象的属性" />
</collection>
</resultMap>

如果collection标签是使用嵌套查询,格式如下:

1
2
<collection column="传递给嵌套查询语句的字段参数" property="pojo对象中集合属性" ofType="集合属性中的pojo对象" select="嵌套的查询语句" > 
</collection>

注意:标签中的column:要传递给select查询语句的参数,如果传递多个参数,格式为column= ” {参数名1=表字段1,参数名2=表字段2} ;

以下以实例介绍resultMap的用法:

一、简单需求:一个商品的结果映射;

1、创建商品pojo对象:

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
public class TShopSku  {
/**
* 主键ID
*/
private Long id;

/**
* 商品名
*/
private String skuName;

/**
* 分类ID
*/
private Long categoryId;


/**
* 主键ID
* @return ID
*/
public Long getId() {
return id;
}

/**
* 主键ID,
* @param id
*/
public void setId(Long id) {
this.id = id;
}

/**
* 商品名
* @return SKU_NAME 商品名
*/
public String getSkuName() {
return skuName;
}

/**
* 商品名
* @param skuName 商品名
*/
public void setSkuName(String skuName) {
this.skuName = skuName == null ? null : skuName.trim();
}

/**
* 分类ID
* @return CATEGORY_ID 分类ID
*/
public Long getCategoryId() {
return categoryId;
}

/**
* 分类ID
* @param categoryId 分类ID
*/
public void setCategoryId(Long categoryId) {
this.categoryId = categoryId;
}

对应的resultMap:

1
2
3
4
5
<resultMap id="BaseResultMap" type="com.meikai.shop.entity.TShopSku">
<id column="ID" jdbcType="BIGINT" property="id" />
<result column="SKU_NAME" jdbcType="VARCHAR" property="skuName" />
<result column="CATEGORY_ID" jdbcType="BIGINT" property="categoryId" />
</resultMap>

二、商品pojo类添加属性集合:

一个商品会有一些属性,现在需要将查询出的商品属性添加到商品对象中,首先需要在原商品pojo类的基础上中添加属性的集合:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 属性集合
*/
private List<TShopAttribute> attributes;

/**
* 获得属性集合
*/
public List<TShopAttribute> getAttributes() {
return attributes;
}

/**
* 设置属性集合
* @param attributes
*/
public void setAttributes(List<TShopAttribute> attributes) {
this.attributes = attributes;
}

将Collection标签添加到resultMap中,这里有两种方式:

1、嵌套结果:

对应的resultMap:

1
2
3
4
5
6
7
8
9
<resultMap id="BasePlusResultMap" type="com.meikai.shop.entity.TShopSku">
<id column="ID" jdbcType="BIGINT" property="id" />
<result column="SKU_NAME" jdbcType="VARCHAR" property="skuName" />
<result column="CATEGORY_ID" jdbcType="BIGINT" property="categoryId" />
<collection property="attributes" ofType="com.meikai.shop.entity.TShopAttribute" >
<id column="AttributeID" jdbcType="BIGINT" property="id" />
<result column="attribute_NAME" jdbcType="VARCHAR" property="attributeName" />
</collection>
</resultMap>

查询语句:

1
2
3
4
5
<select id="getById"  resultMap="basePlusResultMap">
select s.ID,s.SKU_NAME,s.CATEGORY_ID,a.ID,a.ATTRIBUTE_NAME
from t_shop_sku s,t_shop_attribute a
where s.ID =a.SKU_ID and s.ID = #{id,jdbcType =BIGINT};
</select>

2、关联的嵌套查询(在collection中添加select属性):

商品结果集映射resultMap:

1
2
3
4
5
6
7
<resultMap id="BasePlusResultMap" type="com.meikai.shop.entity.TShopSku">
<id column="ID" jdbcType="BIGINT" property="id" />
<result column="SKU_NAME" jdbcType="VARCHAR" property="skuName" />
<result column="CATEGORY_ID" jdbcType="BIGINT" property="categoryId" />
<collection column="{skuId=ID}" property="attributes" ofType="com.meikai.shop.entity.TShopAttribute" select="getAttribute" >
</collection>
</resultMap>

复制代码
collection的select会执行下面的查询属性语句:

1
2
3
4
5
<select id="getAttribute"  resultMap="AttributeResultMap">
select a.ID,s.ATTRIBUTE_NAME
from t_shop_attribute a
where a.ID = #{skuId,jdbcType =BIGINT};
</select>

属性结果集映射:

1
2
3
4
<resultMap id="AttributeResultMap" type="com.meikai.shop.entity.TShopAttribute">
<id column="ID" jdbcType="BIGINT" property="id" />
<result column="ATTRIBUTE_NAME" jdbcType="VARCHAR" property="attributeName" />
</resultMap>

BasePlusResultMap包含了属性查询语句的Collection

所以通过下面的查询商品语句就可获得商品以及其包含的属性集合:

<select id="getById"  resultMap="BasePlusResultMap">
    select s.ID,s.SKU_NAME,s.CATEGORY_ID
    from t_shop_sku s
    where  s.ID = #{id,jdbcType =BIGINT};
</select>

评论