Mybatis-xdclass

2020年10月23日09:11:09 by ddhhdd
end time:2020年10月25日22:13:57

01 ER图

ER图分为实体、属性、关系三个核心部分。实体是长方形体现,而属性则是椭圆形,关系为菱形。

  • ER图的实体(entity即数据模型中的数据对象,例如人、学生、音乐都可以作为一个数据对象,用长方体来表示,每个实体都有自己的实体成员(entity member)或者说实体对象(entity instance),例如学生实体里包括张三、李四等,实体成员(entity member)/实体实例(entity instance) 不需要出现在ER图中。

  • ER图的属性(attribute即数据对象所具有的属性,例如学生具有姓名、学号、年级等属性,用椭圆形表示,属性分为唯一属性(unique attribute)和非唯一属性,唯一属性指的是唯一可用来标识该实体实例或者成员的属性,用下划线表示,一般来讲实体都至少有一个唯一属性。

  • ER图的关系(relationship用来表现数据对象与数据对象之间的联系,例如学生的实体和成绩表的实体之间有一定的联系,每个学生都有自己的成绩表,这就是一种关系,关系用菱形来表示。

1.1 ER图中关联关系有三种:

  • 1对1(1:1):1对1关系是指对于实体集A与实体集B,A中的每一个实体至多与B中一个实体有关系;反之,在实体集B中的每个实体至多与实体集A中一个实体有关系。

  • 1对多(1:N):1对多关系是指实体集A与实体集B中至少有N(N>0)个实体有关系;并且实体集B中每一个实体至多与实体集A中一个实体有关系。

  • 多对多(M:N):多对多关系是指实体集A中的每一个实体与实体集B中至少有M(M>0)个实体有关系,并且实体集B中的每一个实体与实体集A中的至少N(N>0)个实体有关系。

1.2 下面是个简单的例子:

1.3 ER实体补充讲解:

ER的实体还会细分为弱实体和复合实体:

  • 弱实体:一个实体必须依赖于另一个实体存在,那么前者是弱实体,后者是强实体,弱实体必须依赖强实体存在,例如上图的学生实体和成绩单实体,成绩单依赖于学生实体而存在,因此学生是强实体,而成绩单是弱实体。

  • 弱实体和强实体的联系必然只有1:N或者1:1,这是由于弱实体完全依赖于强实体,强实体不存在,那么弱实体就不存在,所以弱实体是完全参与联系的,因此弱实体与联系之间的联系也是用的双线菱形

上面实例根据弱实体的情况更改如下图:

  • 复合实体:复合实体也称联合实体或桥接实体,常常用于实现两个或多个实体间的M:N联系,它由每个关联实体的主玛组成,用长方体内加一个菱形来表示。

下图就是一个典型的复合实体,因为只是举例,相对粗糙,用户和商品两个实体是M:N的关系,中间又订单这个实体联系,因此订单这个实体是一个复合实体,同时如果用户实体不存在,就没有订单实体的存在,因此对于用户实体来讲订单是弱实体,同理商品实体如果不存在,同样不存在订单实体,因此对商品实体而言订单是弱实体,具体如图:

ER属性补充讲解:

ER图的属性还细分为复合属性、多值属性和派生属性、可选属性,同时还有用来表示联系的属性,称为联系属性。

  • 复合属性(composite attribute:复合属性是指具有多个属性的组合,例如名字属性,它可以包含姓氏属性和名字属性,如下图:

复合属性也有唯一属性,例如学生的所在班级属性,由于多个年级都有班级,所以单单班级属性是不唯一的,但是和年级组成的复合属性后则可以匹配成唯一属性。

  • 多值属性(multivalued attribute:一个实体的某个属性可以有多个不同的取值,例如一本书的分类属性,这本书有多个分类,例如科学、医学等,这个分类就是多值属性, 用双线椭圆表示。

  • 派生属性(derivers attribute:是非永久性存于数据库的属性。派生属性的值可以从别的属性值或其他数据(如当前日期)派生出来,用虚线椭圆表示,如下图。

下面的小组人数就是典型的派生属性,随着学生实例的参加的兴趣小组变化,小组人数属性也会变化,一般来讲派生属性不存在于数据库中,而是通过相应的公式进行计算得到,如果要放到数据库中,那么隔一段时间就要进行更新,否则会出现数据错误。

  • 可选属性(optional attribute:并不是所有的属性都必须有值,有些属性的可以没有值,这就是可选属性,在椭圆的文字后用(O)来表示,如下图的地址就是一个可选属性。

  • 联系属性:联系属于用户表示多个实体之间联系所具有的属性,一般来讲M:N的两个实体的联系具有联系属性,在1:1和1:M的实体联系中联系属性并不必要。

02 在线教育项目核心数据库表设计-ER图

  • chapter
CREATE TABLE `chapter` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `video_id` int(11) DEFAULT NULL COMMENT '视频主键',
  `title` varchar(128) DEFAULT NULL COMMENT '章节名称',
  `ordered` int(11) DEFAULT NULL COMMENT '章节顺序',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
  • episode
CREATE TABLE `episode` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(524) DEFAULT NULL COMMENT '集标题',
  `num` int(10) DEFAULT NULL COMMENT '第几集,全局顺序',
  `ordered` int(11) DEFAULT NULL COMMENT '顺序,章里面的顺序',
  `play_url` varchar(256) DEFAULT NULL COMMENT '播放地址',
  `chapter_id` int(11) DEFAULT NULL COMMENT '章节主键id',
  `free` tinyint(2) DEFAULT '0' COMMENT '0表示免费,1表示首付',
  `cover_img` varchar(524) DEFAULT NULL COMMENT '封面图',
  `video_id` int(10) DEFAULT NULL COMMENT '视频id',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
  • user
CREATE TABLE `user` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(128) DEFAULT NULL COMMENT '昵称',
  `pwd` varchar(124) DEFAULT NULL COMMENT '密码',
  `head_img` varchar(524) DEFAULT NULL COMMENT '头像',
  `phone` varchar(64) DEFAULT '' COMMENT '手机号',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
  • video
CREATE TABLE `video` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(524) DEFAULT NULL COMMENT '视频标题',
  `summary` varchar(1026) DEFAULT NULL COMMENT '概述',
  `cover_img` varchar(524) DEFAULT NULL COMMENT '封面图',
  `price` int(11) DEFAULT NULL COMMENT '价格,分',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `c_id` int(10) DEFAULT NULL COMMENT '子分类id',
  `point` double(11,2) DEFAULT '8.70' COMMENT '默认8.7,最高10分',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
  • video_banner
CREATE TABLE `video_banner` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `url` varchar(256) DEFAULT NULL COMMENT '跳转地址',
  `img` varchar(256) DEFAULT NULL COMMENT '图片地址',
  `create_time` datetime DEFAULT NULL,
  `weight` int(11) DEFAULT NULL COMMENT '数字越小排越前',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  • video_order
CREATE TABLE `video_order` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `out_trade_no` varchar(64) DEFAULT NULL COMMENT '订单唯一标识',
  `state` int(11) DEFAULT NULL COMMENT '0表示未支付,1表示已支付',
  `create_time` datetime DEFAULT NULL COMMENT '订单生成时间',
  `total_fee` int(11) DEFAULT NULL COMMENT '支付金额,单位分',
  `video_id` int(11) DEFAULT NULL COMMENT '视频主键',
  `video_title` varchar(256) DEFAULT NULL COMMENT '视频标题',
  `video_img` varchar(256) DEFAULT NULL COMMENT '视频图片',
  `user_id` int(12) DEFAULT NULL COMMENT '用户id',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

03 javaweb通过原生jdbc访问数据库

3.1 原生jdbc访问数据库步骤

  1. 加载JDBC驱动程序
  2. 创建数据库的连接
  3. 创建preparedStatement
  4. 执行SQL语句
  5. 处理结果集
  6. 关闭JDBC对象资源

3.2 SpringBoot项目测试原生JDBC连接

  • pom.xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
  • JdbcApp
package com.ddhhdd.demo;

/**
 * @author ddhhdd
 * @date 2020/10/4 17:38
 */

import java.sql.*;

public class JdbcApp {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        /**
         * 1.加载JDBC驱动程序
         */
        Class.forName("com.mysql.cj.jdbc.Driver");
        /**
         * 2.创建数据库的连接
         */
        String url = "jdbc:mysql://127.0.0.1:3306/xdclass_ssm?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT";
        String username = "root";
        String password = "ddhhdd";
        /**
         * 3.创建preparedStatement
         */
        // 获取连接对象,并连接数据库
        Connection connection = DriverManager.getConnection(url, username, password);
        // 获取语句对象
        Statement statement = connection.createStatement();
        /**
         * 4.执行SQL语句
         */
        ResultSet resultSet = statement.executeQuery("select * from  video");
        /**
         * 5.处理结果集
         */
        while (resultSet.next()) {
            System.out.println("视频标题: " + resultSet.getString("title"));
        }
        /**
         * 6.关闭JDBC对象资源
         */
        statement.close();
    }
}

04 Mybatis 3.x快速入门

  • pom.xml(版本号version可以不用写?)
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.19</version>
</dependency>

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.4</version>
</dependency>
  • 配置mybatis-config.xml

(参考mybatis官网https://mybatis.org/mybatis-3/zh/getting-started.html)

resources下可以新建configmapper两个目录

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url"
                          value="jdbc:mysql://127.0.0.1:3306/xdclass?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false&amp;jdbc:mysql://127.0.0.1:3306/xdclass_ssm?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false&amp;serverTimezone=GMT"/>
                <property name="username" value="root"/>
                <property name="password" value="ddhhdd"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="mapper/VideoMapper.xml"/>
    </mappers>
</configuration>
  • Video
package net.xdclass.online_class.domain;

import lombok.Data;

import java.util.Date;

/**
 * 视频类
 */
@Data
public class Video {

    private int id;
    /**
     * 视频标题
     */
    private String title;
    /**
     * 概述
     */
    private String summary;
    /**
     * 封面图
     */
    private String coverImg;
    /**
     * 价格,分
     */
    private int price;
    /**
     * 创建时间
     */
    private Date createTime;
    /**
     * 评分默认8.7,最高10分
     */
    private Double point;
}
  • VideoMapper
package net.xdclass.online_class.dao;

import net.xdclass.online_class.domain.Video;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface VideoMapper {

    /**
     * 根据视频id查找视频对象
     *
     * @param videoId
     * @return
     */
    // @Param("video_id"),给参数取别名,如果是多个参数,不取别名的话会找不到(一个参数可能不会出错)
    Video selectById(@Param("video_id") int videoId);

    /**
     * 视频列表
     *
     * @return
     */
    // 如果sql简单,没有过多的表关联,则用注解相对简单
    @Select("select * from video")
    List<Video> selectList();

}
  • 配置VideoMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--
namespace:名称空间,一般需要保持全局唯一,最好是和dao层的java接口一致,这样就可以映射sql语句到对应的方法名称和参数、返回类型;

mybatis是使用接口动态代理,所以namespace必须为dao层的全路径
-->
<mapper namespace="net.xdclass.online_class.dao.VideoMapper">

    <!--
    id:当前mapper下需要唯一
    resultType:sql查询结果集的封装
    注意:`id = #{video_id}`,如果没有取别名`Video selectById(@Param("video_id") int videoId);`
    则需要和入参保持一致,即`id = #{videoId}`,如果取了别名,就用别名
    
    注意:一个参数的情况下,`id = #{obj}`都是可以的
    -->
    <select id="selectById" resultType="net.xdclass.online_class.domain.Video">

        select * from video where id = #{video_id}

    </select>

</mapper>
  • SqlSessionDemo
package net.xdclass.online_class;

import net.xdclass.online_class.dao.VideoMapper;
import net.xdclass.online_class.domain.Video;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class SqlSessionDemo {
    public static void main(String[] args) throws IOException {
        String resource = "config/mybatis-config.xml";
        // 读取配置文件
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 构建SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //获取session
        try {
            SqlSession sqlSession = sqlSessionFactory.openSession();
            VideoMapper videoMapper = sqlSession.getMapper(VideoMapper.class);
            Video video = videoMapper.selectById(40);
            System.out.println(video.toString());

            List<Video> videoList = videoMapper.selectList();
            System.out.println(videoList.toString());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

05 Mybatis开发必备调试之控制台打印Sql

Mybatis使用流程

  • 创建mybatis-config.xml 全局的配置文件
  • 创建xxxMapper.xml配置文件
  • 创建SqlSessionFactory
  • 用SqlSessionFactory创建SqlSession对象
  • 用SqlSession执行增删改查CRUD

内置的日志工厂提供日志功能,使用log4j配置打印sql

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.5</version>
</dependency>

在应用的classpath中创建名称为log4j.properties的文件

log4j.rootLogger=ERROR, stdout
log4j.logger.net.xdclass=DEBUG
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

06 Mybatis配置驼峰字段映射java对象和数据库字段

数据库字段是下划线,java属性是驼峰,怎么查询映射上去?

  • 方法1(取别名)
select cover_img as coverImg from video // 多字段怎么办
  • 方法2(mybatis-config.xml,加在文件顶部)
<!-- 下划线自动映射驼峰字段(不然有些下划线的字段会查不出来) -->
<settings>
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

07 Mybatis入参parameterType和取值类型

parameterType参数类型,可以是:

  • 基本类型
parameterType="java.lang.Long"
parameterType="java.lang.String"
  • Java集合List或者Map
parameterType="java.util.Map"
parameterType="java.util.List"
  • Java自定义对象
parameterType="net.xdclass.online_class.domain.Video"

取参数值,具体某个字段的类型,从java类型映射到数据库类型

例子:#{title, jdbcType=VARCHAR}

注意:多数情况不加能正常使用,但是如果出现报错:无效的列类型,则是缺少jdbcType;只有当字段可为NULL时才需要jdbcType属性

常见的数据库类型和java类型对比:

JDBC Type Java Type
CHAR String
VARCHAR String
LONGVARCHAR String
NUMERIC java.math.BigDecimal
DECIMAL java.math.BigDecimal
BIT boolean
BOOLEAN boolean
TINYINT byte
SMALLINT short
INTEGER INTEGER
INTEGER int
BIGINT long
REAL float
FLOAT double
DOUBLE double
BINARY byte[]
VARBINARY byte[]
LONGVARBINARY byte[]
DATE java.sql.Date
TIME java.sql.Time
TIMESTAMP java.sql.Timestamp
CLOB Clob
BLOB Blob
ARRAY Array
DISTINCT mapping of underlying type
STRUCT Struct
REF Ref
DATALINK java.net.URL

08 Mybatis插入语法之视频新增和自增主键

  • VideoMapper.xml
<!--
获取自增主键:`useGeneratedKeys="true" keyProperty="id" keyColumn="id"`;
会自动的将自增主键赋值到实体类`Video`中的`id`属性;
-->
<insert id="add" parameterType="net.xdclass.online_class.domain.Video"
        useGeneratedKeys="true" keyProperty="id" keyColumn="id">
    INSERT INTO `video` (
        `title`,
        `summary`,
        `cover_img`,
        `price`,
        `create_time`,
        `point`
    )
    VALUES
    (
        #{title,jdbcType=VARCHAR},
        #{summary,jdbcType=VARCHAR},
        #{coverImg,jdbcType=VARCHAR},
        #{price,jdbcType=INTEGER},
        #{createTime,jdbcType=TIMESTAMP},
        #{point,jdbcType=DOUBLE}
    )
</insert>

2020年11月26日15:39:26,经过测试,如果自增主键为task_id,

<insert id="addTask" parameterType="com.cmft.fscp.platfrom.domain.Task" useGeneratedKeys="true" keyProperty="taskId" keyColumn="task_id">

<insert id="addTask" parameterType="com.cmft.fscp.platfrom.domain.Task" useGeneratedKeys="true" keyProperty="taskId" keyColumn="taskId">

# 都能返回插入的Task这个类的信息
  • SqlSessionDemo
// 新增一条video记录
Video video = new Video();
video.setTitle("小滴课堂面试专题900道");
video.setCoverImg("xdclass.net/aaa.png");
video.setPoint(9.4);
video.setCreateTime(new Date());
video.setPrice(9900);
video.setSummary("这个是面试专题概要");
int rows = videoMapper.add(video);
System.out.println(rows);
System.out.println(video.toString());

09 Mybatis 标签批量插入

需求:批量插入多条视频记录;

foreach:用于循环拼接的内置标签,常用于批量操作;
包含以下属性:

  • collection:必填,值为要迭代循环的集合类型,情况有多种;
    • 入参是List类型的时候,collection属性值为list;
    • 入参是Map类型的时候,collection属性值为map的key值;
  • item:每一个元素进行迭代时的别名;
  • index:索引的属性名,在集合数组情况下值为当前索引值,当迭代对象是map时,这个值是map的key;
  • open:整个循环内容的开头字符串;
  • close:整个循环内容的结尾字符串;
  • separator: 每次循环的分隔符;
<!--
foreach批量插入操作,注意foreach标签是写在values里面的(如下);
@param collection="list": 表示入参为List类型;
@param item="video":表示list中的每个元素,需要在下面引用(如 video.title),这个名字随意取;
@param separator=",":表示没条插入语句是用“逗号”分割的;
-->
<insert id="addBatch" parameterType="net.xdclass.online_class.domain.Video">
    INSERT INTO `video`
    (
        `title`,
        `summary`,
        `cover_img`,
        `price`,
        `create_time`,
        `point`
    )
    VALUES
    <foreach collection="list" item="video" separator=",">
    (
        #{video.title,jdbcType=VARCHAR},
        #{video.summary,jdbcType=VARCHAR},
        #{video.coverImg,jdbcType=VARCHAR},
        #{video.price,jdbcType=INTEGER},
        #{video.createTime,jdbcType=TIMESTAMP},
        #{video.point,jdbcType=DOUBLE}
    )
    </foreach>
</insert>
  • 如果批量插入要获取自增id,可以按照单条记录获得自增id的方式;

10 Mybatis视频更新

<update id="updateVideo" parameterType="net.xdclass.online_class.domain.Video">
    UPDATE `video`
    SET
        `title` = #{title,jdbcType=VARCHAR},
        `summary` = #{summary,jdbcType=VARCHAR},
        `cover_img` = #{coverImg,jdbcType=VARCHAR},
        `price` = #{price,jdbcType=INTEGER},
        `create_time` = #{createTime,jdbcType=TIMESTAMP},
        `point` = #{point,jdbcType=DOUBLE}
    WHERE
        id = #{id}
</update>
Video video = new Video();
video.setId(51);
video.setTitle("小滴课堂面试专题900道-update");
videoMapper.updateVideo(video);

本来意图是只想把id=51的Video的标题给改了,可得到的结果是:(没有set值的属性直接变为null,这显然不是我需要的)

Video(id=51, title=小滴课堂面试专题900道-update, summary=null, coverImg=null, price=0, createTime=null, point=null)

11 Mybatis视频更新-动态字段更新标签

  • 注意以下代码注释里面的点;
  • 实体类如果是包装数据类型,比如Integer price,则为<if test="price != null">,如果int price,则为<if test="price != 0">
  • 建议都使用包装数据类型,因为踩过坑;
  • <if test>可以用在sql的很多地方,比如WHERE后面的条件等;
<!--
选择性更新;
trim标签:由于是选择性更新,并不知道哪个属性在最后,所以需要以set开始,以“逗号”结束;(suffixOverrides)
注意:<if test="coverImg != null">,这个是coverImg,而不是cover_img;
-->
<update id="updateVideoSelective" parameterType="net.xdclass.online_class.domain.Video">
    UPDATE `video`
    <trim prefix="set" suffixOverrides=",">
        <if test="title != null and title != ''">
        `title` = #{title,jdbcType=VARCHAR},
        </if>
        
        <if test="summary != null">`summary` = #{summary,jdbcType=VARCHAR},</if>
        
        <if test="coverImg != null">`cover_img` = #{coverImg,jdbcType=VARCHAR},</if>
        
        <if test="price != 0">`price` = #{price,jdbcType=INTEGER},</if>
        
        <if test="createTime != null">`create_time` = #{createTime,jdbcType=TIMESTAMP},</if>
        
        <if test="point != null">`point` = #{point,jdbcType=DOUBLE}</if>
    </trim>
    WHERE
    id = #{id}
</update>

12 Mybatis视频删除和转义字符使用

  • <![CDATA[ 大于/小于/大于等于/小于等于 ]]>
<!-- 
根据create_time和price删除Video;
注意:需要使用大于、小于号需要进行转义;
 -->
<delete id="deleteByCreateTimeAndPrice" parameterType="java.util.Map">
    DELETE FROM video
    WHERE
    `create_time` <![CDATA[ > ]]> #{createTime}
    AND
    `price` <![CDATA[ >= ]]>  #{price}
</delete>

为什么parameterType="java.util.Map"的解释

Map<String, Object> map = new HashMap<>();
map.put("createTime","2020-10-21 01:22:42");
map.put("price",10000);
videoMapper.deleteByCreateTimeAndPrice(map);

13 Mybatis的sql片段使用

  • 项目中尽量不要使用SELECT *

什么是sql片段?

  • 根据业务需要,自定制要查询的字段,并可以复用;
<!-- 定义sql片段,可以在其他地方引用 -->
<sql id="base_video_field">
    id, title, summary, cover_img
</sql>
    
<select id="selectById" resultType="net.xdclass.online_class.domain.Video">
    <!--SELECT id, title, summary, cover_img FROM video WHERE id = #{video_id},与下面的效果一致-->
    SELECT <include refid="base_video_field"/> FROM video WHERE id = #{video_id}
</select>

14 Mybatis复杂sql查询

Mybatis的SQL语句返回结果有两种:

  • resultType:查询出的字段在相应的pojo中必须有和它相同的字段对应,或者基本数据类型,适合简单查询;
  • resultMap:需要自定义字段,或者多表查询,一对多等关系,功能比resultType更强大,适合复杂查询;
<!--
@param id:指定查询列的唯一标识(查询语句需要用的时候才需要写,其它result也是一样);
@param column:数据库字段的名称;
@param property:pojo类的属性名称;

注意:`<result column="video_title" property="title" jdbcType="VARCHAR" />`,其实这时候数据库中的字段也是title,
但归因于`title as video_title`,所以可以这么操作?
-->
<resultMap id="VideoResultMap" type="net.xdclass.online_class.domain.Video">
    <id column="id" property="id" jdbcType="INTEGER"/>
    <result column="video_title" property="title" jdbcType="VARCHAR" />
    <result column="summary" property="summary" jdbcType="VARCHAR" />
    <result column="cover_img" property="coverImg" jdbcType="VARCHAR" />
</resultMap>

<!-- resultMap的初步使用 -->
<select id="selectBasedFieldByIdWithResultMap" resultMap="VideoResultMap">
    SELECT id, title as video_title, summary, cover_img FROM video WHERE id = #{video_id}
</select>

*ResultMap复杂对象 1 : 1 查询结果映射-association

  • association:映射到pojo的某个复杂类型属性,比如:订单(Order)与用户(User)是一对一的关系;
<!--
订单(Order)与用户(User)是一对一的关系;
-->
<resultMap id="VideoOrderResultMap" type="net.xdclass.online_class.domain.VideoOrder">
    <id column="id" property="id" jdbcType="INTEGER" />
    <result column="user_id" property="userId" jdbcType="INTEGER" />
    <result column="out_trade_no" property="outTradeNo" jdbcType="VARCHAR" />
    <result column="create_time" property="createTime" jdbcType="DATE" />
    <result column="state" property="state" jdbcType="INTEGER" />
    <result column="total_fee" property="totalFee" jdbcType="INTEGER" />
    <result column="video_id" property="videoId" jdbcType="INTEGER" />
    <result column="video_title" property="videoTitle" jdbcType="VARCHAR" />
    <result column="video_img" property="videoImg" jdbcType="VARCHAR" />
    <!--
    @param association:一对一关系的标签;
    @param property="user",是实体类`VideoOrder`中有`private User user;`这个属性;
    @param javaType:`property="user"`这个属性的类型;
    -->
    <association property="user" javaType="net.xdclass.online_class.domain.User">
        <id column="id" property="id" jdbcType="INTEGER" />
        <result column="name" property="name" jdbcType="VARCHAR" />
        <result column="head_img" property="headImg" jdbcType="VARCHAR" />
        <result column="create_time" property="createTime" jdbcType="DATE" />
        <result column="phone" property="phone" jdbcType="VARCHAR" />
    </association>
</resultMap>

<!--
订单(Order)与用户(User)是一对一的关系;
-->
<select id="queryVideoOrderList" resultMap="VideoOrderResultMap">
    SELECT
    vo.id,
    vo.user_id,
    vo.out_trade_no,
    vo.create_time,
    vo.state,
    vo.total_fee,
    vo.video_id,
    vo.video_title,
    vo.video_img,
    u.name,
    u.head_img,
    u.create_time,
    u.phone
    FROM video_order vo
    LEFT JOIN user u ON vo.user_id = u.id
</select>

*ResultMap复杂对象 1 : N 查询结果映射-collection

  • collection:映射到pojo的多个复杂类型属性,比如:用户(User)与订单(Order)是一对多的关系;
<!--
用户(User)与订单(Order)是一对多的关系;
-->
<resultMap id="UserOrderResultMap" type="net.xdclass.online_class.domain.User">
    <id column="id" property="id" jdbcType="INTEGER" />
    <result column="name" property="name" jdbcType="VARCHAR" />
    <result column="head_img" property="headImg" jdbcType="VARCHAR" />
    <result column="create_time" property="createTime" jdbcType="DATE" />
    <result column="phone" property="phone" jdbcType="VARCHAR" />
    <!--
    @param collection:一对多关系的标签;
    @param property="videoOrderList",是实体类`User`中有`private List<VideoOrder> videoOrderList;`这个属性;
    @param ofType:`property="videoOrderList"`这个属性的类型;
    -->
    <collection property="videoOrderList" ofType="net.xdclass.online_class.domain.VideoOrder">
        <id column="order_id" property="id" jdbcType="INTEGER" />
        <result column="user_id" property="userId" jdbcType="INTEGER" />
        <result column="out_trade_no" property="outTradeNo" jdbcType="VARCHAR" />
        <result column="create_time" property="createTime" jdbcType="DATE" />
        <result column="state" property="state" jdbcType="INTEGER" />
        <result column="total_fee" property="totalFee" jdbcType="INTEGER" />
        <result column="video_id" property="videoId" jdbcType="INTEGER" />
        <result column="video_title" property="videoTitle" jdbcType="VARCHAR" />
        <result column="video_img" property="videoImg" jdbcType="VARCHAR" />
    </collection>
</resultMap>

<!--
用户(User)与订单(Order)是一对多的关系;
注意:`vo.id order_id,`这里取了别名,所以上面的resultMap中
<id column="order_id" property="id" jdbcType="INTEGER" />(本来数据库中的字段就是id的)
-->
<select id="queryUserOrder" resultMap="UserOrderResultMap">
    SELECT
    u.id,
    u.name,
    u.head_img,
    u.create_time,
    u.phone,
    vo.id order_id,
    vo.user_id,
    vo.out_trade_no,
    vo.create_time,
    vo.state,
    vo.total_fee,
    vo.video_id,
    vo.video_title,
    vo.video_img
    FROM `user` u
    LEFT JOIN video_order vo ON u.id = vo.user_id
</select>

15 ResultMap复杂对象查询总结

  • association映射的是一个pojo类,处理一对一的关联关系;
  • collection映射的一个集合列表,处理的是一对多的关联关系;
<!-- `column`不做限制,可以为任意表的字段,而`property`须为type定义的pojo属性 -->

<resultMap id="唯一的标识" type="映射的pojo对象">

	<id column="表的主键字段,或查询语句中的别名字段" jdbcType="字段类型" property="映射pojo对象的主键属性" />
	
	<result column="表的一个字段" jdbcType="字段类型" property="映射到pojo对象的一个属性"/>
	
	<association property="pojo的一个对象属性" javaType="pojo关联的pojo对象">
		
		<id column="关联pojo对象对应表的主键字段" jdbcType="字段类型" property="关联pojo对象的属性"/>
		
		<result column="表的字段" jdbcType="字段类型" property="关联pojo对象的属性"/>
		
	</association>
	
	<!-- 集合中的`property`需要为`oftype`定义的pojo对象的属性-->

	<collection property="pojo的集合属性名称" ofType="集合中单个的pojo对象类型">
	
	<id column="集合中pojo对象对应在表的主键字段" jdbcType="字段类型" property="集合中pojo对象的主键属性" />
	
	<result column="任意表的字段" jdbcType="字段类型" property="集合中的pojo对象的属性" />
	
	</collection>

</resultMap>

16 Mybatis多级缓存和懒加载(面试)

16.1 Mybatis一级缓存介绍和验证

什么是缓存?

  • 程序经常要调用的对象存在内存中,方便其使用时可以快速调用,不必去数据库或者其他持久化设备中查询,主要就是提高性能;

Mybatis一级缓存:

  • 简介:一级缓存的作用域是SqlSession,同一个SqlSession中执行相同的sql查询(相同的sql和参数),第一次会去数据库中查询并写在缓存中,第二次会直接从缓存中取数据;
  • 基于PerpetualCache的HashMap本地缓存;
  • 默认开启一级缓存;

失效策略:

  • 当执行sql时候两次查询中间发生了增删改的操作,即insert、update、delete等操作,commit后会清空该SqlSession缓存;
  • SqlSession关闭,或者清空等;

验证:同一个SqlSession下,执行相同的sql查询,第一次会去数据库中查询并写在缓存中,第二次会直接从缓存中取数据;

public class SqlSessionDemo {

    public static void main(String[] args) throws IOException {
        String resource = "config/mybatis-config.xml";
        // 读取配置文件
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 构建SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //获取session
        try {
            SqlSession sqlSession = sqlSessionFactory.openSession();
            VideoMapper videoMapper = sqlSession.getMapper(VideoMapper.class);

            // 验证Mybatis一级缓存
            for (int i = 0; i < 2; i++) {
                Video video = videoMapper.selectById(40);
                System.out.println(video.toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

控制台输出:(只执行了一次sql,是因为Mybatis自动开启了一级缓存)

DEBUG [main] - ==>  Preparing: SELECT id, title, summary, cover_img FROM video WHERE id = ? 
DEBUG [main] - ==> Parameters: 40(Integer)
DEBUG [main] - <==      Total: 1
Video(id=40, title=全新微信小程序零基础到项目实战, summary=https://xd-video-pc-img.oss-cn-beijing.aliyuncs.com/xdclass_pro/video/2019_frontend/%E5%B0%8F%E7%A8%8B%E5%BA%8F/wx_app_detail.png, coverImg=https://xd-video-pc-img.oss-cn-beijing.aliyuncs.com/xdclass_pro/video/2019_frontend/%E5%B0%8F%E7%A8%8B%E5%BA%8F/wxapp.png, price=null, createTime=null, point=null, cId=null)
Video(id=40, title=全新微信小程序零基础到项目实战, summary=https://xd-video-pc-img.oss-cn-beijing.aliyuncs.com/xdclass_pro/video/2019_frontend/%E5%B0%8F%E7%A8%8B%E5%BA%8F/wx_app_detail.png, coverImg=https://xd-video-pc-img.oss-cn-beijing.aliyuncs.com/xdclass_pro/video/2019_frontend/%E5%B0%8F%E7%A8%8B%E5%BA%8F/wxapp.png, price=null, createTime=null, point=null, cId=null)

Process finished with exit code 0

其它失效策略没有进行验证,记住就好了!

16.2 Mybatis二级缓存

Mybatis二级缓存:

  • 简介:二级缓存是namespace级别的,多个SqlSession去操作同一个namespace下的mapper的sql语句,多个SqlSession可以共用二级缓存,如果两个mapper的namespace相同,(即使是两个mapper,那么这两个mapper中执行sql查询到的数据也将存储在相同的二级缓存区域中,但是最后是每个mapper单独的名称空间);
  • 基于PerpetualCache的HashMap本地缓存,可自定义存储源,如Ehcache/Redis等;
  • 默认是没有开启二级缓存;
  • 操作流程:第一次调用某个namespace下的sql去查询信息,查询到的信息会存放该mapper对应的二级缓存区域。第二次调用同个namespace下的mapper映射文件中,相同
    的sql去查询信息,会去对应的二级缓存内取结果;

失效策略:

  • 执行同个namespace下的mapepr映射文件中增删改sql,并执行了commit操作,会清空该二级缓存;

注意:实现二级缓存的时候,MyBatis建议返回的POJO是可序列化的,也就是建议实现Serializable接口;

缓存淘汰策略:

  • 使用默认的LRU算法来收回(最近最少使用的)

如何开启某个二级缓存?需要在mapper.xml里面配置,以及在全局配置config/mybatis-config.xml文件中配置;

  • VideoMapper.xml
<mapper namespace="net.xdclass.online_class.dao.VideoMapper">

    <!-- 开启mapper的namespace下的二级缓存 -->
    <!--
    @param `eviction`:代表的是缓存回收策略,常见下面两种:
        (1) LRU,最近最少使用的,移除最长时间不用的对象;
        (2) FIFO,先进先出,按对象进入缓存的顺序来移除他们;
    @param `flushInterval`:刷新间隔时间,单位为毫秒,这里配置的是100秒刷新,如果不配置它,当sql被执行的时候才会去刷新缓存。
    @param `size`:引用数目,代表缓存最多可以存储多少个对象,设置过大会导致内存溢出;
    @param `readOnly`:只读,缓存数据只能读取而不能修改,默认值是false;
    -->
    <cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024"/>

</mapper>
  • mybatis-config.xml
<settings>
    <!-- 这个配置使全局的映射器(二级缓存)启用或禁用缓存,全局总开关,这里关闭,mapper中开启了也没用 -->
    <setting name="cacheEnabled" value="true" />
</settings>

验证二级缓存:

public class SqlSessionCacheDemo {

    public static void main(String[] args) throws IOException {
        String resource = "config/mybatis-config.xml";
        // 读取配置文件
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 构建SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //获取session
        try {
            SqlSession sqlSession1 = sqlSessionFactory.openSession();
            VideoMapper videoMapper1 = sqlSession1.getMapper(VideoMapper.class);
            Video video1 = videoMapper1.selectById(40);
            System.out.println(video1.toString());
            sqlSession1.commit();

            SqlSession sqlSession2 = sqlSessionFactory.openSession();
            VideoMapper videoMapper2 = sqlSession2.getMapper(VideoMapper.class);
            Video video2 = videoMapper2.selectById(40);
            System.out.println(video2.toString());
            sqlSession2.commit();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • 控制台输出:(不同的SqlSession下,只执行了一次sql查询)

注意:需要有sqlSession1.commit();,清空一级缓存,不然二级缓存失效。(不知道为啥?-ddh 2020年10月23日22:10:23)

DEBUG [main] - Cache Hit Ratio [net.xdclass.online_class.dao.VideoMapper]: 0.0
DEBUG [main] - ==>  Preparing: SELECT id, title, summary, cover_img FROM video WHERE id = ? 
DEBUG [main] - ==> Parameters: 40(Integer)
DEBUG [main] - <==      Total: 1
Video(id=40, title=全新微信小程序零基础到项目实战, summary=https://xd-video-pc-img.oss-cn-beijing.aliyuncs.com/xdclass_pro/video/2019_frontend/%E5%B0%8F%E7%A8%8B%E5%BA%8F/wx_app_detail.png, coverImg=https://xd-video-pc-img.oss-cn-beijing.aliyuncs.com/xdclass_pro/video/2019_frontend/%E5%B0%8F%E7%A8%8B%E5%BA%8F/wxapp.png, price=null, createTime=null, point=null, cId=null)
DEBUG [main] - Cache Hit Ratio [net.xdclass.online_class.dao.VideoMapper]: 0.5
Video(id=40, title=全新微信小程序零基础到项目实战, summary=https://xd-video-pc-img.oss-cn-beijing.aliyuncs.com/xdclass_pro/video/2019_frontend/%E5%B0%8F%E7%A8%8B%E5%BA%8F/wx_app_detail.png, coverImg=https://xd-video-pc-img.oss-cn-beijing.aliyuncs.com/xdclass_pro/video/2019_frontend/%E5%B0%8F%E7%A8%8B%E5%BA%8F/wxapp.png, price=null, createTime=null, point=null, cId=null)

Process finished with exit code 0
  • useCache="false"可以控制该条sql不使用二级缓存;
<select id="selectById" resultType="net.xdclass.online_class.domain.Video" useCache="false">

    <!--select * from video where id = #{video_id}-->
    SELECT
    <include refid="base_video_field"/>
    FROM video WHERE id = #{video_id}
</select>
  • 一级缓存和二级缓存使用顺序:优先查询二级缓存 -> 查询一级缓存 -> 数据库

16.3 Mybatis懒加载

什么是懒加载?

  • 按需加载,先从单表查询,需要时再从关联表去关联查询,能大大提高数据库性能;但并不是所有场景下使用懒加载都能提高效率;

Mybatis懒加载?

  • resultMap里面的associationcollection有延迟加载功能(即懒加载功能);

  • mybatis-config.xml

<settings>
    <!-- 延迟加载总开关 -->
    <setting name="lazyLoadingEnabled" value="true"/>
    
    <!-- 将aggressiveLazyLoading设置为false表示按需加载,默认为true -->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>
  • VideoMapper.xml

注意:

  • @param column:和select查询关联的字段;
  • @param select:指定懒加载需要执行的statement的id;
<!-- 测试懒加载(按需加载) -->
<resultMap id="VideoOrderResultMapLazy" type="net.xdclass.online_class.domain.VideoOrder">
    <id column="id" property="id" jdbcType="INTEGER"/>
    <result column="user_id" property="userId" jdbcType="INTEGER"/>
    <result column="out_trade_no" property="outTradeNo" jdbcType="VARCHAR"/>
    <result column="create_time" property="createTime" jdbcType="DATE"/>
    <result column="state" property="state" jdbcType="INTEGER"/>
    <result column="total_fee" property="totalFee" jdbcType="INTEGER"/>
    <result column="video_id" property="videoId" jdbcType="INTEGER"/>
    <result column="video_title" property="videoTitle" jdbcType="VARCHAR"/>
    <result column="video_img" property="videoImg" jdbcType="VARCHAR"/>
    <!--
    @param `column`:和select查询关联的字段;
    @param `select`:指定懒加载需要执行的statement的id;
    -->
    <association property="user" javaType="net.xdclass.online_class.domain.User"
                 column="user_id" select="findUserByUserId">
    </association>
</resultMap>

<!-- 测试懒加载(按需加载) -->
<select id="queryVideoOrderListLazy" resultMap="VideoOrderResultMapLazy">
    SELECT
    vo.id,
    vo.user_id,
    vo.out_trade_no,
    vo.create_time,
    vo.state,
    vo.total_fee,
    vo.video_id,
    vo.video_title,
    vo.video_img
    FROM video_order vo
</select>
<!-- 测试懒加载(按需加载) -->
<select id="findUserByUserId" resultType="net.xdclass.online_class.domain.User">
    SELECT * FROM user WHERE id = #{id}
</select>
  • SqlSessionDemo(直接用run进行测试,用debug模式测试懒加载不准确)

因为VideoOrder与User是一对一的关系;如果只查询与VideoOrder有关的内容,那么并不会执行查询User的sql;如果既查询VideoOrder有关的内容,又查询User有关的内容,则会执行查询User的sql;这个就是懒加载,只有在需要User的时候,才会进行查询。

List<VideoOrder> videoOrderList = videoMapper.queryVideoOrderListLazy();
System.out.println(videoOrderList.size());

for (VideoOrder videoOrder : videoOrderList) {
    System.out.println(videoOrder.getVideoTitle());
//  System.out.println(videoOrder.getUser().getName());
}

17 MyBatis3.x事务管理

  • 使用JDBC的事务管理:使用java.sql.Connection对象完成对事务的提交(commit())、回滚(rollback())、关闭(close());

  • 使用MANAGED的事务管理:MyBatis自身不会去实现事务管理,而让程序的容器如(Spring, JBOSS)来实现对事务的管理;

  • config/mybatis-config.xml

注意:<transactionManager type="JDBC"/><transactionManager type="MANAGED"/>

<!-- 配置数据库连接 -->
<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://127.0.0.1:3306/xdclass_ssm?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false&amp;serverTimezone=GMT"/>
            <property name="username" value="root"/>
            <property name="password" value="ddhhdd"/>
        </dataSource>
    </environment>
</environments>

  • JdbcTransaction
public class JdbcTransaction implements Transaction {
  @Override
  public void commit() throws SQLException {
    if (connection != null && !connection.getAutoCommit()) {
      if (log.isDebugEnabled()) {
        log.debug("Committing JDBC Connection [" + connection + "]");
      }
      connection.commit();
    }
  }

  @Override
  public void rollback() throws SQLException {
    if (connection != null && !connection.getAutoCommit()) {
      if (log.isDebugEnabled()) {
        log.debug("Rolling back JDBC Connection [" + connection + "]");
      }
      connection.rollback();
    }
  }

  @Override
  public void close() throws SQLException {
    if (connection != null) {
      resetAutoCommit();
      if (log.isDebugEnabled()) {
        log.debug("Closing JDBC Connection [" + connection + "]");
      }
      connection.close();
    }
  }
}
  • ManagedTransaction
public class ManagedTransaction implements Transaction {
  @Override
  public void commit() throws SQLException {
    // Does nothing
  }

  @Override
  public void rollback() throws SQLException {
    // Does nothing
  }

  @Override
  public void close() throws SQLException {
    if (this.closeConnection && this.connection != null) {
      if (log.isDebugEnabled()) {
        log.debug("Closing JDBC Connection [" + this.connection + "]");
      }
      this.connection.close();
    }
  }
}
  • SpringManagedTransaction
public class SpringManagedTransaction implements Transaction {
  @Override
  public void commit() throws SQLException {
    if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit) {
      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Committing JDBC Connection [" + this.connection + "]");
      }
      this.connection.commit();
    }
  }
  
  @Override
  public void rollback() throws SQLException {
    if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit) {
      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Rolling back JDBC Connection [" + this.connection + "]");
      }
      this.connection.rollback();
    }
  }
  
  @Override
  public void close() throws SQLException {
    DataSourceUtils.releaseConnection(this.connection, this.dataSource);
  }
}
  • 注意:如果不是web程序,然后使用的事务管理形式是MANAGED, 那么将没有事务管理功能;
  • 重点:MyISAM不支持事务,如果需要事务则改为Innodb引擎,更改数据库的表里面的引擎;

MyISAM和Innodb事务实验:

为什么原先没进行commit操作,也可以插入成功?

  • 因为原先是MyISAM引擎,没有事务,直接插入成功

检查数据库的引擎,改为Innodb(多个表video/chapter/episode/user/video_order,我这里实验只改了Video表-2020年10月25日21:54:25-ddh)

  • TransactionDemo
public class TransactionDemo {

    public static void main(String[] args) throws IOException {
        String resource = "config/mybatis-config.xml";
        // 读取配置文件
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 构建SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //获取session
        /*
        如果参数设置为true,则不需要sqlSession.commit();可以看源码,该参数表示是否自动commit;
         */
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        try {
            VideoMapper videoMapper = sqlSession.getMapper(VideoMapper.class);

            // 新增一条video记录
            Video video = new Video();
            video.setTitle("小滴课堂面试专题900道2020年10月23日1551");
            video.setCoverImg("xdclass.net/aaa.png");
            video.setPoint(9.4);
            /*
             1. 日期格式化,DateTimeFormatter线程安全,与LocalDateTime结合使用;
             2. 解决相差8小时问题:数据库字段要为varchar类型,pojo为String类型;
             3. sql格式化函数DATE_FORMAT()在select中可生效,在insert语句中报语法错误;(2020年10月25日21:47:29-ddh)
             */
            LocalDateTime dateTime = LocalDateTime.now();
            DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            video.setCreateTime(dtf.format(dateTime));

            video.setPrice(9900);
            video.setSummary("这个是面试专题概要");
            int rows = videoMapper.add(video);
            // 如果autoCommit参数设置为true,则不需要手动commit了;
            // sqlSession.commit();
            System.out.println(rows);
            System.out.println(video.toString());

        } catch (Exception e) {
            e.printStackTrace();
        }
        sqlSession.close();
    }
}

Mybatis知识点积累

  • Mybatis中sql日期格式化(2020年10月23日14:40:26)
  • sql格式化函数DATE_FORMAT()在select中可生效,在insert语句中报语法错误,不知道是否可以这么用;(2020年10月25日21:47:29-ddh)
DATE_FORMAT(t.`task_start_time`,'%Y-%m-%d %H:%i:%s') 

输出结果:

DATE_FORMAT(t.task_start_time,'%Y-%m-%d %H:%i:%s')
2020-10-12 12:25:03

如果是

DATE_FORMAT(t.`task_start_time`,'%Y-%m-%d %H:%i:%s') as task_start_time

输出结果:(所以要用这个很显然了吧?凡是涉及日期的,都要格式化一下,不然前端显示可能会出问题。)

t.task_start_time
2020-10-12 12:25:03

  • javax.persistence(2020年10月23日15:47:50)

  • pom.xml

<dependency>
    <groupId>javax.persistence</groupId>
    <artifactId>javax.persistence-api</artifactId>
    <version>2.2</version>
</dependency>
import lombok.Data;

import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;

/**
 * 视频类
 */
@Data
@Table(name = "video")
public class Video {

    @Id
    @GeneratedValue(generator = "JDBC")
    private Integer id;
    /**
     * 视频标题
     */
    private String title;
    /**
     * 概述
     */
    private String summary;
    /**
     * 封面图
     */
    private String coverImg;
    /**
     * 价格,分
     */
    private Integer price;
    /**
     * 创建时间
     */
    private Date createTime;
    /**
     * 评分默认8.7,最高10分
     */
    private Double point;
}

本来以为加入@Id以及@GeneratedValue(generator = "JDBC")注解新增一条数据会返回自增主键的,实际上并没有,不知道fscp里面的是怎么操作的?

Video(id=null, title=小滴课堂面试专题900道2020年10月23日1551, summary=这个是面试专题概要, coverImg=xdclass.net/aaa.png, price=9900, createTime=Fri Oct 23 16:01:10 CST 2020, point=9.4)

参考博文:https://www.iteye.com/blog/67566894-659829

先给出3个样例:

@SuppressWarnings("serial")
@Entity
@Table(name = "t_x")
public class X implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    @Column(length = 32)
    private String name;

    @Transient   //表示此数据不在数据库表里建立属性  
    private String temp;

    @Temporal(TemporalType.TIMESTAMP) //这个是带时分秒的类型  
    private Date date;

    @OneToOne(cascade = CascadeType.ALL, mappedBy = "x")
    private A a;
}  
@SuppressWarnings("serial")
@Entity
@Table(name = "t_a")
public class A implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "a", fetch = FetchType.EAGER)
    private List<B> b = new ArrayList<B>();

    @OneToOne()
    @JoinColumn(name = "x_Id") //加这句后就会双方共同维护关系  
    private X x;
}  
@SuppressWarnings("serial")
@Entity
public class B implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    protected int id;

    @ManyToOne()
    @JoinColumn(name = "a_id")
    protected A a;
}  
  • @Table(name="t_x")这句话可以不写,不写就已类名作为表名;
  • 如果想让两个类的属性生成一个数据表,在一个类里这样加入另一个类即可:
@Embedded
private C c;
  • 如果想要一个类继承另一个类的所有属性,则在父类里这样写:
@SuppressWarnings("serial")
@Entity
@MappedSuperclass   //增加这一行,并把父类的所有属性的private改为protected即可

注解说明:

  1. @Id声明属性为主键;
  2. @GeneratedValue表示主键是自动生成策略,一般该注释和@Id一起使用;(两者联合可返回自增id,我在试验的时候没成功,fscp项目里面可以-ddh)
  3. @Entity任何hibernte映射对象都要有的注释;
  4. @Table(name = “tablename”)声明此对象映射到哪个表;
  5. @Column(name = “Name”, nullable=false, length=32)声明数据库字段和类属性对应关系;
    • @param name: (Optional) The name of the column. Defaults to the property or field name.
    • 当字段和mysql关键字冲突时(如表中有个字段为table,sql语句中自带有table这个关键字),需要取别名;或者在sql中用``符号括住也行,本人后者使用多-ddh 2020年10月23日16:58:07)
  6. @Lob声明字段为Clob或Blob类型;
  7. @Transiten表示此属性与表没有映射关系,是一个暂时的属性;(该字段数据库中没有,不会存到数据库中)
  8. 其它请看上述的博文,目前还没有接触到;

  • 使用Mybatis自带的CRUD(2020年10月23日16:31:36)

需要引入的包:

<dependency>
    <groupId>tk.mybatis</groupId>
    <artifactId>mapper-spring-boot-starter</artifactId>
    <version>1.1.4</version>
</dependency>
import tk.mybatis.mapper.common.Mapper;
public interface VideoMapper extends Mapper<Video> {
    
}
posted @ 2020-10-25 22:13  ddhhdd  阅读(371)  评论(0)    收藏  举报