zoukankan      html  css  js  c++  java
  • 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更适合于大型的需要团队合作的项目,利用注解更适用于小型敏捷开发 的项目.可以根据具体情况进行灵活的选择.

  • 相关阅读:
    C# 图片与Base64的相互转化
    LeetCode 303. Range Sum Query – Immutable
    LeetCode 300. Longest Increasing Subsequence
    LeetCode 292. Nim Game
    LeetCode 283. Move Zeroes
    LeetCode 279. Perfect Squares
    LeetCode 268. Missing Number
    LeetCode 264. Ugly Number II
    LeetCode 258. Add Digits
    LeetCode 257. Binary Tree Paths
  • 原文地址:https://www.cnblogs.com/superjishere/p/12715564.html
Copyright © 2011-2022 走看看