mybatis
第一章:了解MyBatis框架
一、mybatis环境搭建
1.创建工程
创建Maven工程,name为项目名,groupld为公司名。
2.引入相关依赖
在pom.xml文件用
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<!-- 单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<!-- <scope>test</scope>-->
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
3.数据库准备
创建一个mybatis数据库,在数据库里创建users表。
create database mybatis;
use mybatis;
create table users(
uid int primary key auto_increment,
uname varchar(20) not null,
uage int not null
);
insert into users(uid,uname,uage) values(null,'张三',20),(null,'李四',18);
4.编写数据库连接信息配置文件
在resources中创建一个db.properties文件
mysql.driver=com.mysql.cj.jdbc.Driver
mysql.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false
mysql.username=root
mysql.password=root
5.编写核心配置文件和映射文件
在resources中建立一个xml核心配置文件,名字自定义,一般为mybatis-config.xml
<?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>
<!-- 环境配置 -->
<!-- 引入外部db.properties文件 -->
<properties resource="db.properties"/>
<typeAliases>
<package name="com.ju"/>
</typeAliases>
<!-- environment是配置mybatis连接环境的 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<!-- 数据库连接相关配置 ,db.properties文件中的内容-->
<dataSource type="POOLED">
<property name="driver" value="${mysql.driver}"/>
<property name="url" value="${mysql.url}"/>
<property name="username" value="${mysql.username}"/>
<property name="password" value="${mysql.password}"/>
</dataSource>
</environment>
</environments>
</configuration>
6.创建POJO实体类,该类封装SQL的表里的所有字段属性
在main里的Java下创建一个包com.xiaohuang,在包里创建一个POJO实体类User.java,字段名可以和数据库字段名不对应,这里是对应的。
package com.xiaohuang;
public class User {
private int uid;
private String uname;
private int uage;
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public int getUage() {
return uage;
}
public void setUage(int uage) {
this.uage = uage;
}
}
7.创建映射文件
在resources文件下创建一个mapper文件夹,在mapper文件夹中创建UserMapper.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类似命名空间,当其他的xml映射文件中的SQL语句的id有相同发生冲突时,该命名空间就是该映射文件中SQL语句的唯一标识,能保证与其他文件的SQL语句不发生冲突-->
<mapper namespace="com.xiaohuang.User">
<select id="findById" resultType="com.xiaohuang.User">
select * from users where uid=#{id}
</select>
</mapper>
因为我们自己创建的映射文件mybatis是不知道的,所以要到核心配置文件中添加映射文件所在的路径。
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
8.创建测试类
在test文件夹下创建一个包com.xiaohuang,在包下创建一个测试类UserTest.java。
package com.xiaohuang;
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 org.junit.Test;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class UserTest {
@Test
public void testQuery(){
String resources = "mybatis-config.xml";
try {
//1.获取核心配置文件信息
Reader reader = Resources.getResourceAsReader(resources);
//2.创建SqlSessionFactor工厂对象
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
//3.创建SqlSession会话对象
SqlSession session = sqlMapper.openSession();
//4.通过sql语句的唯一标识执行sql语句
User user = session.selectOne("findById", 1);
System.out.println(user.getUname());
//5.释放资源
session.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
第二章:MyBatis的核心配置
一、mybatis的几个对象及其方法
1.SqlSessionFactoryBuilder的多个重载build()方法(主要有三种形式)。
- build(InputStream inputStream,String environment,Properties properties)
上述build()方法中,参数inputStream是字节流,它封装了XML文件形式的配置信息;
参数environment和参数properties;为可选参数。其中,参数environment决定将要加载的环境,包括数据源和事务管理器;参数properties决定将要加载的properties文件。
- build(Reader reader,String environment,Properties properties)
由上述build()方法可知,第二种形式的build()方法参数作用与第一种形式大体一致,
唯一不同的是,第一种形式的build()方法使用InputStream字节流封装了XML文件形式的配
置信息,而第二种形式的build()方法使用Reader字符流封装了Xml文件形式的配置信息。
- build(Configuration config)
通过以上代码可知,配置信息可以通过InputStream(字节流)、Reader(字符流)、Configuration(类)三种形式提供给SqlSessionFactoryBuilder的build()方法。无论通过哪种法,目的都是读入核心配置文件,然后创创建SqlSessionFactory对象。
SqlSessionFactory对象是线程安全的,它一旦被创建,在整个应用程序执行期间都会
存在。如果我们多次创建同一个数据库的SqlSessionFactory对象,那么该数据库的资源将
很容易被耗尽。通常每一个数据库都只创建一个SqlSession Factory对象,所以在构建
SqlSessionFactory对象时,建议使用单例模式。
2.SqlSessionFactory的openSession()方法。
-
SqlSession openSession(Boolean autoCommit)自动提交事务
-
openSession(ExecutorType execType)参数值
参数execType有三个可选值:
ExecutorType.SIMPLE:表示为每条语句创建一条新的预处理语句。
ExecutorType.REUSE:表示会复用预处理语句。
ExecutorType.BATCH:表示会批量执行所有更新语句。
3.SqlSession对象的作用:
SqlSession是MyBatis框架中另一个重要的对象,它是应用程序与持久层之间执行交
互操作的一个单线程对象,主要作用是执行持久化操作,类似于JDBC中的Connection。
SqlSession对象包含了执行SQL操作的方法,由于其底层封装了JDBC连接,所以可以直接
使用SqlSession对象来执行已映射的SQL语句。
4.SqlSession对象中常用方法
每一个线程都应该有一个自己的SqlSession对象,并且该对象不能共享。SqlSession
对象是线程不安全的,因此其使用范围最好在一次请求或一个方法中,绝不能将其放在类的静态字段、对象字段或任何类型的管理范围(如Servlet的HttpSession)中使用。SqlSession对象使用完之后,要及时的关闭,SqlSession对象通常放在finally块中关闭,代
码如下所示。
二、配置文件的主要元素
1.<properties>
如果想获取数据库的连接信息,可以在MyBatis的核心配置文件mybatis-config.xml中使
用
<properties resource="db.properties/>
引入db.properties文件后,如果希望动态获取db.properties文件中的数据库连接信息
可以使用
<dataSource type="POOLED">
<!-数据库驱动->
<property name="driver" value="${jdbc.driver}"/>
<!-连接数据库的url->
<property name="url" value="${jdbc.url}"/>
<!-连接数据库的用户名->
<property name="username" value="${jdbc.username}"/>
<!连接数据库的密码->
<property name="password" value="${jdbc.password}"/>
</dataSource>
完成上述配置后,
username和password)值将会由db.oroperties文件中对应的值来动态替换。这样一来,
2.<settings>
<settings>
<!--是否开启缓存,默认开启-->
<setting name="cacheEnabled" value="true"/>
<!--是否开启延迟加载,如果开启,所有关联对象都会延迟加载,默认不开启-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--是否开启关联对象属性的延迟加载,如果开启,对任意延迟属性的调用
都会用带有延迟加载属性的对象向完整加载,否则每种属性都按需加载-->
<setting name="aggresiveLazyLoading" value="true"/>
<!--开启自动驼峰命名规则映射,一般数据库的命名规则是两单词用下划线隔开,如
user_name,而Java则是首个单词首字母小写,气候所有单词首字母大写,如
userName,所以为解决两个字段名字不一致,要用驼峰映射-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
3.<typeAliases>
<select id="findById" resultType="com.xiaohuang.User">
select * from users where uid=#{id}
</select>
如上面代码,要写sql语句时得用resultType指定属性类所在位置,当包名很长是,写起来就很麻烦,所以要起别名。
<typeAliases>
<!--第一种配置方式-->
<typeAlias type="com.xiaohuang.User" alias="user"/>
</typeAliases>
当一个包下有很多属性类时,用上面的方法要给每一个类都起别名也很麻烦,可以用扫描包的形式起别名。
<typeAliases>
<!--第一种配置方式-->
<typeAlias type="com.xiaohuang.User" alias="user"/>
<!--第二种配置方式,扫包,默认实体的类名就是别名,大小写不区分-->
<package name="com.ju"/>
</typeAliases>
这个时候写sql语句时就可以用别名了
<select id="findById" resultType="User">
select * from users where uid=#{id}
</select>
除了可以使用
4.<typeHandlers>
5.<objectFactory>
6.<plugins>
7.<environments>
M小yBatisi可以配置多套运行环境,如开发环境、测试环境、生产环境等,我们可以灵活选择不同的配置,从而将SQL映射到不同运行环境的数据库中。不同的运行环境可以通过
MyBatis的运行环境信息包括事务管理器和数据源。在M小yBatis的核心配置文件中,MyBatisi通过
当有多个数据库环境时,采用哪个环境default属性值就设置为该环境的id值
在MyBatis中,
JDBC:此配置直接使用DBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务的作用域。
MANAGED:此配置不提交或回滚一个连接,而是让容器来管理事务的整个生命期。默认情况下,它会关闭连接,但可以将
:元素的closeConnection属性设置为false来阻止它默认的关闭行为。
项目中使用Spring+MyBatis,则没必要在MyBatis中配置事务管理器,实际开发中,
项目会使用Springl自带的管理器来实现事务管理。对于数据源的配置,MyBatisj提供了
UNPOOLED、POOLED和NDI三种数据源类型。
-
UNPOOLED表示数据源为无连接池类型。配置此数据源类型后,程序在每次被请求
时会打开和关闭数据库连接。UNPOOLED适用于对性能要求不高的简单应用程。
UNPOOLED类型的数据源需要配置5种属性。
-
POOLED表示数据源为连接池类型。POOLED数据源利用"池”的概念将DBC连接对象组织起来,节省了在创建新的连接对象时需要初始化和认证的时间。POOLED数据源使得并发Wb应用可以快速的响应请求,是当前比较流行的数据源配置类型。
-
JNDI表示数据源可以在EJB或应用服务器等容器中使用。
8.<mappers>
<mappers>
<!--第一种:映射文件全路径-->
<mapper resource="mapper/UserMapper.xml"/>
<!--第二种:映射文件绝对路径-->
<mapper url="file://D:....."/>
<!--第三种:接口全路径-->
<mapper class="com.xiaohuang.UserMapper"/>
<!--第四种:包扫描,扫描包下的所有接口-->
<package name="com.ju.mapper"/>
</mappers>
其中第三种需要创建一个接口代理,这种代理遵循规范:
- 1.映射文件的位置必须放在接口的同包目录下
- 2.映射文件名必须与接口同名
接口代理步骤:
1、定义与SQL映射文件同名的Mapper接口,并且将Mapper接口和SQL映射文件放在同
一目录下(在java下的包里再添加一个包,把接口放在包里, 然后再在resources下建一个
mapper包,把映射文件放到resoures下的mapper包里,注意名字和Java下的包相同,
用全限名建包,但不能用点,得用/)
2、设置SQL映射文件的namespace属性为Mapper接口全限名
<mapper namespace="com.xiaohuang.mapper.UserMapper">
3、在Mapper接口中定义方法,方法名就是SQL映射文件中sql语句的id,并保持类型和
返回值类型一致
public interface UserMapper {
User findById(int id);
}
4、编码:
(1)通过SqlSession的getMapper方法获取Mapper接口的代理对象
UserMapper userMapper = session.getMapper(UserMapper.class);
(2)调用该方法完成sql的执行
User user0 = userMapper.findById(4);
System.out.println(user0.getUname());
第三章:MyBatis映射文件
一、<mapper>
元素
1、namespace属性有两个作用:
用于区分不同的mapper,全局唯一。
绑定DAO接口,即面向接口编程。当namespace绑定某一接口之后,可以不用写该接口的实现类,MyBatis会通过接口的全限定名查找到对应的mapper配置来执行SQL语句,因此namespacel的命名必须跟接口同名。
namespace的值没有接口的时候可以随便起一个名字,有接口的时候就要写接口全限名
在不同的映射文件中,
二、<select>
元素
1、id:表示该select语句的唯一标识
2、parameterType:指定传入的参数的类型,可以省略,系统会自动判断。
3、resultType:指定查询返回的全限类名或别名,也就是用哪个类接受查询返回的结果。
4、resultMap:我们自定义结果集的映射,和resultType不能同时使用。
5、其他属性:
6、用法:
<select id="findById" resultType="User">
select * from users where uid=#{id}
</select>
三、<insert>
元素
1、id:该insert元素的唯一标识
2、parameterType:指定传入参数类型
3、keyProperty:将插入或更新操作返回值赋值给POJO类的某个属性值。
4、useGeneratedKeys:该属性会调用JDBC的getGeneratedkeys()方法获取由数据库内部产生的主键。默认值为false。
5、用法:
- 主键不是增的情况:
<insert id="addUser">
insert into users(uid,uname,uage) values (#{uid}, #{uname}, #{uage})
</insert>
User user2 = new User();
user2.setUage(20);
user2.setUname("王6");
user2.setUid(9);
session.insert("addUser", user2);
- 主键是自增的情况:
<insert id="addUser">
insert into users(uid,uname,uage) values (null, #{uname}, #{uage})
</insert>
User user2 = new User();
user2.setUage(20);
user2.setUname("王6");
session.insert("addUser", user2);
- 主键是自增且想获得主键返回值时:
<insert id="addUser" keyColumn="uid" useGeneratedKeys="true">
insert into users(uid,uname,uage) values (null, #{uname}, #{uage})
</insert>
User user2 = new User();
user2.setUage(20);
user2.setUname("王6");
session.insert("addUser", user2);
System.out.println(user2.getUid());
四、<update>
元素
- 不用获取返回键值:
<update id="updateUser">
update users set
uname = #{uname},
uage = #{uage}
where uid = #{uid}
</update>
User user4 = new User();
user4.setUname("张4");
user4.setUid(4);
session.update("updateUser", user4);
- 获取返回主键值:
<update id="updateUser" keyProperty="uid" useGeneratedKeys="true">
update users set
uname = #{uname},
uage = #{uage}
where uid = #{uid}
</update>
User user4 = new User();
user4.setUname("张4");
user4.setUid(4);
session.update("updateUser", user4);
System.out.println(user4.getUid());
五、<delete>
元素
用法:
<delete id="remove" >
delete from users where uid=${id}
</delete>
session.delete("remove", 4);
六、<sql>
元素
在一个映射文件中,通常需要定义多条SQL语句,这些SQL语句的组成可能有一部分是相同的(如多条select语句中都查询相同的id、username字段),如果每一个SQL语句都重写一遍相同的部分,势必会增加代码量。针对此问题,可以在映射文件中使用MyBatis所提供的
元素可以被静态地(在加载参数时)参数化,
1、如果数据库字段名和类字段名有些不一致,会导致无法获取数据,有三种方法:
~起别名:用as,缺点:不能执行操作*
<select id="selectAll" resultType="com.ju.User">
select id as uid, name as uname, age from users
</select>
~定义sql片段:用sql和include标签, 缺点:不能执行操*
<sql id="pianduan">
id as uid, name as uname, age as age
</sql>
<select id="selectAll" resultType="com.ju.User">
select <include refid="pianduan"/> from users
</select>
七、<resultMap>
元素
~高级映射:用resultMap标签:当数据库字段名和Java类字段名不一致且不满足驼峰映射时,可以用resultMap元素自定义映射。其中column时数据库的字段名,property时Java类的字段名。
<resultMap id="gaoji" type="com.ju.User"> //id随便起,type调用类名
<id column="id" property="uid"/> //主键字段用id
<result column="name" property="uname"/> //一般字段用result
</resultMap>
<select id="selectAll" resultMap=“gaoji”>
select * from users
</select>
第四章:动态SQL中的元素
一、if 元素
当条件成立时,会执行多个if选项
在test中进行条件判断,有多少if标签就执行多少if标签,其中1=1是为了统一if里的语法格式,保证当条件不成立是if中的and不会会导致语法错误。
<select id="findById" resultType="user">
select * from users where 1=1
<if test="uage != null and uage !=''">
and uage = #{uage}
</if>
<if test="uname != null and uname !=''">
and uname like concat('%',#{uname},'%')
</if>
</select>
User user =new User();
user.setUage(18);
user.setUname("李");
List<User> user1 = session.selectList
("com.xiaohuang.mapper.CustomerMapper.findById",user);
System.out.println(user1.get(0).getUname());
session.close();
二、choose、when、otherwise元素
从上往下判断,但其中一个条件成立时,就只执行该标签,然后结束,上面条件不成立或下面还没被检索到的标签都不会执行。
<select id="chooseTests" resultType="user">
select * from users where 1=1
<choose>
<when test="uname != null and uname !=''">
and uname like concat('%',#{uname},'%')
</when>
<when test="uage != null and uage !=''">
and uage = #{uage}
</when>
<otherwise>
and uid = #{uid}
</otherwise>
</choose>
</select>
User user =new User();
user.setUage(20);
user.setUname("李");
user.setUid(31);
List<User> user1 = session.selectList
("com.xiaohuang.mapper.CustomerMapper.chooseTests",user);
System.out.println(user1.get(0).getUname());
session.close();
三、where元素
where标签会删除标签内多余的and关键字,而且当条件成立时,会自动补上where,不成立时就不会补上where,所以就不用写where 1=1了
<select id="findById" resultType="user">
select * from users
<where>
<if test="uage != null and uage !=''">
and uage = #{uage}
</if>
<if test="uname != null and uname !=''">
and uname like concat('%',#{uname},'%')
</if>
</where>
</select>
四、trim元素
trim标签用于删除指定的多余关键字,可以实现where功能,有4个属性:
<select id="findById" resultType="user">
select * from users where
<trim prefix="where" prefixOverrides="and">
<if test="uage != null and uage !=''">
and uage = #{uage}
</if>
<if test="uname != null and uname !=''">
and uname like concat('%',#{uname},'%')
</if>
</trim>
</select>
五、set元素(更新操作)
可以进行更新操作,并把set标签内的多余的逗号去除
<update id="setTests" keyProperty="uid" useGeneratedKeys="true">
update users
<set>
<if test="uage != null and uage !=''">
uage = #{uage},
</if>
<if test="uname != null and uname !=''">
uname =#{uname},
</if>
</set>
where uid = #{uid}
</update>
User user =new User();
user.setUage(19);
user.setUname("小黄");
user.setUid(31);
session.update("com.xiaohuang.mapper.CustomerMapper.setTests",user);
System.out.println(user.getUid());
session.commit();//注意插入或更新都要提交事务
session.close();
六、foreach元素
常见属性:
-
若入参为单参数且参数类型是一个数组,collection属性值为array。
例如,要从数据表users中查询出id为1、2、3的客户信息,就可以利用数组作为参数,存储id的属性值1、2、3,并通过
元素迭代数组完成客户信息的批量查询操作。其中item名和#{名}要相同,它代表数组中每个元素的别名,collection="array"的array表示迭代的是数组类型,index可有可无。 <select id="foreachTest" resultType="user"> select * from users where uid in <foreach collection="array" item="ids" index="index" open="(" separator="," close=")"> #{ids} </foreach> </select>
List<User> user; Integer[] Id = {1,2,3}; user = session.selectList("com.xiaohuang.mapper. CustomerMapper.foreachTest",Id); for (User c:user) { System.out.println(c); } session.close();
-
若入参为单参数且参数类型是一个List,collection属性值为list。
例如,要从数据表users中查询出id为1、2、3的客户信息,就可以利用集合作为参数,存储id的属性值1、2、3,并通过
元素迭代集合完成客户信息的批量查询操作。其中item名和#{名}要相同,它代表集合中每个元素的别名,collection="list"的list表示迭代的是集合类型,index可有可无。 <select id="foreachTestList" resultType="user"> select * from users where uid in <foreach collection="list" item="ids" index="index" open="(" separator="," close=")"> #{ids} </foreach> </select>
List<User> user; List<Integer> Id = new ArrayList<Integer>(); Id.add(1); Id.add(2); Id.add(3); user = session.selectList("com.xiaohuang.mapper. CustomerMapper.foreachTestList",Id); for (User c:user) { System.out.println(c); } session.close();
-
若传入参数为多参数,就需要把参数封装为一个Map进行处理,collection属性值为Map。
知识拓展:
java中的map是一种依照键存储元素的容器。 在map中键可以是任意类型的对象。 map中不能有重复的键,每个键都有一个对应的值。
Map是一个集合,一种依照键(key)存储元素的容器,键(key)很像下标,在List中下标是整数。在Map中键(key)可以是任意类型的对象。Map中不能有重复的键(Key),每个键(key)都有一个对应的值(value)。
无论是selectOne()还是selectList(),除了指定sql语句的id外,只能传入一种类型的参数,当我们要传入多个参数时得把多个参数封装成一种类型也就是Map类型,在用这两种方法把Map传递进去。
<select id="foreachTestMap" resultType="user"> select * from users where uname = #{uname} or uid in <foreach collection="mapId" item="ids" index="index" open="(" separator="," close=")"> #{ids} </foreach> </select>
List<User> user; List<Integer> Id = new ArrayList<Integer>(); Id.add(1); Id.add(2); Id.add(3); Map<String,Object> map = new HashMap<String, Object>(); map.put("uname","小黄"); map.put("mapId",Id); user = session.selectList("com.xiaohuang.mapper.CustomerMapper.foreachTestMap",map); for (User c:user) { System.out.println(c); } session.close();
Map要指定键名和键值类型,一般都为Map<String,Object>,其中键名要和sql语句中接受参数名保持一致,如:map.put("uname","小黄")中的uname和#{uname}中的uname,map.put("mapId",Id)中的mapId和collection="mapId"中的mapId保持一致,item="ids"和#{ids}保持一致。
第五章:关联映射和缓存机制
一、关联映射
在关系型数据库中,表与表之间存在着三种关联映射关系,分别为一对一关系,一对多关系和多对多关系。
1、一对一
一个数据表中的一条记录最多可以和另一个数据表中的一条记录相关。例如,
现实生活中学生与校园卡就属于一对一的关系,一个学生只能拥有一张校园卡,一张校园卡只能属于一个学生。
一对一就是在本类中定义与之关联的类的对象作为属性,例如,A类中定义B类对象b作为属性,在B类中定义A类对象a作为属性。
在MyBatis中,通过
其中最常用的是property、column、javaType和select
- property:用于映射实体类名,和实体类字段一一对应
- column:用于映射数据库主表的字段名,于主表中字段名一一对应
- javaType:用于指定property对应的该字段类型
- select:用于引入查询子嵌套需要查询的sql语句id
两种用法
嵌套查询方式:
IdCardMapper.xml
<mapper namespace="com.xiaohuang.mapper.IdCardMapper">
<select id="idCard" resultType="IdCard">
select * from tb_idcard where id = #{id}
</select>
</mapper>
PersonMapper.xml
<mapper namespace="com.xiaohuang.mapper.PersonMapper">
<select id="person" resultMap="cardAndPerson">
select *from tb_person where id = #{id}
</select>
<resultMap id="cardAndPerson" type="Person">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="sex" property="sex"/>
<result column="age" property="age"/>
<association property="card" column="card_id"javaType="IdCard"
select="com.xiaohuang.mapper.IdCardMapper.idCard"/>
</resultMap>
</mapper>
两个实体类Person和IdCard
package com.xiaohuang.pojo;
public class Person {
private Integer id;
private String name;
private Integer age;
private String sex;
private IdCard card;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public IdCard getCard() {
return card;
}
public void setCard(IdCard card) {
this.card = card;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
", card=" + card +
'}';
}
}
package com.xiaohuang.pojo;
public class IdCard {
private Integer id; //身份证id
private String code; //身份证号码
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
@Override
public String toString() {
return "IdCard{" +
"id=" + id +
", code='" + code + '\'' +
'}';
}
}
//测试类
@Test
public void Test1() throws IOException {
Reader reader = Resources.getResourceAsReader(resources);
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sqlMapper.openSession();
Person person = session.selectOne("com.xiaohuang.mapper.PersonMapper.person",1);
System.out.println(person);
}
//结果
Person{id=1, name='Rose', age=22, sex='女', card=IdCard{id=1, code='152221198711020624'}}
Process finished with exit code 0
嵌套结果查询方式:主流的是用嵌套结果查询方式
PersonMapper.xml
<select id="person1" resultMap="cardAndPerson1">
select *from tb_person p,tb_idcard c
where p.id = #{id} and c.id = p.card_id
</select>
<resultMap id="cardAndPerson1" type="Person">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="sex" property="sex"/>
<result column="age" property="age"/>
<association property="card" javaType="IdCard">
<id property="id" column="id"/>
<result property="code" column="code"/>
</association>
</resultMap>
@Test
public void Test2() throws IOException {
Reader reader = Resources.getResourceAsReader(resources);
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sqlMapper.openSession();
Person person = session.selectOne("com.xiaohuang.mapper.PersonMapper.person1",2);
System.out.println(person);
session.close();
}
//结果
Person{id=2, name='Jack', age=23, sex='男', card=IdCard{id=2, code='152221198711020617'}}
Process finished with exit code 0
2、一对多
主键数据表中的一条记录可以和另外一个数据表的多条记录相关。但另外一个数据表中的记录只能与主键数据表中的某一条记录相关。例如,现实中班级与学生的关系就属于一对多的关系,一个班级可以有很多学生,但一个学生只能属于一个班级。
多对多就是一个A类对象对应多个B类对象的情况,例如,定义在A类中,定义一个B类对象的集合作为A类的属性;在B类中,定义A类对象a作为B类的属性。
在MyBatis中提供
#创建一个名称为tb_orders的表,同理
CREATE TABLE tb_orders(
id INT(32) PRIMARY KEY AUTO_INCREMENT,
number VARCHAR(32) NOT NULL,
user_id INT(32) NOT NULL,
FOREIGN KEY(user_id) REFERENCES tb_user(id)
);
INSERT INTO tb_orders VALUES('1','1000011','1');
INSERT INTO tb_orders VALUES('2','1000012','1');
INSERT INTO tb_orders VALUES('3','1000013','2');
#创建一个名称为tb user的表
CREATE TABLE tb_user(
id INT(32)PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(32),
address VARCHAR(256)
);
#插入3条数据,其他语句省略`tb_user`
INSERT INTO tb_user VALUES('1','小明','北京');
INSERT INTO tb_user VALUES('2','小黄','上海');
INSERT INTO tb_user VALUES('3','小朱','广州');
<mapper namespace="com.xiaohuang.mapper.TbUsersMapper">
<select id="TbUsers1" resultMap="TbUsersMap">
select u.*,o.id as order_id,o.number from tb_user u,tb_orders o
where o.user_id = u.id and u.id = #{id}
</select>
<resultMap id="TbUsersMap" type="TbUsers">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="address" column="address"/>
<collection property="tbOrdersList" ofType="TbOrders" javaType="list">
<id property="id" column="order_id"/>
<result property="number" column="number"/>
</collection>
</resultMap>
</mapper>
package com.xiaohuang.pojo;
import java.util.List;
public class TbUsers {
private Integer id;
private String username;
private String address;
private List<TbOrders> tbOrdersList;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public List<TbOrders> getTbOrdersList() {
return tbOrdersList;
}
public void setTbOrdersList(List<TbOrders> tbOrdersList) {
this.tbOrdersList = tbOrdersList;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "TbUsers{" +
"id=" + id +
", name='" + username + '\'' +
", address='" + address + '\'' +
", tbOrdersList=" + tbOrdersList +
'}';
}
}
package com.xiaohuang.pojo;
public class TbOrders {
private Integer id;
private String number;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
@Override
public String toString() {
return "TbOrders{" +
"id=" + id +
", number='" + number + '\'' +
'}';
}
}
String resources = "mybatis-config.xml";
@Test
public void Test1() throws IOException {
Reader reader = Resources.getResourceAsReader(resources);
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sqlMapper.openSession();
TbUsers users = session.selectOne("com.xiaohuang.mapper.TbUsersMapper.TbUsers1",1);
System.out.println(users);
session.close();
}
//执行结果
TbUsers{id=1, name='小明', address='北京', tbOrdersList=[TbOrders{id=1, number='1000011'}, TbOrders{id=2, number='1000012'}]}
Process finished with exit code 0
注意,当select放回多表查询的结果时,两个表中有字段名相同,应该给相同的字段名另起别名,如: select u.*,o.id as order_id,o.number,其中两个表中的id值相同,应该给其中一个另起别名,然后在
3、多对多
一个数据表中的一条记录可以与另外一个数据表任意数量的记录相关,另外一个数据表中的一条记录也可以与本数据表中任意数量的记录相关。例如,现实中学生与教师属于多对多的关系,一名学生可以由多名教师授课,一名教师可以为多名学生授课。
多对多就是在两个相互关联的类中,都可以定义多个与之关联的类的对象。例如,在A类中定义B类类型的集合作为属性,在B类中定义A类类型的集合作为属性。
和一对多差不多,只是借助了一个中间表。
#数据库准备
CREATE TABLE tb_product(
id INT(32) PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(32),
price DOUBLE
);
INSERT INTO tb_product VALUE('1','Java基础入门','44.5');
INSERT INTO tb_product VALUE('2','JavaWeb程序开发入门','38.5');
INSERT INTO tb_product VALUE('3','SSM框架整合实战','50.0');
CREATE TABLE tb_ordersitem(
id INT(32) PRIMARY KEY AUTO_INCREMENT,
orders_id INT(32),
product_id INT(32),
FOREIGN KEY(orders_id) REFERENCES tb_orders(id),
FOREIGN KEY(product_id) REFERENCES tb_product(id)
);
INSERT INTO tb_ordersitem VALUES('1','1`tb_product`','1');
INSERT INTO tb_ordersitem VALUES('2','1','3');
INSERT INTO tb_ordersitem VALUES('3','3','3');
<!--TbOrders.xml>
<mapper namespace="com.xiaohuang.mapper.TbOrders">
<select id="ordersfiId" resultMap="ordersMap">
SELECT o.*,p.id AS pid,p.name,p.price FROM
tb_orders o,tb_product p,tb_ordersitem m WHERE
o.id=#{id} AND m.orders_id=o.id AND p.id=m.product_id;
</select>
<resultMap id="ordersMap" type="TbOrders">
<id property="id" column="id"/>
<result property="number" column="number"/>
<collection property="productList" ofType="product">
<id property="id" column="pid"/>
<result property="name" column="name"/>
<result property="price" column="price"/>
</collection>
</resultMap>
</mapper>
// Product实体类
package com.xiaohuang.pojo;
import java.util.List;
public class Product {
private Integer id;
private String name;
private Double price;
private List<TbOrders> ordersList;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public List<TbOrders> getOrdersList() {
return ordersList;
}
public void setOrdersList(List<TbOrders> ordersList) {
this.ordersList = ordersList;
}
@Override
public String toString() {
return "Product{" +
"id=" + id +
", name='" + name + '\'' +
", price=" + price +
'}';
}
}
//TbOrders实体类
package com.xiaohuang.pojo;
import java.util.List;
public class TbOrders {
private Integer id;
private String number;
private List<Product> productList;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public List<Product> getProductList() {
return productList;
}
public void setProductList(List<Product> productList) {
this.productList = productList;
}
@Override
public String toString() {
return "TbOrders{" +
"id=" + id +
", number='" + number + '\'' +
", productList=" + productList +
'}';
}
}
//测试类
String resources = "mybatis-config.xml";
@Test
public void Tests1() throws IOException {
Reader reader = Resources.getResourceAsReader(resources);
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sqlMapper.openSession();
TbOrders users = session.selectOne("com.xiaohuang.mapper.TbOrders.ordersfiId",1);
System.out.println(users);
session.close();
}
//测试结果
TbOrders{id=1, number='1000011', productList=[Product{id=1, name='Java基础入门', price=44.5}, Product{id=3, name='SSM框架整合实战', price=50.0}]}
Process finished with exit code 0
二、Mybatis缓存机制
1、一级缓存:默认开启
MyBatis的一级缓存是SqlSession级别的缓存。如果同一个SqlSession对象多次执行完全相同的SQL语句时,在第一次执行完成后,MyBatis:会将查询结果写入到级缓存中,此后,如果程序没有执行插入、更新、删除操作,当第二次执行相同的查询语句时MyBatis会直接读取一级缓存中的数据,而不用再去数据库查询,从而提高了数据库的查询效率。(也就是得在同一个SqlSession环境下和没有进行插入、更新、删除操作时才能缓存)
例如,存在数据表tb bopk,从表中多次查询id为1的图书信息,当程序第一次查询id为1的图书信息时,程序会将查询结果写入MyBatis一级缓存,当程序第二次查询id为1的图书信息时,MyBatisj直接从一级缓存中读取,不再访问数据库进行查询。当程序对数据库执行了插入、更新、删除,甚至查询id为2的图书信息等操作,MyBatiss会清空一级缓存中的内容以防止程序误读。
2、二级缓存:手动开启
由一级缓存的内容可知,相同的Mapper类,相同的SQL语句,如果SqlSession不同,则两个SqlSession查询数据库时,会查询数据库两次,这样也会降低数据库的查询效率。为了解决这个问题,就需要用到MyBatis的二级缓存。MyBatis的二级缓存是Mappera级别的缓存,与一级缓存相比,二级缓存的范围更大,多个SqlSession可以共用二级缓存,并且二级缓存可以自定义缓存资源。
比如有SqlSession1和SqlSession2,当SqlSession1执行完查询后数据还没有存到二级缓存中,这时执行SqlSession2的查询还会从数据库中查询,只有当上一个SqlSession,也就是SqlSession1释放资源后数据才对上传到二级缓存中,执行SqlSession2的查询才会从二级缓存中读取数据。
手动开启步骤:
-
使用二级缓存前,需要在MyBatis的核心配置mybatis-config.xml文件中通过
元素开启二级缓存的全局配置。 <settings> <setting name="cacheEnabled"value="true"/> </settings>
-
开启当前Mappert的namespace下的二级缓存,可以通过MyBatisl映射文件中的
元素来完成(直接写 就行,如果要改默认特性,再在中间改)。 <mapper namespace="com.xiaohuang.mapper.TbOrders"> <cache></cache> <select>...</select> </mapper>
二级缓存可实现的功能:
- 映射文件中所有select语句将会被缓存。
- 映射文件中的所有insert、update和deletei语句都会刷新缓存。
- 缓存会使用LRU算法回收 ( 缓存数量有限,所以要回收旧缓存 )。
- 没有刷新间隔,缓存不会以任何时间顺序来刷新。
- 缓存会存储列表集合或对象的1024个引用。
- 缓存是可读/可写的缓存,这意味着对象检索不是共享的,缓存可以安全的被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
以上是二级缓存在默认状态下的特性,如果需要调整上述特性,可通过cache:元素的属性来实现。
缓存命中率:
终端用户访问缓存时,如果在缓存中查找到了要被访问的数据,就叫做命中。如果缓存中没有查找到要被访问的数据,就是没有命中。当多次执行查询操作时,缓
存命中次数与总的查询次数(缓存命中次数+缓存没有命中次数)的比,就叫作缓存命中率,即缓存命中率=缓存命中次数/总的查询次数。当MyBatisi开启二级缓存后,第一次查询数据时,由于数据还没有进入缓存,所以需要在数据库中查询而不是在缓存中查询,此时,缓存命中率为0。第一次查询过后,MyBatis会将查询到的数据写入缓存中,当第二次再查询相同的数据时,MyBatis:会直接从缓存中获取这条数据,缓存将命中,此时的缓存命中率为0.5(1/2)。当第三次查询相同的数据,则缓存命中率为0.66666(2/3),以此类推。
第六章:MyBatis的注解开发
MyBatis除了用.xml的映射方式外,还提供注解式开发进行关联映射,可以提高对单表操作效率,如果是一对一或一对对多注解开发就不如.xml映射方式效率高,而且可读性也没.xml映射方式高,因为一对一或对多是操作书写SQL语句的模式像.xml模式的嵌套查询模式,要把单条SQL拆分为多条对单表操作的SQL语句,比嵌套结果查询语句只用一条SQL步骤繁琐点,而且可读性低。
一、创建文件步骤
-
在main/java/com.xiaoguang/dao下新建一个接口,接口名随便,不过最好遵循XxxMapper格式
-
在mybatis-config.xml核心配置文件中的mappers元素下指定该接口路径,可以用类全路径,也可以用包扫描
-
在接口中写注解
public interface UserMapper {
@Select("select * from users where uid = #{id}")
User selectUser(int id); //方法名字随便起
} -
新建一个测试类,类名不限制,在类里创建测试方法,方法名不限制
public class UserMapperTest extends TestCase {
@Test
public void testSelectUser(){
}}
二、几种基本注解用法
1、@Select注解
@Select("select * from users where uid = #{id}")
User selectUser(int id);
@Test
public void testSelectUser() throws IOException {
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sqlMapper.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
User users = userMapper.selectUser(1);
System.out.println(users);
session.close();
}
2、@Insert注解
@Insert("insert into users(uid,uname,uage) values(#{uid},#{uname},#{uage})")
Integer insertUser(User user);
@Test
public void testInsertUser() throws IOException {
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sqlMapper.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
User users = new User();
users.setUname("小朱");
users.setUage(18);
Integer id = userMapper.insertUser(users);
System.out.println(id);
session.commit();
session.close();
}
3、@Update注解
@Update("update users set uname=#{uname},uage=#{uage} where uid=#{uid}")
Integer updateUser(User user);
@Test
public void testUpdatetUser() throws IOException {
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sqlMapper.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
User users = new User();
users.setUid(35);
users.setUname("竹林小溪");
users.setUage(19);
Integer id = userMapper.updateUser(users);
System.out.println(id);
session.commit();
session.close();
}
4、@Delete注解
@Delete("delete from users where uid=#{id}")
Integer deleteUser(int id);
@Test
public void testdeletetUser() throws IOException {
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sqlMapper.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
Integer id = userMapper.deleteUser(35);
System.out.println(id);
session.commit();
session.close();
}
5、@Param注解
@Update("update users set uid=#{id} where uid=#{idd}")
Integer paramUser(@Param("id")int id,@Param("idd")int idd);
@Test
public void testParamtUser() throws IOException {
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sqlMapper.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
Integer id = userMapper.paramUser(32,34);
System.out.println(id);
session.commit();
session.close();
}
三、基于注解的关联查询
1、一对一查询
要求:根据id查询tb_person表的个人信息和tb_idcard中的身份信息
-
CardMapper接口
public interface CardMapper { @Select("select * from tb_idcard where id=#{id}") IdCard selectCard(int id); }
-
PersonMapper接口
如果实体类字段和数据库字段相同,则可以省略普通字段的映射
public interface PersonMapper { @Select("select * from tb_person where id=#{id}") @Results({@Result(column = "card_id",property = "card", one = @One(select = "com.xiaohuang.dao.CardMapper.selectCard"))}) Person selectPerson(int id); }
也可以加上普通字段的映射
public interface PersonMapper { @Select("select * from tb_person where id=#{id}") @Results({ @Result(id=true,column = "id",property = "id"), @Result(column = "name",property = "name"), @Result(column = "age",property = "age"), @Result(column = "sex",property = "sex"), @Result(column = "card_id",property = "card", one = @One(select = "com.xiaohuang.dao.CardMapper.selectCard"))}) Person selectPerson(int id); }
-
测试方法
@Test public void selectPerson() throws IOException { Reader reader = Resources.getResourceAsReader("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); SqlSession sqlSession = sqlSessionFactory.openSession(); PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class); Person person = personMapper.selectPerson(1); System.out.println(person); sqlSession.close(); }
-
结果
Person{id=1, name='Rose', age=22, sex='女', card=IdCard{id=1, code='152221198711020624'}}
Process finished with exit code 0
2、一对多查询
要求:根据id查询tb_user表的个人信息和tb_orders中的订单信息
-
OrdersMapper接口
public interface OrdersMapper { @Select("select * from tb_orders where user_id=#{id}") TbOrders selectOrders(int id); }
-
UsersMapper接口
public interface UsersMapper { @Select("select * from tb_user where id=#{id}") @Results({ @Result(id=true,column = "id",property = "id"), @Result(column = "id",property = "tbOrdersList", many = @Many(select = "com.xiaohuang.dao.OrdersMapper.selectOrders"))}) TbUsers selectUser(int id); }
-
测试方法
@Test public void UserAndOrders() throws IOException { Reader reader = Resources.getResourceAsReader("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); SqlSession sqlSession = sqlSessionFactory.openSession(); UsersMapper usersmapper = sqlSession.getMapper(UsersMapper.class); TbUsers tbUsers = usersmapper.selectUser(1); System.out.println(tbUsers); sqlSession.close(); }
-
结果
TbUsers{id=1, name='小明', address='北京', tbOrdersList=[TbOrders{id=1, number='1000011', productList=null}, TbOrders{id=2, number='1000012', productList=null}]}
Process finished with exit code 0
3、多对多查询
要求:根据id查询tb_person表的个人信息和tb_idcard中的身份信息
-
ProductMapper接口
public interface ProductMapper { @Select("select * from tb_product where id in" + "(select product_id from tb_ordersitem where orders_id=#{id})") Product selectProduct(int id); }
-
PersonMapper接口
public interface OrdersMapper { @Select("select * from tb_orders where id=#{id}") @Results({ @Result(id = true,column = "id",property = "id"), @Result(column = "id",property = "productList", many = @Many(select = "com.xiaohuang.dao.ProductMapper.selectProduct")) }) TbOrders selectOrdersAndProduct(int id); }
-
测试方法
@Test public void ProductAndOrders() throws IOException { Reader reader = Resources.getResourceAsReader("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); SqlSession sqlSession = sqlSessionFactory.openSession(); OrdersMapper ordersMapper = sqlSession.getMapper(OrdersMapper.class); TbOrders tbOrders = ordersMapper.selectOrdersAndProduct(1); System.out.println(tbOrders); sqlSession.close(); }
-
结果
TbOrders{id=1, number='1000011', productList=[Product{id=1, name='Java基础入门', price=44.5}, Product{id=3, name='SSM框架整合实战', price=50.0}]}
Process finished with exit code 0
完结
小技巧
-
在编辑器中右击,选择generator,选中想要生成get、set方法的属性,点击完成即可自动生成get和set方法。快捷键为:alt+insert
-
public String toString()是Object里面已经有了的方法,而所有类都是继承Object,所以“所有对象都有这个方法”。它通常只是为了方便输出,比如System.out.println(xx),括号里面的“xx”如果不是String类型的话,就自动调用xx的toString()方法。
-
当sql需要接受多个参数时,则当传入一个类时,sql中接受参数名要和类中的字段名一一对应,当传入一个Map容器时,sql中接受的参数名要和Map容器中的键值一一对应。
-
使用控制台日志形输出方法配置步骤:
1、到pom.xml文件添加日志依赖
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.12</version> </dependency>
2、在resources文件下新建一个log4j.properties文件,文件内容为
#全局日志配置 log4j.rootLogger=DEBUG,Console #控制台输出配置 log4j.appender.Console=org.apache.log4j.ConsoleAppender log4j.appender.Console.layout=org.apache.log4j.PatternLayout log4j.appender.Console.layout.ConversionPattern=%d[%t] %-5p[%c]-%m%n #日志输出级别,只展示了一个 log4j.logger.java.sql.PreparedStatement=DEBUG