Java 学习(2022-07-17)
一、MyBatis
1.1 MyBatis 返回多表查询
<!-- 多表查询 , 会将每一条查询出来的结果包装程 Map 对象-->
<!-- LinkedHashMap 不会乱序, Map 会乱序-->
<select id="selectGoodsMap" resultType="java.util.LinkedHashMap" parameterType="java.util.Map" >
select c.category_id,g.* from goods g, category c
where c.category_id = g.category_id
</select>
</mapper>
1.2 ResultMap 结果映射
-
ResultMap
可以将查询结果映射为复杂类型的Java对象 -
ResultMap
适用于Java对象保存多表关联结果 -
ResultMap
支持对象关联查询等高级特性案例:使用 ResultMap 返回多表关联数据
MyBatisTest.java 测试类代码
@Test public void testSelectGoodsResultMap() throws Exception{ SqlSession sqlSession = null; try { sqlSession = MyBatisUtils.openSession(); List<GoodsDto> objects = sqlSession.selectList("goods.selectGoodsDTO"); for (GoodsDto goodsDto : objects) { System.out.println(goodsDto.getGoods().getGoodsId()); System.out.println(goodsDto.getGoods().getGoodsName()); } } catch (Exception e) { e.printStackTrace(); }finally { MyBatisUtils.closeSession(sqlSession); } }goods.xml 代码
<!-- type 映射对象--> <resultMap id="rmGoods" type="com.lyq.mybatis.dto.GoodsDto"> <!-- id 代表主键 , property 和 column 必填--> <id property="goods.goodsId" column="goods_id"></id> <!-- result 代表其他 --> <result property="goods.goodsName" column="goods_name"></result> <result property="categoryName" column="category_name"/> </resultMap> <select id="selectGoodsDTO" resultMap="rmGoods"> select c.category_name,g.* from goods g, category c where c.category_id = g.category_id </select>GoodsDTO.java 代码
public class GoodsDto { private Goods goods = new Goods(); private String categoryName; private String goods_name; public Goods getGoods() { return goods; } public void setGoods(Goods goods) { this.goods = goods; } public String getCategoryName() { return categoryName; } public void setCategoryName(String categoryName) { this.categoryName = categoryName; } public String getGoods_name() { return goods_name; } public void setGoods_name(String goods_name) { this.goods_name = goods_name; } }
1.3 MyBatis 操作数据
1.3.1 新增<insert>
测试类
public void testInsert() throws Exception{
SqlSession sqlSession = null;
try {
sqlSession = MyBatisUtils.openSession();
Goods goods = new Goods();
goods.setGoodsName("大大大瓜");
goods.setCategoryId(2);
int insert = sqlSession.insert("goods.insertGoods", goods);
// 提交事务!!!!!!!
sqlSession.commit();
System.out.println(goods.getGoodsId());
} catch (Exception e) {
if(sqlSession != null)
// 错误就回滚!!!!!
sqlSession.rollback();
e.printStackTrace();
}finally {
MyBatisUtils.closeSession(sqlSession);
}
}
Goods.xml
<!-- 传入 Goods 对象 -->
<insert id="insertGoods" parameterType="com.lyq.mybatis.entity.Goods">
insert into goods(goods_name, category_id) values (#{goodsName}, #{categoryId})
<selectKey resultType="Integer" keyProperty="goodsId" order="AFTER">
select last_insert_id()
<!-- 用来获取insert过后,获取新产生的编号,回填到 Goods的id属性中 -->
</selectKey>
</insert>
1.3.2 修改<update>
Update 案例
@Test
public void test8(){
SqlSession sqlSession = null;
try {
sqlSession = MyBatisUtils.openSession();
Goods goods = sqlSession.selectOne("goods.selectId", 1);
goods.setGoodsName("更新后的Name");
int update = sqlSession.update("goods.updateGoods", goods);
sqlSession.commit();
} catch (Exception e) {
if(sqlSession != null)
sqlSession.rollback();
e.printStackTrace();
}finally {
sqlSession.close();
}
System.out.println(update);
}
<update id="updateGoods" parameterType="com.lyq.mybatis.entity.Goods">
update goods
set goods_name = #{goodsName}
where goods_id = #{goodsId}
</update>
<!-- 如果需要传递单参数 -->
<select id="selectId" resultType="com.lyq.mybatis.entity.Goods" parameterType="Integer">
select * from goods where goods_id = #{value};
</select>
1.3.3 删除<delete>
Delete 案例
public void test9(){
SqlSession sqlSession = null;
try {
sqlSession = MyBatisUtils.openSession();
int num = sqlSession.delete("goods.deleteGoods", 1);
sqlSession.commit();
} catch (Exception e) {
if(sqlSession != null)
sqlSession.rollback();
e.printStackTrace();
}finally {
sqlSession.close();
}
}
<delete id="deleteGoods" parameterType="Integer">
delete from goods where goods_id = #{goodsId}
</delete>
1.3.4 selectKey (用于业务复杂)与 useGeneratedKeys (常用)的区别
- selectKey标签需要明确编写获取最新主键的SQL语句
- selectKey适用于
所有的关系型数据库 - useGeneratedKeys属性会
自动根据驱动生成对应SQL语句 - useGeneratedKeys
只支持"自增主键"类型的数据库
1.4 MyBatis 预防 SQL 注入攻击
1.4.1 MyBatis 两种传值方式
$文本替换,未经任何处理对SQL文本替换(动态sql)#{}预编译传值,使用预编译传值可以预防SQL注入
1.5 MyBatis 工作流程
1.6 MyBatis 日志管理
1.6.1 什么是日志
- 日志文件是用于记录系统操作事件的记录文件或文件集合
- 日志保存历史数据,是诊断问题以及理解系统活动的重要依据
1.6.2 SLF4J 与 Logback(主流)
1.6.3 添加打印日志
- 引入
logback依赖
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
- 在
resource目录下添加logback.xml文件
-
在
logback.xml写入配置信息<?xml version="1.0" encoding="UTF-8"?> <configuration> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <!-- 10:48:50.618 [main] DEBUG o.a.i.d.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections. --> <!--thread 线程名词, %d{HH:mm:ss.SSS} 控制台输出时间--> <!-- %-5level 日志级别 --> <!-- %logger{36} 那个类产生的日志 --> <!-- %n 代表换行 %msg 内容 --> <pattern> %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <!-- 日志输出级别(优先级从高到低) error:错误 - 系统故障日志 warn:警告 - 存在风险或使用不当的日志 info:一般性消息 debug:程序调试信息 trace:程序运行跟踪信息 --> <root level="info"> <appender-ref ref="console"/> </root> </configuration>
1.7 MyBatis 动态 SQL
1.8 MyBatis 二级缓存
- —级缓存
默认开启,缓存范围SqISession会话 - 二级缓存手动开启,属于范围
Mapper Namespace
1.8.1 二级缓存运行规则
二级开启后默认所有查询操作均使用缓存写操作commit提交时对该namespace缓存强制清空(保证数据一致性)- 配置
useCache=false可以不用缓存 - 配置
flushCache=true代表强制清空缓存
1.8.2 二级缓存常用参数
- flushIntrval: 多久清空缓存,
单位毫秒 - eviction:缓存清除策略
LRU- 最久未使用的被移除FIFO- 先进先出 - 按照进入缓存顺序移除SOFT- 软引用:移除基于垃圾收集器状态和软引用规则的对象WEAK- 弱引用:更积极的移除基于垃圾收集器和弱引用规则的对象
- size:缓存对象数量
- readOnly
- 设置为 true,执行性强
- 设置为 false,安全性强
- flushCache
<!-- flushCache 在 sql 执行后强制清空缓存 -->
<select id="selectId" resultType="com.lyq.mybatis.entity.Goods" parameterType="Integer" flushCache="true">
select * from goods where goods_id = #{value};
</select>
1.9 MyBatis 多表级联查询
1.9.1 MyBatis 一对多查询(collection)
goods_detail.xml
goods.xml
1.9.2 MyBatis 多对一查询(association)
1.10 MyBatis 分页插件 PageHelper
1.10.1 使用流程
- maven
引入PageHelper与jsqlparser - mybatis-config.xml
增加Plugin配置 - 代码中使用
PageHelper.startPage()自动分页
测试类
@Test
public void testPage () {
SqlSession sqlSession = null;
try {
sqlSession = MyBatisUtils.openSession();
PageHelper.startPage(2, 2);
Page<Goods> page = (Page)sqlSession.selectList("goods.selectPage");
System.out.println(page.getPages());
System.out.println(page.getTotal());
System.out.println(page.getEndRow());
List<Goods> data = page.getResult();//当前页数据
for (Goods goods : data) {
System.out.println(goods.getGoodsName());
}
} catch (Exception e) {
if(sqlSession != null)
sqlSession.rollback();
e.printStackTrace();
}finally {
sqlSession.close();
}
}
依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.10</version>
</dependency>
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>2.0</version>
</dependency>
1.11 MyBatis 拓展使用 C3P0 连接池
- 引入依赖
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.4</version>
</dependency>
- 在
dataSource下建立数据源类
/**
* C3P0 与 MyBatis 兼容使用的数据源工厂类
*/
public class C3P0DataSourceFactory extends UnpooledDataSourceFactory {
public C3P0DataSourceFactory(){
this.dataSource = new ComboPooledDataSource();
}
}
- 修改
mybatis-config.xml文件的数据源
<environment id="dev">
<!--采用 jdbc 方式-->
<transactionManager type="JDBC"></transactionManager>
<!--连接池的方式-->
<!--<dataSource type="POOLED">-->
<dataSource type="com.lyq.mybatis.dataSource.C3P0DataSourceFactory">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mysqldemo?useUnicode=true&characterEncoding=utf-8"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
<property name="initialPoolSize" value="5"/>
<property name="maxPoolSize" value="20"/>
<property name="minPoolSize" value="5"/>
</dataSource>
</environment>
1.12 MyBatis 批处理
1.12.1 批处理局限
- 无法获得插入数据的 id
- 批量生成的 SQL 太长,可能会被服务器拒绝
利用集合存储,然后使用批处理代码完成
批处理添加1000条数据
// goods.xml
<insert id="batchInsert" parameterType="java.util.List">
insert into goods(goods_name)
values
<foreach collection="list" item="item" index="index" separator=",">
(#{item.goodsName})
</foreach>
</insert>
// 测试方法
public void batchInsert () {
SqlSession sqlSession = null;
try {
sqlSession = MyBatisUtils.openSession();
List list = new ArrayList();
for (int i = 0; i < 10000; i++) {
Goods goods = new Goods();
goods.setGoodsName("测试名字");
list.add(goods);
}
int insert = sqlSession.insert("goods.batchInsert", list);
sqlSession.commit();
System.out.println("条数= " + insert);
} catch (Exception e) {
if(sqlSession != null)
sqlSession.rollback();
e.printStackTrace();
}finally {
sqlSession.close();
}
}
批量删除案例
// 测试类
public void batchDelete () {
SqlSession sqlSession = null;
try {
sqlSession = MyBatisUtils.openSession();
List list = new ArrayList();
list.add(20010);
list.add(20500);
int delete = sqlSession.delete("goods.batchDelete", list);
sqlSession.commit();
System.out.println("条数= " + delete);
} catch (Exception e) {
if(sqlSession != null)
sqlSession.rollback();
e.printStackTrace();
}finally {
sqlSession.close();
}
}
// goods.xml
<delete id="batchDelete" parameterType="java.util.List">
delete from goods where goods_id in
<foreach collection="list" item="item" index="index" open="(" close=")" separator=",">
#{item}
</foreach>
</delete>
1.13 MyBatis 注解
用注解开发,不需要xml文件
1.13.1 MyBatis 常用注解
1.13.2 注解实现xml
项目目录
Goods.Dao文件的实现
package com.mybatis.dao;
import com.mybatis.dto.GoodsDTO;
import com.mybatis.entity.Goods;
import org.apache.ibatis.annotations.*;
import java.util.List;
public interface GoodsDAO {
@Select("select * from goods where goods_id between #{start} and #{end}")
public List<Goods> selectRange(@Param("start") Integer start, @Param("end") Integer end);
@Insert("insert into goods(goods_name) values (#{goodsName})")
@SelectKey(statement = "select last_insert_id()" , before = false, keyProperty = "goodsId", resultType = Integer.class)
public int insert(Goods goods);
// 对应 <resultMap>
@Select("select * from goods")
@Results({
// 对应 id
@Result(column = "goods_id", property = "goodsId", id = true),
// 对应 result
@Result(column = "goods_name", property = "goodsName")
})
public List<GoodsDTO> selectAll();
}
GoodsDTO文件的实现
package com.mybatis.dto;
public class GoodsDTO {
private Integer goodsId;
private String goodsName;
public Integer getGoodsId() {
return goodsId;
}
public void setGoodsId(Integer goodsId) {
this.goodsId = goodsId;
}
public String getGoodsName() {
return goodsName;
}
public void setGoodsName(String goodsName) {
this.goodsName = goodsName;
}
}
Goods文件的实现
package com.mybatis.entity;
public class Goods {
private Integer goodsId;
private String goodsName;
public Integer getGoodsId() {
return goodsId;
}
public void setGoodsId(Integer goodsId) {
this.goodsId = goodsId;
}
public String getGoodsName() {
return goodsName;
}
public void setGoodsName(String goodsName) {
this.goodsName = goodsName;
}
}
mybatis-config.xml文件配置映射到Dao包下
<mappers>
<package name="com.mybatis.dao"></package>
</mappers>