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访问数据库步骤
- 加载JDBC驱动程序
- 创建数据库的连接
- 创建preparedStatement
- 执行SQL语句
- 处理结果集
- 关闭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下可以新建config和mapper两个目录

<?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&characterEncoding=utf-8&useSSL=false&jdbc:mysql://127.0.0.1:3306/xdclass_ssm?useUnicode=true&characterEncoding=utf-8&useSSL=false&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里面的
association、collection有延迟加载功能(即懒加载功能); -
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&characterEncoding=utf-8&useSSL=false&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即可
注解说明:
@Id声明属性为主键;@GeneratedValue表示主键是自动生成策略,一般该注释和@Id一起使用;(两者联合可返回自增id,我在试验的时候没成功,fscp项目里面可以-ddh)@Entity任何hibernte映射对象都要有的注释;@Table(name = “tablename”)声明此对象映射到哪个表;@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)
- @param
@Lob声明字段为Clob或Blob类型;@Transiten表示此属性与表没有映射关系,是一个暂时的属性;(该字段数据库中没有,不会存到数据库中)- 其它请看上述的博文,目前还没有接触到;
- 使用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> {
}

浙公网安备 33010602011771号