Java 学习(2022-07-17)

139 阅读4分钟

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>    

image.png

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>

image.png

1.3.4 selectKey (用于业务复杂)与 useGeneratedKeys (常用)的区别

  • selectKey标签需要明确编写获取最新主键的SQL语句
  • selectKey适用于所有的关系型数据库
  • useGeneratedKeys属性会自动根据驱动生成对应SQL语句
  • useGeneratedKeys只支持"自增主键"类型的数据库

image.png

1.4 MyBatis 预防 SQL 注入攻击

1.4.1 MyBatis 两种传值方式

  • $文本替换,未经任何处理对SQL文本替换(动态sql)
  • #{}预编译传值,使用预编译传值可以预防SQL注入

1.5 MyBatis 工作流程

image.png

1.6 MyBatis 日志管理

1.6.1 什么是日志

  • 日志文件是用于记录系统操作事件的记录文件或文件集合
  • 日志保存历史数据,是诊断问题以及理解系统活动的重要依据

1.6.2 SLF4J 与 Logback(主流)

image.png

1.6.3 添加打印日志

  1. 引入 logback依赖
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>
  1. resource目录下添加logback.xml文件

image.png

  1. 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

image.png

1.8 MyBatis 二级缓存

  • —级缓存默认开启,缓存范围SqISession会话
  • 二级缓存手动开启,属于范围Mapper Namespace image.png

1.8.1 二级缓存运行规则

  • 二级开启后默认所有查询操作均使用缓存
  • 写操作commit提交时对该namespace缓存强制清空(保证数据一致性)
  • 配置useCache=false可以不用缓存
  • 配置flushCache=true代表强制清空缓存

1.8.2 二级缓存常用参数

  • flushIntrval: 多久清空缓存,单位毫秒
  • eviction:缓存清除策略
    1. LRU - 最久未使用的被移除
    2. FIFO - 先进先出 - 按照进入缓存顺序移除
    3. SOFT - 软引用:移除基于垃圾收集器状态和软引用规则的对象
    4. WEAK - 弱引用:更积极的移除基于垃圾收集器和弱引用规则的对象
  • size:缓存对象数量
  • readOnly
    1. 设置为 true,执行性强
    2. 设置为 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

image.png

goods.xml

image.png

1.9.2 MyBatis 多对一查询(association)

image.png

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 连接池

  1. 引入依赖
<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.5.4</version>
</dependency>
  1. dataSource下建立数据源类 image.png
/**
 * C3P0 与 MyBatis 兼容使用的数据源工厂类
 */
public class C3P0DataSourceFactory extends UnpooledDataSourceFactory {
    public C3P0DataSourceFactory(){
        this.dataSource = new ComboPooledDataSource();
    }
}
  1. 修改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&amp;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 常用注解

image.png

1.13.2 注解实现xml

项目目录

image.png

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>