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

  • 相关阅读:
    标签,css,排版
    浏览器的内核
    焦点事件
    cookie
    浏览器的行为
    百叶窗分析
    水仙花数
    递归函数
    拖拽的问题解决
    正则的具体
  • 原文地址:https://www.cnblogs.com/superjishere/p/12715564.html
Copyright © 2011-2022 走看看