Mybatis入门

软件开发中的框架

- 框架是 可被应用开发者定制的应用骨架

- 框架是一种规则,保证开发者遵循相同的方式开发程序

- 框架提倡"不要重复造轮子",对基础功能进行封装

框架的优点

- 极大提高了开发效率

- 统一的编码规则,利于团队管理

- 灵活配置的应用,拥有更好的维护性

SSM开发框架

MyBatis入门

1 什么是MyBatis

- MyBatis是优秀的持久层框架:

  通过DAO类结合Mybatis框架让我们快速完成对数据的增删改查,

  持久就是将内存中的数据保存到数据库中,防止重启后数据丢失.

- MyBatis使用XML将SQL与程序解耦,便于维护

- MyBatis学习简单,执行高效.是JDBC的延申

官网:https://mybatis.org/mybatis-3/zh/index.html

2 MyBatis开发流程

1 2

3 pojo 4

5 6

MyBatis是通过SqlSession对象对数据进行操作的,SqlSession对象由SessionFactory的对象生成。

 3 MyBatis环境配置

 finish

中央仓库在国外有时下载比较慢,为了解决这个问题,可以在pom.xml增加一个镜像仓库配置

 

 jdbc驱动(这里是mysql)

 IDEA内置的数据库客户端

 

有时可能出现下载不成功的情况

解决方法:

1.在Mysql官网找到对应版本的jar包: https://dev.mysql.com/downloads/connector/j/

 官网下载太慢时,可以通过MvnJar(https://www.mvnjar.com/)搜索对应的jar包来下载

2.将下载好的jar包将jar包放在IDEA的配置目录或Maven仓库中(只要能找到就行)

在IDEA的Mysql驱动文件列表中添加下载好的jar包

一切就绪后,点击测试连接,如果显示success表示连接成功,再点击ok就配置好了

创建一个全新的数据库并导入初始化表 (用数据源的方式)

在文件夹上点击鼠标右键

执行成功后

还可以修改表结构

 

 resource目录下要新创建一个xml文件

 

xml声明

dtd声明

注:

 &的转义: &

可以有多个数据源的环境配置

默认用哪个用default设置,值为数据源对应的id的值

environment只可以配置一个数据库,每个配置好的environment可以当做一个数据源,而environments标签用于数据源的配置,可以配置多个数据源。

 1 <?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     <settings>
 7         <!-- goods_id ==> goodsId 驼峰命名转换 -->
 8         <setting name="mapUnderscoreToCamelCase" value="true"/>
 9     </settings>
10 
11     <!--设置默认指向的数据库-->
12     <environments default="dev">
13         <!--配置环境,不同的环境不同的id名字-->
14         <environment id="dev">
15             <!-- 采用JDBC方式对数据库事务进行commit/rollback -->
16             <transactionManager type="JDBC"></transactionManager>
17             <!--采用连接池方式管理数据库连接-->
18             <dataSource type="POOLED">
19                 <property name="driver" value="com.mysql.jdbc.Driver"/>
20                 <property name="url" value="jdbc:mysql://localhost:3306/babytun?useUnicode=true&amp;characterEncoding=UTF-8"/>
21                 <property name="username" value="root"/>
22                 <property name="password" value="root"/>
23             </dataSource>
24         </environment>
25     </environments>
26     <mappers>
27         <!--<mapper class="com.imooc.mybatis.dao.GoodsDAO"/>-->
28         <package name="com.imooc.mybatis.dao"/>
29     </mappers>
30 </configuration>
View Code

4 SqlSessionFactory

SqlSessionFactory

SqlSession

添加单元测试框架,通过单元测试可以了解程序的情况

 该包中新建一个测试类

在方法上写test注解

只有添加了test注解,junit才能对这个类执行

 文本文件

返回reader对象

build通过reader对象解析xml,返回对应的sqlsessionfactory对象

opensession:创建一个sqlsession对象

getConnection:获得底层的数据库连接对象

5 初始化工具类MyBatisUtils

 如何保证SqlSessionFactory在应用中全局唯一

 1 package com.imooc.mybatis.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 
 8 import java.io.IOException;
 9 import java.io.Reader;
10 
11 /**
12  * MyBatisUtils工具类,创建全局唯一的SqlSessionFactory对象
13  */
14 public class MyBatisUtils {
15     //利用static(静态)属于类不属于对象,且全局唯一
16     private static SqlSessionFactory sqlSessionFactory = null;
17     //利用静态块在初始化类时实例化sqlSessionFactory
18     static {
19         Reader reader = null;
20         try {
21             reader = Resources.getResourceAsReader("mybatis-config.xml");
22             sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
23         } catch (IOException e) {
24             e.printStackTrace();
25             //初始化错误时,通过抛出异常ExceptionInInitializerError通知调用者
26             throw new ExceptionInInitializerError(e);
27         }
28     }
29 
30     /**
31      * openSession 创建一个新的SqlSession对象
32      * @return SqlSession对象
33      */
34     public static SqlSession openSession(){
35         //默认SqlSession对自动提交事务数据(commit)
36         //设置false代表关闭自动提交,改为手动提交事务数据
37         return sqlSessionFactory.openSession(false);
38     }
39 
40     /**
41      * 释放一个有效的SqlSession对象
42      * @param session 准备释放SqlSession对象
43      */
44     public static void closeSession(SqlSession session){
45         if(session != null){
46             session.close();
47         }
48     }
49 }
View Code

测试类中使用

6 MyBatis数据查询步骤

1→2

3→4

5→6

1.创建实体类

增加一些列的自由属性,与goods表一一对应,

写好字段信息后,生成get,set方法

 1 package com.imooc.mybatis.entity;
 2 
 3 import java.util.List;
 4 
 5 public class Goods {
 6     private Integer goodsId;//商品编号
 7     private String title;//标题
 8     private String subTitle;//子标题
 9     private Float originalCost;//原始价格
10     private Float currentPrice;//当前价格
11     private Float discount;//折扣率
12     private Integer isFreeDelivery;//是否包邮 ,1-包邮 0-不包邮
13     private Integer categoryId;//分类编号
14     private List<GoodsDetail> goodsDetails;
15 
16     public Integer getGoodsId() {
17         return goodsId;
18     }
19 
20     public void setGoodsId(Integer goodsId) {
21         this.goodsId = goodsId;
22     }
23 
24     public String getTitle() {
25         return title;
26     }
27 
28     public void setTitle(String title) {
29         this.title = title;
30     }
31 
32     public String getSubTitle() {
33         return subTitle;
34     }
35 
36     public void setSubTitle(String subTitle) {
37         this.subTitle = subTitle;
38     }
39 
40     public Float getOriginalCost() {
41         return originalCost;
42     }
43 
44     public void setOriginalCost(Float originalCost) {
45         this.originalCost = originalCost;
46     }
47 
48     public Float getCurrentPrice() {
49         return currentPrice;
50     }
51 
52     public void setCurrentPrice(Float currentPrice) {
53         this.currentPrice = currentPrice;
54     }
55 
56     public Float getDiscount() {
57         return discount;
58     }
59 
60     public void setDiscount(Float discount) {
61         this.discount = discount;
62     }
63 
64     public Integer getIsFreeDelivery() {
65         return isFreeDelivery;
66     }
67 
68     public void setIsFreeDelivery(Integer isFreeDelivery) {
69         this.isFreeDelivery = isFreeDelivery;
70     }
71 
72     public Integer getCategoryId() {
73         return categoryId;
74     }
75 
76     public void setCategoryId(Integer categoryId) {
77         this.categoryId = categoryId;
78     }
79 
80     public List<GoodsDetail> getGoodsDetails() {
81         return goodsDetails;
82     }
83 
84     public void setGoodsDetails(List<GoodsDetail> goodsDetails) {
85         this.goodsDetails = goodsDetails;
86     }
87 }
View Code

2.创建mapper  xml

说明实体类和哪个表对应

  1 <?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="goods">
  6     <!--开启了二级缓存
  7         eviction是缓存的清除策略,当缓存对象数量达到上限后,自动触发对应算法对缓存对象清除
  8             1.LRU – 最近最少使用的:移除最长时间不被使用的对象。
  9             O1 O2 O3 O4 .. O512
 10             14 99 83 1     893
 11             2.FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
 12             3.SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
 13             4.WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
 14 
 15         flushInterval 代表间隔多长时间自动清空缓存,单位毫秒,600000毫秒 = 10分钟
 16         size 缓存存储上限,用于保存对象或集合(1个集合算1个对象)的数量上限
 17         readOnly 设置为true ,代表返回只读缓存,每次从缓存取出的是缓存对象本身.这种执行效率较高
 18                  设置为false , 代表每次取出的是缓存对象的"副本",每一次取出的对象都是不同的,这种安全性较高
 19     -->
 20     <cache eviction="LRU" flushInterval="600000" size="512" readOnly="true"/>
 21     <!-- useCache="false"代表不使用缓存 -->
 22     <select id="selectAll" resultType="com.imooc.mybatis.entity.Goods" useCache="false">
 23         select * from t_goods order by goods_id desc limit 10
 24     </select>
 25     <!-- 单参数传递,使用parameterType指定参数的数据类型即可,SQL中#{value}提取参数-->
 26     <select id="selectById" parameterType="Integer" resultType="com.imooc.mybatis.entity.Goods">
 27         select * from t_goods where goods_id = #{value}
 28     </select>
 29 
 30     <!-- 多参数传递时,使用parameterType指定Map接口,SQL中#{key}提取参数 -->
 31     <select id="selectByPriceRange" parameterType="java.util.Map" resultType="com.imooc.mybatis.entity.Goods">
 32         select * from t_goods
 33         where
 34           current_price between  #{min} and #{max}
 35         order by current_price
 36         limit 0,#{limt}
 37     </select>
 38 
 39     <!-- 利用LinkedHashMap保存多表关联结果
 40         MyBatis会将每一条记录包装为LinkedHashMap对象
 41         key是字段名  value是字段对应的值 , 字段类型根据表结构进行自动判断
 42         优点: 易于扩展,易于使用
 43         缺点: 太过灵活,无法进行编译时检查
 44      -->
 45     <select id="selectGoodsMap" resultType="java.util.LinkedHashMap" flushCache="true">
 46         select g.* , c.category_name,'1' as test from t_goods g , t_category c
 47         where g.category_id = c.category_id
 48     </select>
 49 
 50     <!--结果映射-->
 51     <resultMap id="rmGoods" type="com.imooc.mybatis.dto.GoodsDTO">
 52         <!--设置主键字段与属性映射-->
 53         <id property="goods.goodsId" column="goods_id"></id>
 54         <!--设置非主键字段与属性映射-->
 55         <result property="goods.title" column="title"></result>
 56         <result property="goods.originalCost" column="original_cost"></result>
 57         <result property="goods.currentPrice" column="current_price"></result>
 58         <result property="goods.discount" column="discount"></result>
 59         <result property="goods.isFreeDelivery" column="is_free_delivery"></result>
 60         <result property="goods.categoryId" column="category_id"></result>
 61         <result property="category.categoryId" column="category_id"></result>
 62         <result property="category.categoryName" column="category_name"></result>
 63         <result property="category.parentId" column="parent_id"></result>
 64         <result property="category.categoryLevel" column="category_level"></result>
 65         <result property="category.categoryOrder" column="category_order"></result>
 66 
 67 
 68         <result property="test" column="test"/>
 69     </resultMap>
 70     <select id="selectGoodsDTO" resultMap="rmGoods">
 71         select g.* , c.*,'1' as test from t_goods g , t_category c
 72         where g.category_id = c.category_id
 73     </select>
 74     <!--flushCache="true"在sql执行后强制清空缓存-->
 75     <insert id="insert" parameterType="com.imooc.mybatis.entity.Goods" flushCache="true">
 76         INSERT INTO t_goods(title, sub_title, original_cost, current_price, discount, is_free_delivery, category_id)
 77         VALUES (#{title} , #{subTitle} , #{originalCost}, #{currentPrice}, #{discount}, #{isFreeDelivery}, #{categoryId})
 78       <!--<selectKey resultType="Integer" keyProperty="goodsId" order="AFTER">-->
 79           <!--select last_insert_id()-->
 80       <!--</selectKey>-->
 81     </insert>
 82 
 83     <update id="update" parameterType="com.imooc.mybatis.entity.Goods">
 84         UPDATE t_goods
 85         SET
 86           title = #{title} ,
 87           sub_title = #{subTitle} ,
 88           original_cost = #{originalCost} ,
 89           current_price = #{currentPrice} ,
 90           discount = #{discount} ,
 91           is_free_delivery = #{isFreeDelivery} ,
 92           category_id = #{categoryId}
 93         WHERE
 94           goods_id = #{goodsId}
 95     </update>
 96     <!--delete from t_goods where goods_id in (1920,1921)-->
 97     <delete id="delete" parameterType="Integer">
 98         delete from t_goods where goods_id = #{value}
 99     </delete>
100 
101     <select id="selectByTitle" parameterType="java.util.Map" resultType="com.imooc.mybatis.entity.Goods">
102         select * from t_goods where title = #{title}
103         ${order}
104     </select>
105 
106     <select id="dynamicSQL" parameterType="java.util.Map" resultType="com.imooc.mybatis.entity.Goods">
107         select * from t_goods
108         <where>
109           <if test="categoryId != null">
110               and category_id = #{categoryId}
111           </if>
112           <if test="currentPrice != null">
113               and current_price &lt; #{currentPrice}
114           </if>
115         </where>
116     </select>
117 
118     <!--
119         resultMap可用于说明一对多或者多对一的映射逻辑
120         id 是resultMap属性引用的标志
121         type 指向One的实体(Goods)
122     -->
123     <resultMap id="rmGoods1" type="com.imooc.mybatis.entity.Goods">
124         <!-- 映射goods对象的主键到goods_id字段 -->
125         <id column="goods_id" property="goodsId"></id>
126         <!--
127             collection的含义是,在
128             select * from t_goods limit 0,1 得到结果后,对所有Goods对象遍历得到goods_id字段值,
129             并代入到goodsDetail命名空间的findByGoodsId的SQL中执行查询,
130             将得到的"商品详情"集合赋值给goodsDetails List对象.
131         -->
132         <collection property="goodsDetails" select="goodsDetail.selectByGoodsId"
133                     column="goods_id"/>
134     </resultMap>
135     <select id="selectOneToMany" resultMap="rmGoods1">
136         select * from t_goods limit 0,10
137     </select>
138 
139     <select id="selectPage" resultType="com.imooc.mybatis.entity.Goods">
140         select * from t_goods where current_price &lt; 1000
141     </select>
142 
143     <!--INSERT INTO table-->
144     <!--VALUES ("a" , "a1" , "a2"),("b" , "b1" , "b2"),(....)-->
145     <insert id="batchInsert" parameterType="java.util.List">
146         INSERT INTO t_goods(title, sub_title, original_cost, current_price, discount, is_free_delivery, category_id)
147         VALUES
148         <foreach collection="list" item="item" index="index" separator=",">
149             (#{item.title},#{item.subTitle}, #{item.originalCost}, #{item.currentPrice}, #{item.discount}, #{item.isFreeDelivery}, #{item.categoryId})
150         </foreach>
151     </insert>
152     <!--in (1901,1902)-->
153     <delete id="batchDelete" parameterType="java.util.List">
154         DELETE FROM t_goods WHERE goods_id in
155         <foreach collection="list" item="item" index="index" open="(" close=")" separator=",">
156             #{item}
157         </foreach>
158     </delete>
159 </mapper>
View Code

注意dtd声明与之前不同

namesapce:命名空间,类似java中的包,对于不同表或不同功能的sql语句可以指定不同的命名空间,

      通过不同的命名空间就可以区分开不同的sql语句了.不同命名空间中的sql id可以同名

 id:sql的名字

ResultType:数据返回的对象,sql语句执行完后会自动的将得到的每一条记录包装成对应的goods对象

如何让mybatis认识这个goods.xml?

要在mybatis-config.xml中声明

 下面用测试类执行写好的sql

返回的是goods对象

对于表中有下划线的字段 在实体类中由于语法问题 只能用驼峰命名法,这样会造成丢值的问题

只需在mybatis文件中增加驼峰命名法与字段名转换的配置就可

 

SQL传参

1)查询

 

 ①根据id查一条数据

第二个参数传入的值类型要与mapper中  parameterType的类型一致!

②根据价值范围查询

mybatis只支持一个parameterType的传递,如果要传递多个,parameterType中设置的就不能是某个基础类型,而是Map

 1 <!-- 单参数传递,使用parameterType指定参数的数据类型即可,SQL中#{value}提取参数-->
 2     <select id="selectById" parameterType="Integer" resultType="com.imooc.mybatis.entity.Goods">
 3         select * from t_goods where goods_id = #{value}
 4     </select>
 5 
 6     <!-- 多参数传递时,使用parameterType指定Map接口,SQL中#{key}提取参数 -->
 7     <select id="selectByPriceRange" parameterType="java.util.Map" resultType="com.imooc.mybatis.entity.Goods">
 8         select * from t_goods
 9         where
10           current_price between  #{min} and #{max}
11         order by current_price
12         limit 0,#{limt}
13     </select>

若映射文件中,SQL语句对应的id是全局唯一的,调用时也可以不写命名空间

2)MyBatis获取多表关联查询结果

 

有一个问题:返回的map中,对应的字段顺序是混乱的

因为hashmap的机制决定,key是按照key的hash值来排序的,而哈希值是一个不稳定的数字

为了保证字段的前后顺序一致

返回值不要使用Map接口,而要使用具体的对象 

 

map还可以自己进行扩展字段

 

 

7 ResultMap结果映射

扩展一个java对象 对多表查询返回的结果进行保存

对goods对象进行扩展

如何让mybatis自动对其进行对应赋值呢?

 

 测试类:

 resultMap中对应数据库的字段的属性应该是column

8 MyBatis日志输出

什么是日志?

9 MyBatis内置日志工厂

step1 添加依赖

执行后,在控制台可以看到打印出来的sql语句

step 2 展现日志 自定义日志 

resource文件夹下新建logback.html文件

appender 输出器

class 不能随便写 意思是向控制台进行打印输出 

pattern 输出时间 + 线程名称 + 日志级别(-5:按五个字符右对齐) + 是哪个类产生的日志({36}:最多允许36字符,超过会采用简写形式)  +  具体的日志输出内容 + 换行

root 打印的跟标签

level 日志输出级别

appender-ref的ref属性 :debug级别以上的信息都会按照pattern中定义的格式在console中输出,开发环境一般设置为debug,生产环境,一般设置为info.

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <configuration>
 3    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
 4        <encoder>
 5            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
 6        </encoder>
 7    </appender>
 8 
 9     <!--
10         日志输出级别(优先级高到低):
11         error: 错误 - 系统的故障日志
12         warn: 警告 - 存在风险或使用不当的日志
13         info: 一般性消息
14         debug: 程序内部用于调试信息
15         trace: 程序运行的跟踪信息
16      -->
17     <root level="debug">
18         <appender-ref ref="console"/>
19     </root>
20 </configuration>

logback官网:http://logback.qos.ch/     

  文档(Documentation)中可以看到各种logback的配置细节

logback中文网:http://www.logback.cn/

MyBatis配置SLFJ日志组件 

10 数据库事务

先往日志中写,通过commit提交后会一次性把所有日志写入数据表中

如果发现有数据丢失,客户端可以发起回滚功能,这时事务日志中的所有日志将会全部被删除

11 MyBatis数据写入 

MyBatis写操作包含三种:

新增:

获取最新插入的主键:selectKeyuseGeneratedKeys

oracle中要用序列sql实现

更新:

删除:

12 Map的妙用

Map的适用场景

 - 为SQL语句传入多个参数

 - 查询返结果包含跨表字段

13 ResultMap结果映射

14 MyBatis预防SQL注入攻击

15 MyBatis工作流程

16 动态sql

应用场景:

根据选择条件搜索

 为了解决上述问题,使用where标签,自动判断and是否会产生语法错误

17  Mybatis二级缓存

一级缓存,每个对象有一个,用完释放就没有了

相同命名空间的二级缓存中,一级缓存是可以共享的

总结:

1.

不同对象,同一个session,相同的sql写了两次,只执行一次

相同的对象,不同的session,相同的sql分别写了一次,这两次都会执行

不同对象,同一个session,两个相同的sql之间使用commit强制清空了缓存,就会执行两次

2.在实际开发中,一级缓存会被默认开启,但它的生命周期较短,缓存的开启和关闭之间只执行了少量的sql语句.

这里的缓存对象很快被释放,这也意味着缓存使用率并不高.为了解决这个问题,mybatis提供了二级缓存.

3.二级缓存需要手动开启,我们要在对应的xml文件中进行配置(如该例子中的goods.xml)

开启了二级缓存后,在不同的session中查询相同的数据,全局只执行了一次sql

运行结果:

 上下两个sql的哈希值也是一致的,说明他们在同一块内存中

一共执行了两次sql,第一次没有通过缓存,第二次通过缓存提取,所以命中率1/2 = 0.5

在实际开发中,缓存命中率越高代码缓存的使用效率越高.对程序优化效果越好,二级缓存存储到了命名空间这个级别上,不会因为session的开启和关闭跟着销毁.

4.二级缓存相关参数

 list返回结果不固定,缓存命中率不高,类似情况,不推荐使用二级缓存,设置useCache为false即可

一般将单一结果存入二级缓存中

内存足够的情况下.size不要太小.如1000个实体对象,size设置1000

size表示缓存存储的对象或集合的数量的上限。如果是集合,不论里面存储多少个对象,都代表一个对象

有时需要在执行一个sql之后立马清空缓存,而不是在commit后清空,只要将flushCache设置为true即可,切该条sql结果不会放入缓存

18 Mybatis多表级联查询

OneToMany对象关联查询

应用:商品和详情对象关联查询

商品详情表

gd_id 自动生成的主键

goods_id 对应了每个商品,也是商品表的外键

gd_pic_url 商品图片

order 代表显示时的排序顺序 100是第一张图片,200是第二张...以此类推

创建实体类

在entity中新建GoodsDetail实体类

在实体类中增加4个私有属性及对应的get,set方法

创建&修改mapper文件

在resources→mapper目录中,创建对应的mapper文件

分析:Goods和Goodsdetail的关系,Goods是1,GoodsDetail是多

如果用对象的方式表达,1的一方如何持有多的一方呢?

我们可以在1的里面添加一个集合

同时添加上对应的get,set方法

由于list中的数据我们还没有获取,所以现在是空的,我们需要在1的一方(Goods.xml)对这个集合进行说明.

在Goods.xml中下方新增如下代码

column:主键字段(数据库中)

property:实体类中对应的字段名(Tips:property中填写的一定是实体类中某个属性的名字)

collection:描述Goods对象中的list集合的数据从哪来

-property:对应Goods中list的名字

最终就可以通过Goods中的goodsDetails来获得商品详情的信息了

测试类中测试方法

运行后报错

goodsDetail的selectByGoodsId并没有找到对应的sql

报错原因:

忘记在mybatis注册文件中将新的mapper注册了.

解决方法:在配置文件中新增

 

 

总结:

这种通过对象获取其他对象的方式我们就称为对象关联查询.这些过程都是有mybatis中书写了配置信息之后自动完成的.

ManyToOne对象关联查询

应用:商品和详情对象关联查询,通过某个goodsDetail对象获得商品信息

修改实体类

多的一方关联到1的一方,只需在多的一方的实体类中添加多的一方的实体对象即可.同时添加上相应的get,set方法.

修改mapper文件

修改goods_detail.xml

association中的property:指向多的一方实体类中的1的实体对象.

select:命名空间.select的id

column:根据哪个字段查询

测试类中测试方法

注:

在关联了实体类对象后,goodsId变成了null?

原因

这是mybatis的默认策略,GoodsDetail中的goodsId被用到了对象关联上 ,因此没有被正确赋值

解决方法

在mapper的resultMap中手动增加一个result,完成这个字段的映射

19 分页插件PageHelper

 在文档中可以看到具体的操作流程.

PageHelper使用流程

jsqlparser:PageHelper的底层依赖

配置文件中引用PageHelper

pom.xml中添加依赖

 原理:在原有sql基础上进行分析与解析(使用jsqlparser这个sql解释器组件)后,自动进行分页以及select * 这样的语句

mybatis-config.xml中增加插件配置

应用:商品信息分页

修改mapper文件:goods.xml

在测试类中调用
 1  @Test
 2     /**
 3      * PageHelper分页查询
 4      */
 5     public void testSelectPage() throws Exception {
 6         SqlSession session = null;
 7         try {
 8             session = MyBatisUtils.openSession();
 9             /*startPage方法会自动将下一次查询进行分页*/
10             PageHelper.startPage(2,10);
11             Page<Goods> page = (Page) session.selectList("goods.selectPage");
12             System.out.println("总页数:" + page.getPages());
13             System.out.println("总记录数:" + page.getTotal());
14             System.out.println("开始行号:" + page.getStartRow());
15             System.out.println("结束行号:" + page.getEndRow());
16             System.out.println("当前页码:" + page.getPageNum());
17             List<Goods> data = page.getResult();//当前页数据
18             for (Goods g : data) {
19                 System.out.println(g.getTitle());
20             }
21             System.out.println("");
22         } catch (Exception e) {
23             throw e;
24         } finally {
25             MyBatisUtils.closeSession(session);
26         }
27     }
View Code

不同数据库分页的实现原理

1)Mysql

limit 起始行号,从这行之后向后取多少行;

2)Oracle

三层嵌套

最内层是核心查询sql,外面两层一般是固定的

rownum 伪列,表示当前行号

3)SQL Server 2000

4)SQL Server 2012+

20 Mybatis批处理

批量插入

在goods.xml书写批量插入代码

collection:迭代的数据源从哪来,集合类型,mybatis强制要求为list

item:循环中的迭代变量

index:循环的索引

seperator:分隔器,例如写逗号,表示每个数据之间用逗号分隔.但最后一个记录不会再增加逗号.

测试

 1 /**
 2      * 批量插入测试
 3      * @throws Exception
 4      */
 5     @Test
 6     public void testBatchInsert() throws Exception {
 7         SqlSession session = null;
 8         try {
 9             long st = new Date().getTime();
10             session = MyBatisUtils.openSession();
11             List list = new ArrayList();
12             for (int i = 0; i < 10000; i++) {
13                 Goods goods = new Goods();
14                 goods.setTitle("测试商品");
15                 goods.setSubTitle("测试子标题");
16                 goods.setOriginalCost(200f);
17                 goods.setCurrentPrice(100f);
18                 goods.setDiscount(0.5f);
19                 goods.setIsFreeDelivery(1);
20                 goods.setCategoryId(43);
21                 //insert()方法返回值代表本次成功插入的记录总数
22 
23                 list.add(goods);
24             }
25             session.insert("goods.batchInsert", list);
26             session.commit();//提交事务数据
27             long et = new Date().getTime();
28             System.out.println("执行时间:" + (et - st) + "毫秒");
29 //            System.out.println(goods.getGoodsId());
30         } catch (Exception e) {
31             if (session != null) {
32                 session.rollback();//回滚事务
33             }
34             throw e;
35         } finally {
36             MyBatisUtils.closeSession(session);
37         }
38     }
View Code

 1  /**
 2      * 10000次数据插入对比测试用例
 3      * @throws Exception
 4      */
 5     @Test
 6     public void testInsert1() throws Exception {
 7         SqlSession session = null;
 8         try{
 9             long st = new Date().getTime();
10             session = MyBatisUtils.openSession();
11             List list = new ArrayList();
12             for(int i = 0 ; i < 10000 ; i++) {
13                 Goods goods = new Goods();
14                 goods.setTitle("测试商品");
15                 goods.setSubTitle("测试子标题");
16                 goods.setOriginalCost(200f);
17                 goods.setCurrentPrice(100f);
18                 goods.setDiscount(0.5f);
19                 goods.setIsFreeDelivery(1);
20                 goods.setCategoryId(43);
21                 //insert()方法返回值代表本次成功插入的记录总数
22 
23                 session.insert("goods.insert" , goods);
24             }
25 
26             session.commit();//提交事务数据
27             long et = new Date().getTime();
28             System.out.println("执行时间:" + (et-st) + "毫秒");
29 //            System.out.println(goods.getGoodsId());
30         }catch (Exception e){
31             if(session != null){
32                 session.rollback();//回滚事务
33             }
34             throw e;
35         }finally {
36             MyBatisUtils.closeSession(session);
37         }
38     }
View Code

 

批量导入前最好提前做好压力测试,查看sql的情况

可以通过数据分段,将过多的数据插入分成若干份插入,比如10000条,分成10次100条,通过for循环嵌套来实现.

批量删除

在goods.xml书写批量插入代码

把查询条件中具体的数值,改成in的方式

 1 /**
 2      * 批量删除测试
 3      * @throws Exception
 4      */
 5     @Test
 6     public void testBatchDelete() throws Exception {
 7         SqlSession session = null;
 8         try {
 9             long st = new Date().getTime();
10             session = MyBatisUtils.openSession();
11             List list = new ArrayList();
12             list.add(1920);
13             list.add(1921);
14             list.add(1922);
15             session.delete("goods.batchDelete", list);
16             session.commit();//提交事务数据
17             long et = new Date().getTime();
18             System.out.println("执行时间:" + (et - st) + "毫秒");
19 //            System.out.println(goods.getGoodsId());
20         } catch (Exception e) {
21             if (session != null) {
22                 session.rollback();//回滚事务
23             }
24             throw e;
25         } finally {
26             MyBatisUtils.closeSession(session);
27         }
28     }
View Code

21 Mybatis配置C3P0连接池

1.Maven中引入C3P0

2.新建目录用于存放数据源

在该目录中新建一个类

继承图中的类,来完成C3P0的迁入工作

 C3P0提供的复合连接池数据源

3. 修改mybatis-config.xml配置文件

22 MyBatis注解开发

 不用在mybatis-config.xml配置文件中配置mapper映射了

:如果采用注解开发的方式,就不需要mapper的xml文件了

取而代之.在工程目录中新建dao目录,在这个目录中创建一系列的接口

用接口+注解来替代原有的xml文件

GoodsDAO.java

 1 package com.imooc.mybatis.dao;
 2 
 3 import com.imooc.mybatis.dto.GoodsDTO;
 4 import com.imooc.mybatis.entity.Goods;
 5 import org.apache.ibatis.annotations.*;
 6 
 7 import java.util.List;
 8 
 9 public interface GoodsDAO {
10     @Select("select * from t_goods where current_price between  #{min} and #{max} order by current_price limit 0,#{limt}")
11     public List<Goods> selectByPriceRange(@Param("min") Float min ,@Param("max") Float max ,@Param("limt") Integer limt);
12 
13     @Insert("INSERT INTO t_goods(title, sub_title, original_cost, current_price, discount, is_free_delivery, category_id) VALUES (#{title} , #{subTitle} , #{originalCost}, #{currentPrice}, #{discount}, #{isFreeDelivery}, #{categoryId})")
14     //<selectKey>
15     @SelectKey(statement = "select last_insert_id()" , before = false , keyProperty = "goodsId" , resultType = Integer.class)
16     public int insert(Goods goods);
17 
18     @Select("select * from t_goods")
19     //<resultMap>
20     @Results({
21             //<id>
22           @Result(column = "goods_id" ,property = "goodsId" , id = true) ,
23             //<result>
24             @Result(column = "title" ,property = "title"),
25             @Result(column = "current_price" ,property = "currentPrice")
26     })
27     public List<GoodsDTO> selectAll();
28 }
View Code

在mybatis-config.xml文件中增加相应的说明

第一种方法,当接口比较多的时候,要重复写好多句类似代码,很麻烦,也不便于程序维护

第二种方法,只需要写一行,mybatis就会在加载时对相应包下所有的接口进行扫描

测试

 1 package com.imooc.mybatis;
 2 
 3 import com.imooc.mybatis.dao.GoodsDAO;
 4 import com.imooc.mybatis.dto.GoodsDTO;
 5 import com.imooc.mybatis.entity.Goods;
 6 import com.imooc.mybatis.utils.MyBatisUtils;
 7 import org.apache.ibatis.session.SqlSession;
 8 import org.junit.Test;
 9 
10 import java.util.List;
11 
12 //JUNIT单元测试类
13 public class MyBatisTestor {
14 
15     @Test
16     public void testSelectByPriceRange() throws Exception {
17         SqlSession session = null;
18         try{
19             session = MyBatisUtils.openSession();
20             GoodsDAO goodsDAO = session.getMapper(GoodsDAO.class);
21             List<Goods> list = goodsDAO.selectByPriceRange(100f, 500f, 20);
22             System.out.println(list.size());
23         }catch (Exception e){
24             throw e;
25         } finally {
26             MyBatisUtils.closeSession(session);
27 
28         }
29     }
30 
31     /**
32      * 新增数据
33      * @throws Exception
34      */
35     @Test
36     public void testInsert() throws Exception {
37         SqlSession session = null;
38         try{
39             session = MyBatisUtils.openSession();
40             Goods goods = new Goods();
41             goods.setTitle("测试商品");
42             goods.setSubTitle("测试子标题");
43             goods.setOriginalCost(200f);
44             goods.setCurrentPrice(100f);
45             goods.setDiscount(0.5f);
46             goods.setIsFreeDelivery(1);
47             goods.setCategoryId(43);
48             GoodsDAO goodsDAO = session.getMapper(GoodsDAO.class);
49             //insert()方法返回值代表本次成功插入的记录总数
50             int num = goodsDAO.insert(goods);
51             session.commit();//提交事务数据
52             System.out.println(goods.getGoodsId());
53         }catch (Exception e){
54             if(session != null){
55                 session.rollback();//回滚事务
56             }
57             throw e;
58         }finally {
59             MyBatisUtils.closeSession(session);
60         }
61     }
62 
63     @Test
64     public void testSelectAll() throws Exception {
65         SqlSession session = null;
66         try{
67             session = MyBatisUtils.openSession();
68             GoodsDAO goodsDAO = session.getMapper(GoodsDAO.class);
69             List<GoodsDTO> list = goodsDAO.selectAll();
70             System.out.println(list.size());
71         }catch (Exception e){
72             throw e;
73         } finally {
74             MyBatisUtils.closeSession(session);
75 
76         }
77     }
78 }
View Code

mybatis会根据注解自动生成接口的实现类

:利用注解的方式可以提高编码效率,利用xml的方式利于程序的维护,没有绝对的好坏,xml更适合于大型的需要团队合作的项目,利用注解更适用于小型敏捷开发 的项目.可以根据具体情况进行灵活的选择.

原文地址:https://www.cnblogs.com/superjishere/p/12715564.html