视频
https://www.bilibili.com/video/BV1EE411W7Q1
代码
https://github.com/wei198621/allTypes_persistence
mybatis 相关网站
MyBatis中文官网 http://www.mybatis.cn/
mybatis中文网 http://www.mybatis.org (202012不可以访问了,原因未知)
mybatis 简介
环境搭建
获取sqlSession
public class TestMybatis {
public static void main(String[] args) throws SQLException, IOException {
/* //读取配置 方式一
InputStream resourceAsStream = TestMybatis.class.getResourceAsStream("/mybatis-config.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);*/
//Mybatis读取配置文件的方式
Reader resourceAsReader = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(resourceAsReader);
SqlSession sqlSession = sessionFactory.openSession();
System.out.println(sqlSession);
//org.apache.ibatis.session.defaults.DefaultSqlSession@5a39699c
sqlSession.getConnection().commit();
}
}
执行查询
mybatis 增删改
https://www.bilibili.com/video/BV1EE411W7Q1?p=4
20201210 12
mybatis SQL进阶学习
Xml中的特殊字符
关于 jdbcType
全部的jdbcType属性
// 测试的时候,不写 ,jdbcType=INTEGER 也可以正常更细 Null 类型的值,可能是最新版本的mybatis 解决了陈老师讲解时间点2018年时候的问题(也可能是数据库原因,我测试库是mysql)
<update id="update" parameterType="Emp">
update t_emp set name=#{name,jdbcType=VARCHAR},age=#{age,jdbcType=INTEGER},birthday=#{bir,jdbcType=DATE} where id=#{id}
</update>
关于like
//like '%keyWords%' ⇒ like '%'||#{keyWords}||'%'
<select id="selectByLike" parameterType="String" resultType="Emp">
select id,name,age,birthday bir from t_emp where name like '%'||#{keyWords}||'%'
</select>
关于分页查询
<select id="selectByPage" resultType="Emp">
<!-- < < > > <= <= >= >= -->
select e1.id,e1.name,e1.age,e1.bir from
(select e.id,e.name,e.age,e.birthday bir,rownum r from
(select id , name ,age , birthday from t_emp order by name) e
where rownum <= #{pageNow}*#{pageSize} ) e1
where r > (#{pageNow}-1)*#{pageSize}
</select>
动态sql
sql标签 & include 标签
<!-- sql sql语句片段复用标签
id : 代表片段的唯一标识 (唯一)
-->
<sql id="commonFileds">
id,name,publishDate pubDate,price,author
</sql>
<select id="selectAll" parameterType="com.baizhi.entity.Book" resultType="com.baizhi.entity.Book">
<!--
include 使用外部的哪个sql片段
refid 使用片段的id
-->
select <include refid="commonFileds"/> from t_book
</select>
if 标签 版本一
这样做
当 name author 都为空 sql 语句为 **** where 会报错
当前name为空 sql 语句为 **** where and author= 会报错
<!-- sql sql语句片段复用标签
id : 代表片段的唯一标识 (唯一) -->
<sql id="commonFileds">
id,name,publishDate pubDate,price,author
</sql>
<select id="selectByFields" parameterType="com.baizhi.entity.Book" resultType="com.baizhi.entity.Book">
select <include refid="commonFileds"/> from t_book
where
<if test="name!=null and name!=''">
name = #{name}
</if>
<if test="author!=null and author!=''">
and author = #{author}
</if>
</select>
if 标签 版本二
<!-- sql sql语句片段复用标签
id : 代表片段的唯一标识 (唯一) -->
<sql id="commonFileds">
id,name,publishDate pubDate,price,author
</sql>
<select id="selectByFields" parameterType="com.baizhi.entity.Book" resultType="com.baizhi.entity.Book">
select <include refid="commonFileds"/> from t_book
where 1=1
<if test="name!=null and name!=''">
and name = #{name}
</if>
<if test="author!=null and author!=''">
and author = #{author}
</if>
</select>
if 标签 版本三 (if标签 + where 标签)
<!-- sql sql语句片段复用标签
id : 代表片段的唯一标识 (唯一) -->
<sql id="commonFileds">
id,name,publishDate pubDate,price,author
</sql>
<select id="selectByFields" parameterType="com.baizhi.entity.Book" resultType="com.baizhi.entity.Book">
select <include refid="commonFileds"/> from t_book
<where>
<if test="name!=null and name!=''">
name = #{name}
</if>
<if test="author!=null and author!=''">
and author = #{author}
</if>
</where>
</select>
choose when 标签
// name price 都不为空 ,只执行第一个name ***的匹配
<sql id="commonFileds">
id,name,publishDate pubDate,price,author
</sql>
<select id="selectByChoose" parameterType="com.baizhi.entity.Book" resultType="com.baizhi.entity.Book">
select <include refid="commonFileds"/> from t_book
where
<choose>
<when test="name!=null and name!=''">
name = #{name}
</when>
<when test="price!=null">
price = #{price}
</when>
<otherwise>
author = #{author}
</otherwise>
</choose>
</select>
set 标签
// A code block
var foo = 'bar';
trim 标签
<update id="update" parameterType="com.baizhi.entity.Book">
update t_book
<!--
prefix:加入前缀
prefixOverrides:去掉前缀
suffix:加入后缀
suffixOverrides:去掉后缀
-->
<trim prefix="set" suffix="where" suffixOverrides=",">
<if test="name!=null and name != ''">
name = #{name},
</if>
<if test="author!=null and author != ''">
author = #{author}
</if>
</trim>
id = #{id}
</update>
foreach 批处理
---- 批处理
BEGIN
insert into t_book values('13','六脉神剑',sysdate,120.23,'小黑');
insert into t_book values('14','如来神掌',sysdate,110.23,'小名');
insert into t_book values('15','葵花宝典',sysdate,80.23,'无名');
insert into t_book values('16','星星点灯',sysdate,90.34,'小牛');
insert into t_book values('17','黯然销魂掌',sysdate,67.23,'小伟');
END;
<insert id="insertAll" parameterType="list">
BEGIN
<!--
collection:用来书写遍历集合类型
index : 当前遍历的索引 从0 开始
item : 当前遍历元素的别名
open : 在第一遍历之前加入的语句
close : 在最后一个次遍历之后加入的语句
separator: 在每一次遍历之后加入的语句 最后一次不加入
-->
<foreach collection="list" close=";" index="i" item="book" separator=";">
insert into t_book values(#{book.id},#{book.name},#{book.pubDate},#{i},#{book.author})
</foreach>
END;
</insert>
关联关系 一对一
数据库关系描述 : 用户表 user 与 身份信息表 card 是一对一的关系 ,数据库中可以在user表中加入外键cardid 引用 card 表,也可以在card表中加入user表id,作为外键。现实操作中我没有使用外键的习惯,(不清楚是好是坏)
代码中类描述: 可以如下,在User类中加入Card类,也可以在Card 类中引入 User ,
public class User {
private String id;
private String name;
private Integer age;
private Date bir;
private Card card;//关系属性 不要出现在toString方法里面
}
当SQL结果集,不是java代码中的对象时候, 用resultMap 替换resultType
association 处理一对一关系
<?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" >
<mapper namespace="com.baizhi.dao.UserDAO">
<!--
处理关联关系时 resultType无法将关系属性进行自动封装 只能处理单表简单类型(对象)
处理关系属性赋值 :使用resultMap
type:封装对象类型 如果是一个 直接写对象的全名(别名) 如果是多个 同样写泛型的类型
id : resultMap的名字
-->
<resultMap type="com.baizhi.entity.User" id="aaa">
<!-- 用来封装user中的主键的值 column 数据库返回的列名 property 类中属性名 -->
<id column="userId" property="id"/>
<!-- 用来封装user中的普通属性值 -->
<result column="name" property="name"/>
<result column="age" property="age"/>
<result column="bir" property="bir"/>
<!-- 关系属性封装 一对一 对象类型
association: 用来处理一对一关系属性封装
property : 关系属性名
javaType: 关系属性的类型
-->
<association property="card" javaType="com.baizhi.entity.Card" >
<id column="cardId" property="id" />
<result column="no" property="no"/>
<result column="address" property="address"/>
<result column="fork" property="fork"/>
</association>
</resultMap>
<!--
resultMap: 封装对象时使用外部的哪个resultMap进行封装 书写resultMap标签的id属性
-->
<select id="queryAll" resultMap="aaa">
select u.id userId,u.name,u.age,u.bir,
c.id cardId,c.no,c.fork,c.address
from t_users u
left join t_card c
on u.cardId = c.id
</select>
</mapper>
//关系属性 不要出现在toString方法里面
如果 User.Card Card.User 相互出现在彼此的toString方法里面, 当打印某个toString方法时候,会形成回环,造成栈溢出,稍后试试
视频地址 – 编程不良人
https://www.bilibili.com/video/BV1EE411W7Q1?p=10
<?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" >
<mapper namespace="com.tiza.leo.mybatis_baizhi02_complex.dao.BookDAO">
<sql id="commonFields">
id,name,publishDate pubDate,price,author
</sql>
<select id="selectAll" parameterType="com.tiza.leo.mybatis_baizhi02_complex.entity.Book" resultType="com.tiza.leo.mybatis_baizhi02_complex.entity.Book">
select <include refid="commonFields"/> from mybatis_baizhi_book
</select>
<select id="selectByFields" parameterType="com.tiza.leo.mybatis_baizhi02_complex.entity.Book" resultType="com.tiza.leo.mybatis_baizhi02_complex.entity.Book">
select <include refid="commonFields"/> from mybatis_baizhi_book
<where>
<if test="name!=null and name!=''">
name = #{name}
</if>
<if test="author!=null and author!=''">
and author = #{author}
</if>
</where>
</select>
<select id="selectByChoose" parameterType="com.tiza.leo.mybatis_baizhi02_complex.entity.Book" resultType="com.tiza.leo.mybatis_baizhi02_complex.entity.Book">
select <include refid="commonFields"/> from mybatis_baizhi_book
WHERE
<choose>
<when test="name!=null and name!=''">
name=#{name}
</when>
<when test="price!=null">
price=#{price}
</when>
<otherwise>
author=#{author}
</otherwise>
</choose>
</select>
<update id="update" parameterType="com.tiza.leo.mybatis_baizhi02_complex.entity.Book">
update mybatis_baizhi_book
<trim prefix="set" suffix="where" suffixOverrides=",">
<if test="name!=null and name!=''">
name=#{name}
</if>
<if test="author!=null and author!=''">
author =#{author},
</if>
</trim>
id=#{id}
</update>
<!-- * mysql 测试没有通过 在local zhan comp
* oracle 没有测试
*
* 一致 mysql 不支持 begin end 批量处理语法 -->
<insert id="insertAll" parameterType="list">
BEGIN
<!--
collection:用来书写遍历集合类型
index : 当前遍历的索引 从0 开始
item : 当前遍历元素的别名
open : 在第一遍历之前加入的语句
close : 在最后一个次遍历之后加入的语句
separator: 在每一次遍历之后加入的语句 最后一次不加入
-->
<foreach collection="list" close=";" index="i" item="book" separator=";">
insert into mybatis_baizhi_book values(#{book.id},#{book.name},#{book.pubDate},#{i},#{book.author})
</foreach>
END;
</insert>
</mapper>
关联关系 一对多
// A code block
var foo = 'bar';
关联关系 多对多
学生与课程的关系是多对多 (通过选课表关联)
代码
https://github.com/wei198621/allTypes_persistence
/**
* @author leowei
* @date 2020/12/13 - 10:01
*/
public class Student {
private String id;
private String name;
private Integer age;
private List<Course> courses;//关系属性 用来表示一个学生选了多个课程
}
/**
* @author leowei
* @date 2020/12/13 - 10:01
*/
public class Course {
private String id;
private String name;
}
/**
* @author leowei
* @date 2020/12/13 - 13:19
*/
public interface StudentDAO {
public List<Student> queryAll();
}
<?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" >
<mapper namespace="com.tiza.leo._05mybatis_baizhi03_complex_plus.dao.StudentDAO">
<resultMap id="studentMap" type="com.tiza.leo._05mybatis_baizhi03_complex_plus.entity.Student">
<id column="id" property="id" />
<result column="name" property="name"/>
<result column="age" property="age"/>
<!-- 封装课程信息 一对多 collection类型 -->
<collection property="courses" javaType="list" ofType="com.tiza.leo._05mybatis_baizhi03_complex_plus.entity.Course">
<id column="cid" property="id"/>
<result column="cname" property="name"/>
</collection>
</resultMap>
<select id="queryAll" resultMap="studentMap">
select s.id,s.name,s.age,
c.id cid,c.name cname
from mybatis_baizhi_student s
left join mybatis_baizhi_student_course sc on s.id = sc.sid
left join mybatis_baizhi_course c on sc.cid = c.id
</select>
</mapper>
package com.tiza.leo._05mybatis_baizhi03_complex_plus.test;
import com.tiza.leo._05mybatis_baizhi03_complex_plus.dao.StudentDAO;
import com.tiza.leo._05mybatis_baizhi03_complex_plus.entity.Course;
import com.tiza.leo._05mybatis_baizhi03_complex_plus.entity.Student;
import com.tiza.leo._05mybatis_baizhi03_complex_plus.util.MybatisUtil;
import java.util.List;
/**
* @author leowei
* @date 2020/12/13 - 13:25
*/
public class TestStudentDAO {
public static void main(String[] args) {
StudentDAO mapper = MybatisUtil.getSqlSession().getMapper(StudentDAO.class);
List<Student> students = mapper.queryAll();
for (Student student : students) {
System.out.println("student = " + student);
List<Course> courses = student.getCourses();
for (Course cours : courses) {
System.out.println("cours = " + cours);
}
System.out.println("===================================================");
}
}
}
MyBatis 通过包含的jdbcType类型
BIT FLOAT CHAR TIMESTAMP OTHER UNDEFINED
TINYINT REAL VARCHAR BINARY BLOB NVARCHAR
SMALLINT DOUBLE LONGVARCHAR VARBINARY CLOB NCHAR
INTEGER NUMERIC DATE LONGVARBINARY BOOLEAN NCLOB
BIGINT DECIMAL TIME NULL CURSOR
Mybatis中javaType和jdbcType对应和CRUD例子
//column 列名
<resultMap type="java.util.Map" id="resultjcm">
<result property="FldNumber" column="FLD_NUMBER" javaType="double" jdbcType="NUMERIC"/>
<result property="FldVarchar" column="FLD_VARCHAR" javaType="string" jdbcType="VARCHAR"/>
<result property="FldDate" column="FLD_DATE" javaType="java.sql.Date" jdbcType="DATE"/>
<result property="FldInteger" column="FLD_INTEGER" javaType="int" jdbcType="INTEGER"/>
<result property="FldDouble" column="FLD_DOUBLE" javaType="double" jdbcType="DOUBLE"/>
<result property="FldLong" column="FLD_LONG" javaType="long" jdbcType="INTEGER"/>
<result property="FldChar" column="FLD_CHAR" javaType="string" jdbcType="CHAR"/>
<result property="FldBlob" column="FLD_BLOB" javaType="Blob" jdbcType="BLOB" />
<result property="FldClob" column="FLD_CLOB" javaType="string" jdbcType="CLOB"/>
<result property="FldFloat" column="FLD_FLOAT" javaType="float" jdbcType="FLOAT"/>
<result property="FldTimeStamp" column="FLD_TIMESTAMP" javaType="java.sql.Timestamp" jdbcType="TIMESTAMP"/>
</resultMap>
Mybatis中javaType和jdbcType对应关系
JDBCType JavaType
CHAR String
VARCHAR String
LONGVARCHAR String
NUMERIC java.math.BigDecimal
DECIMAL java.math.BigDecimal
BIT boolean
BOOLEAN boolean
TINYINT byte
SMALLINT short
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[color=red][/color]
配置logback打印SQL日志
恰好我的环境跟这位兄台的一直,所以 参考文章 :
https://blog.csdn.net/u012489412/article/details/86691611
我的配置
我当前的springboot版本2.1.2.RELEASE
myabtis版本为3.5.0
logback版本为1.2.3
开始配置
1.在application.yml里面添加logback-spring.xml配置
下面是我的logback.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true" scan="true" scanPeriod="1 seconds">
<contextName>logback</contextName>
<!--定义参数,后面可以通过${app.name}使用-->
<property name="app.name" value="logback_test"/>
<!--logback方式打印日志的目录地址-->
<property name="scheduler.manager.server.home" value="log_logback"></property>
<!--ConsoleAppender 用于在屏幕上输出日志-->
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<!--定义了一个过滤器,在LEVEL之下的日志输出不会被打印出来-->
<!--这里定义了DEBUG,也就是控制台不会输出比ERROR级别小的日志 即DEBUG,INFO,WARN,ERROR,FATAL; -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>即DEBUG</level>
</filter>
<!-- encoder 默认配置为PatternLayoutEncoder -->
<!--定义控制台输出格式-->
<encoder>
<pattern>%d [%thread] %-5level %logger{36} [%file : %line] - %msg%n</pattern>
</encoder>
</appender>
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--定义日志输出的路径-->
<!--这里的scheduler.manager.server.home 没有在上面的配置中设定,所以会使用java启动时配置的值-->
<!--比如通过 java -Dscheduler.manager.server.home=/path/to XXXX 配置该属性-->
<file>${scheduler.manager.server.home}/logs/${app.name}.log</file>
<!--定义日志滚动的策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--定义文件滚动时的文件名的格式-->
<fileNamePattern>${scheduler.manager.server.home}/logs/${app.name}.%d{yyyy-MM-dd.HH}.log.gz
</fileNamePattern>
<!--60天的时间周期,日志量最大20GB-->
<maxHistory>60</maxHistory>
<!-- 该属性在 1.1.6版本后 才开始支持-->
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<!--每个日志文件最大100MB-->
<maxFileSize>100MB</maxFileSize>
</triggeringPolicy>
<!--定义输出格式-->
<encoder>
<pattern>%d [%thread] %-5level %logger{36} [%file : %line] - %msg%n</pattern>
</encoder>
</appender>
<!--这个一定要 -->
<logger name="com.tiza.xcmg.syncVehDataFromCrm.mapper" level="DEBUG" ></logger>
<!--root是默认的logger 这里设定输出级别是debug-->
<root level="ERROR">
<!--定义了两个appender,日志会通过往这两个appender里面写-->
<appender-ref ref="stdout"/>
<appender-ref ref="file"/>
</root>
</configuration>
关于这里,有两点想说明,
第一点:需要添加logger
<!--这个一定要 -->
<logger name="com.tiza.xcmg.syncVehDataFromCrm.mapper" level="DEBUG" ></logger>
第二点:root的level为INFO,这里使用debug也可以打印日志,但是日志太多,看起来头疼。也不方便有问题时查找问题。