软件开发中的框架
- 框架是 可被应用开发者定制的应用骨架
- 框架是一种规则,保证开发者遵循相同的方式开发程序
- 框架提倡"不要重复造轮子",对基础功能进行封装
框架的优点
- 极大提高了开发效率
- 统一的编码规则,利于团队管理
- 灵活配置的应用,拥有更好的维护性
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&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>
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 }
测试类中使用
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 }
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 < #{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 < 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>
注意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写操作包含三种:
新增:
获取最新插入的主键:selectKey和useGeneratedKeys
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多表级联查询
应用:商品和详情对象关联查询
gd_id 自动生成的主键
goods_id 对应了每个商品,也是商品表的外键
gd_pic_url 商品图片
order 代表显示时的排序顺序 100是第一张图片,200是第二张...以此类推
创建实体类
在entity中新建GoodsDetail实体类
在实体类中增加4个私有属性及对应的get,set方法
创建&修改mapper文件
在resources→mapper目录中,创建对应的mapper文件
如果用对象的方式表达,1的一方如何持有多的一方呢?
我们可以在1的里面添加一个集合
同时添加上对应的get,set方法
由于list中的数据我们还没有获取,所以现在是空的,我们需要在1的一方(Goods.xml)对这个集合进行说明.
在Goods.xml中下方新增如下代码
property:实体类中对应的字段名(Tips:property中填写的一定是实体类中某个属性的名字)
collection:描述Goods对象中的list集合的数据从哪来
-property:对应Goods中list的名字
最终就可以通过Goods中的goodsDetails来获得商品详情的信息了
测试类中测试方法
运行后报错
goodsDetail的selectByGoodsId并没有找到对应的sql
报错原因:
解决方法:在配置文件中新增
总结:
应用:商品和详情对象关联查询,通过某个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
原理:在原有sql基础上进行分析与解析(使用jsqlparser这个sql解释器组件)后,自动进行分页以及select * 这样的语句
应用:商品信息分页

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 }
不同数据库分页的实现原理
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 }

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 }
批量导入前最好提前做好压力测试,查看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 }
21 Mybatis配置C3P0连接池
1.Maven中引入C3P0
2.新建目录用于存放数据源
在该目录中新建一个类
继承图中的类,来完成C3P0的迁入工作
C3P0提供的复合连接池数据源
3.
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 }
在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 }
注:利用注解的方式可以提高编码效率,利用xml的方式利于程序的维护,没有绝对的好坏,xml更适合于大型的需要团队合作的项目,利用注解更适用于小型敏捷开发 的项目.可以根据具体情况进行灵活的选择.