MyBatis(二)

因为内容太多了,所以将其拆分为以下内容

参考

https://www.bilibili.com/video/BV1NE411Q7Nx

https://mybatis.org/mybatis-3/zh/index.html

日志

日志工厂

设置名 描述 有效值 默认值
logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未设置

Mybatis 通过使用内置的日志工厂提供日志功能。内置日志工厂将会把日志工作委托给下面的实现之一:

  • SLF4J
  • Apache Commons Logging
  • Log4j 2
  • Log4j
  • JDK logging

配置日志

核心配置加入

1
2
3
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

测试结果:

initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Opening JDBC Connection
Created connection 1658699134.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@62ddbd7e]
==> Preparing: select * from user where id = ?
==> Parameters: 1(Integer)
<== Columns: id, name, pwd
<== Row: 1, 张三, 123456
<== Total: 1
User0{id=1, name='张三', password='123456'}
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@62ddbd7e]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@62ddbd7e]
Returned connection 1658699134 to pool.
Process finished with exit code 0

Log4j

Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

1、导入 log4j 包

1
2
3
4
5
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

2、log4j.properties

详细配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# priority  :debug<info<warn<error
#you cannot specify every priority with different file for log4j
log4j.rootLogger=debug,stdout,info,debug,warn,error

#console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern= [%d{yyyy-MM-dd HH:mm:ss a}]:%p %l%m%n
#info log
log4j.logger.info=info
log4j.appender.info=org.apache.log4j.DailyRollingFileAppender
log4j.appender.info.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.info.File=./src/com/hp/log/info.log
log4j.appender.info.Append=true
log4j.appender.info.Threshold=INFO
log4j.appender.info.layout=org.apache.log4j.PatternLayout
log4j.appender.info.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
#debug log
log4j.logger.debug=debug
log4j.appender.debug=org.apache.log4j.DailyRollingFileAppender
log4j.appender.debug.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.debug.File=./src/com/hp/log/debug.log
log4j.appender.debug.Append=true
log4j.appender.debug.Threshold=DEBUG
log4j.appender.debug.layout=org.apache.log4j.PatternLayout
log4j.appender.debug.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
#warn log
log4j.logger.warn=warn
log4j.appender.warn=org.apache.log4j.DailyRollingFileAppender
log4j.appender.warn.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.warn.File=./src/com/hp/log/warn.log
log4j.appender.warn.Append=true
log4j.appender.warn.Threshold=WARN
log4j.appender.warn.layout=org.apache.log4j.PatternLayout
log4j.appender.warn.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
#error
log4j.logger.error=error
log4j.appender.error = org.apache.log4j.DailyRollingFileAppender
log4j.appender.error.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.error.File = ./src/com/hp/log/error.log
log4j.appender.error.Append = true
log4j.appender.error.Threshold = ERROR
log4j.appender.error.layout = org.apache.log4j.PatternLayout
log4j.appender.error.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n

略简版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file

#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/wnhyang.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

极简版

1
2
3
4
5
6
7
8
# 全局日志配置
log4j.rootLogger=ERROR,stdout
# MyBatis 日志配置
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# 控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

3、配置 log4j

1
2
3
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>

4、测试运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[org.apache.ibatis.logging.LogFactory]-Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 279593458.
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@10aa41f2]
[com.dao.UserMapper.getUserById]-==> Preparing: select * from user where id = ?
[com.dao.UserMapper.getUserById]-==> Parameters: 1(Integer)
[com.dao.UserMapper.getUserById]-<== Total: 1
User0{id=1, name='张三', password='123456'}
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@10aa41f2]
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@10aa41f2]
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Returned connection 279593458 to pool.

简单使用:

1、在要使用 Log4j 的类中,导入包 import org.apache.log4j.Logger;

2、日志对象,参数为当前的class

1
static Logger logger=  Logger.getLogger(UserDaoTest.class);

3、日志级别

1
2
3
logger.info("info:进入了testLog4j");
logger.debug("debug:进入了testLog4j");
logger.error("error:进入了testLog4j");

注解开发

使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。

1、接口方法上加上注解

1
2
@Select("select * from user")
List<User> getUserList();

2、核心配置绑定接口

1
2
3
<mappers>
<mapper resource="com/dao/UserMapper.class"/>
</mappers>

xml 与注解同时使用需要:接口和它的 Mapper 配置文件必须同名且必须在同一包下,并配置 xml

3、测试

本质:反射机制实现

底层:动态代理

CRUD

1
2
3
4
5
6
7
8
@Select()
...
@Insert()
...
@Update()
....
@Delete()
...

关于 @Param() 注解

  • 8基本类型 + String 类型,需要加上
  • 引用类型不用加
  • 只有一个基本类型,可以忽略,但建议加上
  • 在 sql 中引用的就是我们这里的 @Param() 中设定的属性名

#{} ${} 区别

参考:

MyBatis视频教程

[#{}与\({}的区别](https://blog.csdn.net/qian_qian_123/article/details/92844194?ops_request_misc=%25257B%252522request%25255Fid%252522%25253A%252522160759025519726885835134%252522%25252C%252522scm%252522%25253A%25252220140713.130102334..%252522%25257D&request_id=160759025519726885835134&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~baidu_landing_v2~default-3-92844194.pc_search_result_no_baidu_js&utm_term=#{}%E4%B8%8E\){}%E5%8C%BA%E5%88%AB)

Mybatis 详细执行流程

参考

复杂查询

数据库准备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
CREATE TABLE `teacher` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO teacher(`id`, `name`) VALUES (1, '老杨');

CREATE TABLE `student` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`tid` INT(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fktid` (`tid`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');

配置

实体类(使用了 Lombok 插件)

Teacher

1
2
3
4
5
6
7
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Teacher {
private int id;
private String name;
}

Student

1
2
3
4
5
6
7
8
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
private int id;
private String name;
private Teacher teacher;
}

接口和对应的 Mapper.xml

注意放在同一包下,且命名相同,如下模板

1
2
3
4
5
6
7
8
<?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.dao.xxxMapper">


</mapper>

核心配置

如果需要的话修改 db.properties

mybatis-config.xml 修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?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>
<properties resource="db.properties"/> <!-- 数据库配置要正确 -->

<settings> <!-- 自带日志开启 -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

<typeAliases> <!-- 扫描包取别名 -->
<package name="com.pojo"/>
</typeAliases>

<environments default="development"> <!-- 需要的修改 -->
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/dao/UserMapper.xml"/>
<mapper resource="com/dao/TeacherMapper.xml"/> <!-- + -->
<mapper resource="com/dao/StudentMapper.xml"/> <!-- + -->
</mappers>

</configuration>

测试

要求一:

查询所有学生信息,内容包括(学生id,学生name,老师name)

方法一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<resultMap id="studentMap" type="Student">
<result column="id" property="id"/>
<result column="name" property="name"/>
<!-- 复杂属性,需要单独处理
对象:association
集合:collection
-->
<association property="teacher" column="tid" javaType="teacher" select="getTeacherById"/>
</resultMap>


<select id="getStudent" resultMap="studentMap">
select *
from student
</select>

<select id="getTeacherById" resultType="teacher">
select *
from teacher
where id = #{id}
</select>

按照查询嵌套处理,相当于子查询

方法二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<resultMap id="studentMap0" type="Student">
<result column="sid" property="id"/>
<result column="sname" property="name"/>
<!-- 复杂属性,需要单独处理
对象:association
集合:collection
-->
<association property="teacher" javaType="teacher">
<result column="tname" property="name" />
</association>
</resultMap>

<select id="getStudent0" resultMap="studentMap0">
select s.id sid, s.name sname, t.name tname
from student s,
teacher t
where s.tid = t.id
</select>

按照结果嵌套处理,即连表查询

要求二:

查询所有老师信息,内容包括(老师id,老师name,所有学生(学生id,学生name,学生老师id))

实体类的变化

Student0

1
2
3
4
5
6
7
8
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student0 {
private int id;
private String name;
private int tid;
}

Teacher0

1
2
3
4
5
6
7
8
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Teacher0 {
private int id;
private String name;
private List<Student0> student0s;
}

方法一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<resultMap id="teacherMap0" type="teacher0">
<result column="id" property="id"/>
<result column="name" property="name"/>
<collection property="student0s" javaType="ArrayList" ofType="Student0" select="getStudentByTid" column="id"/>
</resultMap>


<select id="getTeacher0" resultMap="teacherMap0">
select *
from teacher
where id = #{id}
</select>
<select id="getStudentByTid" resultType="student0">
select *
from student
where tid = #{id}
</select>

按照查询嵌套处理,相当于子查询

方法二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<resultMap id="teacherMap" type="teacher0">
<result column="tid" property="id"/>
<result column="tname" property="name"/>
<!--
javaType="" 指定属性类型
集合中的泛型信息,我们使用 ofType 获取
-->
<collection property="student0s" ofType="Student0">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>

<select id="getTeacher" resultMap="teacherMap">
select t.id tid, t.name tname, s.id sid, s.name sname
from student s,
teacher t
where s.tid = t.id
and t.id = #{id}
</select>

按照结果嵌套处理,即连表查询

总结

1、关联 - association

2、集合 - collection

3、javaType & ofType

  1. javaType 用来指定实体类中属性的类型
  2. ofType 用来指定映射到 List 或集合中的 pojo 类型,泛型中的约束类型

注意点:

  • sql 可读性
  • 数据库字段与实体类属性对应
  • 排错依靠日志信息(log4j等)

因为内容太多了,所以将其拆分为以下内容