Mybaits
官方的帮助文档:https://mybatis.org/mybatis-3/zh/sqlmap-xml.html
1、创建数据库
使用mysql创建
CREATE DATABASE mybaits;
use mybaits
CREATE TABLE user(
id INT(20) NOT NULL PRIMARY KEY,
name VARCHAR(30) DEFAULT NULL,
pwd VARCHAR(30) DEFAULT NULL
)ENGINE=INNODB DEFAULT CHARSET=utf8;
插入语句:
INSERT into user(id,name,pwd) VALUES(1,'曹操','123456');
INSERT into user(id,name,pwd) VALUES(2,'刘备','123456');
INSERT into user(id,name,pwd) VALUES(3,'孙权','123456');
2、第一个mybaits程序
2.1、搭建环境
使用idea创建maven工程
删除src目录(创建子模块)
导入:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.5</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
2.2、新建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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?userSSL=true&userUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</dataSource>
</environment>
</environments>
</configuration>
2.3、新建mybatis工具类
//使用sqlSessionFactory 获取sqlsession,sqlsession就可以进行数据库操作
//sqlSessionFactory则是从resource中获取到的
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
2.4、新建dao、entity和mapper
mapper.xml:
id是dao接口层的方法名
resulttype是返回的类型填写User的路径
<?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/Mapper接口-->
<mapper namespace="com.zhang.dao.UserDao">
<select id="getAllUser" resultType="com.zhang.entity.User">
select * from mybatis.user;
</select>
</mapper>
2.5、测试
public class UserTest {
@Test
public void getAllUserTest(){
//第一步获取sqlsession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//执行sql(方式一)
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> allUser = userDao.getAllUser();
for (User user : allUser) {
System.out.println(user);
}
}
}
遇到的两个报错信息:
问题:
org.apache.ibatis.binding.BindingException: Type interface com.zhang.dao.UserDao is not known to the MapperRegistry.
原因:
当mapper存在java文件夹下时,没有在mybatis配置中心进行配置
解决:
在mybatis-config.xml中加入
<mappers>
<mapper resource="com/zhang/mapper/UserMapper.xml"/>
resource是Mapper所在路径
</mappers>
问题:
java.io.IOException: Could not find resource com/xxx/xxxMapper.xml
原因:
当Mapper.xml放在java文件下时,mybatis 找不到映射器xml文件
解决:
在pom文件下添加资源映射
<build>
<resources>
<resource>
<directory>src/main/Java</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.* </include>
</includes>
</resource>
</resources>
</build>
表示java下除了.java文件和resources中所有文件均可以被mybatis找到
3、CRUD
3.1、按ID查询
<mapper namespace="com.zhang.dao.UserDao">
<select id="getUserById" resultType="com.zhang.entity.User" parameterType="int">
select * from user where id=#{id};
</select>
</mapper>
namespace是对应的UserDao路径
id是对应UserDao的接口名称
resultType是返回值类型
parameterType是参数类型
3.2、更新
<update id="updateUser" parameterType="com.zhang.entity.User">
update user set name=#{name},pwd=#{pwd} where id=#{id};
</update>
3.3、插入
<insert id="insertUser" parameterType="com.zhang.entity.User">
insert into user(id, name, pwd) values (#{id},#{name},#{pwd});
</insert>
3.4、删除
<delete id="deleteUser" parameterType="int">
delete from user where id=#{id};
</delete>
注意:
除了查询其他操作都需要sqlsession提交事务
sqlSession.commit();
更新插入和删除都可以返回一个int类型,判断其是否执行成功
3.5、使用Map作为参数
有时在项目中实体的属性有多个,使用其作为参数在创建和传值不方便时,就可以使用Map进行传值
Map<String,Object> m = new HashMap<String,Object>();
m.put("id",1);
m.put("name",曹操);
在xml中可以这么写
<select id="getUserById" resultType="com.zhang.entity.User" parameterType="map">
select * from user where id=#{id} and name=#{name};
</select>
Map传递参数,直接在sql中取出key来
当只有一个基本类型的参数的时候,可以直接在sql中取出不需要使用parameterType=“map”
4、配置解析
4.1、核心配置文件
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
4.2、环境配置
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?userSSL=true&userUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?userSSL=true&userUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</dataSource>
</environment>
</environments>
其中的default就可以修改为development或test
事务管理器(transactionManager)
transactionManager中的type可以是JDBC或者是MANAGED
数据源(dataSource)
dataSource中的type可以是[UNPOOLED|POOLED|JNDI]
UNPOOLED表示无池连接,既每次使用都要连接和关闭
POOLED表示有池,每次连接用完后会等待下次连接,一般都会使用POOLED
4.3、属性配置
属性可以在外部配置进行动态的替换
driver=com.mysql.jdbc.Driver
url=jdbc:=mysql://localhost:3306/mybatis?userSSL=true&userUnicode=true&characterEncoding=UTF-8
username=root
password=
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="password" value="123456"/>
</properties>
从properties中读取信息
<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>
使用properties可以从外部读取信息,也可以自己声明变量使用
当外部文件和内部声明的变量相同时以外部文件为主
注意:一定要在的上方
(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)".
具体应该符合上面的顺序
4.3、别名配置
在上面mapper.xml中resultType="com.zhang.entity.User"和parameterType="com.zhang.entity.User"这样都比较冗余
可以使用别名将其变短
方式一:指定某个类对其起别名
<typeAliases>
<typeAlias alias="User" type="com.zhang.entity.User"/>
</typeAliases>
在mapper.xml中即可使用User别名
方式二:对这个包下的类起别名,使用时大写小写均可
<typeAliases>
<package name="com.zhang.entity"/>
</typeAliases>
<select id="getAllUser" resultType="User">
方式二也可通过加注解的方式对实体类取别名
@Alias("hello")
public class User implements Serializable {
<select id="getAllUser" resultType="hello">
在使用时就可以写做hello
当实体类较少的时候建议使用方式一,实体类较多的时候建议使用方式二
4.4、映射器配置
方式一:【推荐使用】
Mybaits
1、创建数据库
使用mysql创建
CREATE DATABASE mybaits;
use mybaits
CREATE TABLE user(
id INT(20) NOT NULL PRIMARY KEY,
name VARCHAR(30) DEFAULT NULL,
pwd VARCHAR(30) DEFAULT NULL
)ENGINE=INNODB DEFAULT CHARSET=utf8;
插入语句:
INSERT into user(id,name,pwd) VALUES(1,'曹操','123456');
INSERT into user(id,name,pwd) VALUES(2,'刘备','123456');
INSERT into user(id,name,pwd) VALUES(3,'孙权','123456');
2、第一个mybaits程序
2.1、搭建环境
使用idea创建maven工程
删除src目录(创建子模块)
导入:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.5</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
2.2、新建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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?userSSL=true&userUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</dataSource>
</environment>
</environments>
</configuration>
2.3、新建mybatis工具类
//使用sqlSessionFactory 获取sqlsession,sqlsession就可以进行数据库操作
//sqlSessionFactory则是从resource中获取到的
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
2.4、新建dao、entity和mapper
mapper.xml:
id是dao接口层的方法名
resulttype是返回的类型填写User的路径
<?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/Mapper接口-->
<mapper namespace="com.zhang.dao.UserDao">
<select id="getAllUser" resultType="com.zhang.entity.User">
select * from mybatis.user;
</select>
</mapper>
2.5、测试
public class UserTest {
@Test
public void getAllUserTest(){
//第一步获取sqlsession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//执行sql(方式一)
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> allUser = userDao.getAllUser();
for (User user : allUser) {
System.out.println(user);
}
}
}
遇到的两个报错信息:
问题:
org.apache.ibatis.binding.BindingException: Type interface com.zhang.dao.UserDao is not known to the MapperRegistry.
原因:
当mapper存在java文件夹下时,没有在mybatis配置中心进行配置
解决:
在mybatis-config.xml中加入
<mappers>
<mapper resource="com/zhang/mapper/UserMapper.xml"/>
resource是Mapper所在路径
</mappers>
问题:
java.io.IOException: Could not find resource com/xxx/xxxMapper.xml
原因:
当Mapper.xml放在java文件下时,mybatis 找不到映射器xml文件
解决:
在pom文件下添加资源映射
<build>
<resources>
<resource>
<directory>src/main/Java</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.* </include>
</includes>
</resource>
</resources>
</build>
表示java下除了.java文件和resources中所有文件均可以被mybatis找到
3、CRUD
3.1、按ID查询
<mapper namespace="com.zhang.dao.UserDao">
<select id="getUserById" resultType="com.zhang.entity.User" parameterType="int">
select * from user where id=#{id};
</select>
</mapper>
namespace是对应的UserDao路径
id是对应UserDao的接口名称
resultType是返回值类型
parameterType是参数类型
3.2、更新
<update id="updateUser" parameterType="com.zhang.entity.User">
update user set name=#{name},pwd=#{pwd} where id=#{id};
</update>
3.3、插入
<insert id="insertUser" parameterType="com.zhang.entity.User">
insert into user(id, name, pwd) values (#{id},#{name},#{pwd});
</insert>
3.4、删除
<delete id="deleteUser" parameterType="int">
delete from user where id=#{id};
</delete>
注意:
除了查询其他操作都需要sqlsession提交事务
sqlSession.commit();
更新插入和删除都可以返回一个int类型,判断其是否执行成功
3.5、使用Map作为参数
有时在项目中实体的属性有多个,使用其作为参数在创建和传值不方便时,就可以使用Map进行传值
Map<String,Object> m = new HashMap<String,Object>();
m.put("id",1);
m.put("name",曹操);
在xml中可以这么写
<select id="getUserById" resultType="com.zhang.entity.User" parameterType="map">
select * from user where id=#{id} and name=#{name};
</select>
Map传递参数,直接在sql中取出key来
当只有一个基本类型的参数的时候,可以直接在sql中取出不需要使用parameterType=“map”
4、配置解析
4.1、核心配置文件
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
4.2、环境配置
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?userSSL=true&userUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?userSSL=true&userUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</dataSource>
</environment>
</environments>
其中的default就可以修改为development或test
事务管理器(transactionManager)
transactionManager中的type可以是JDBC或者是MANAGED
数据源(dataSource)
dataSource中的type可以是[UNPOOLED|POOLED|JNDI]
UNPOOLED表示无池连接,既每次使用都要连接和关闭
POOLED表示有池,每次连接用完后会等待下次连接,一般都会使用POOLED
4.3、属性配置
属性可以在外部配置进行动态的替换
driver=com.mysql.jdbc.Driver
url=jdbc:=mysql://localhost:3306/mybatis?userSSL=true&userUnicode=true&characterEncoding=UTF-8
username=root
password=
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="password" value="123456"/>
</properties>
从properties中读取信息
<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>
使用properties可以从外部读取信息,也可以自己声明变量使用
当外部文件和内部声明的变量相同时以外部文件为主
注意:一定要在的上方
(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)".
具体应该符合上面的顺序
4.3、别名配置
在上面mapper.xml中resultType="com.zhang.entity.User"和parameterType="com.zhang.entity.User"这样都比较冗余
可以使用别名将其变短
方式一:指定某个类对其起别名
<typeAliases>
<typeAlias alias="User" type="com.zhang.entity.User"/>
</typeAliases>
在mapper.xml中即可使用User别名
方式二:对这个包下的类起别名,使用时大写小写均可
<typeAliases>
<package name="com.zhang.entity"/>
</typeAliases>
<select id="getAllUser" resultType="User">
方式二也可通过加注解的方式对实体类取别名
@Alias("hello")
public class User implements Serializable {
<select id="getAllUser" resultType="hello">
在使用时就可以写做hello
当实体类较少的时候建议使用方式一,实体类较多的时候建议使用方式二
4.4、映射器配置
我们需要告诉 MyBatis 到哪里去找到这些语句,最好的办法是直接告诉 MyBatis 到哪里去找映射文件
方式一:【推荐使用】相对于类路径的资源引用
<mappers>
<mapper resource="com/zhang/mapper/UserMapper.xml"/>
</mappers>
方式二:类名
<mappers>
<!-- <mapper resource="com/zhang/mapper/UserMapper.xml"/>-->
<mapper class="com.zhang.UserDao"/>
</mappers>
问题:当UserDao不与UserMapper同名时找不到
当UserDao不与UserMapper再同一文件夹下找不到
方式三:包名
<mappers>
<!-- <mapper resource="com/zhang/mapper/UserMapper.xml"/>-->
<!-- <mapper class="com.zhang.dao.UserDao"/>-->
<package name="com.zhang.dao"/>
</mappers>
问题:当UserDao不与UserMapper同名时找不到
当UserDao不与UserMapper再同一文件夹下找不到
4.5、生命周期及作用域
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pUmRvT0K-1586163754296)(C:\Users\广远\AppData\Roaming\Typora\typora-user-images\image-20200401153120608.png)]
SqlSessionFactoryBuilder
一旦创建了 SqlSessionFactory,就不再需要它了
所以最佳的作用域就是局部变量
SqlSessionFactory
可以想象成数据库连接池
一旦被创建就一直存在,不需要再去创建一个新的实例
因此 SqlSessionFactory 的最佳作用域是应用作用域(整个应用)
SqlSession
每个Sqlsession就是向数据库的一次请求
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域
每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它
5、ResultMap结果映射
5.1、问题:
当User中的属性与数据库中的字段对应不上的时候怎么办
原User:
public class User implements Serializable {
private Integer id;
private String name;
private String pwd;
现User
public class User implements Serializable {
private Integer id;
private String name;
private String password;
数据库中的字段是:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cludzAzk-1586163754298)(C:\Users\广远\AppData\Roaming\Typora\typora-user-images\image-20200401161332939.png)]
5.2、解决方法:
-
在sql语句中使用别名
既修改sql语句为
select id,name,pwd as password from user where id=#{id};
-
使用ResultMap
<resultMap id="UserMap" type="User"> <result property="password" column="pwd"/> </resultMap> <select id="getUserById" resultMap="UserMap" parameterType="int"> select id,name,pwd from user where id=#{id}; </select> 1、将返回类型设置为自定义的resultMap 2、建立resultMap,id是其名字,type是对应的实体类 3、可以在其中设置多对一,一对多的关系或者是简单的reslut关系
6、日志
6.1、mybatis中的日志实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WjwM73fw-1586163754299)(C:\Users\广远\AppData\Roaming\Typora\typora-user-images\image-20200401192248747.png)]
可以在mybatis配置文件中进行设置使用什么日志记录
STDOUT_LOGGING是控制台实现可以直接使用,其他则是需要导包
测试使用STDOUT_LOGGING:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QwXvCoJY-1586163754301)(C:\Users\广远\AppData\Roaming\Typora\typora-user-images\image-20200401192725209.png)]
分别的意思是:
- 打开JDBC连接
- 创建一个连接对象
- 设置自动提交为false
- 执行的sql语句
- 传递的参数
- 查询的列
- 得到的结果
- 结果总数
- 打印结果
- 结果集自动设置
- 关闭connection对象
- 将connection对象返回到连接池
6.2、log4j
如何在mybatis中使用log4j
- 在maven中导入log4j的包
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
-
在mybatis配置文件中设置
<settings> <setting name="logImpl" value="LOG4J"/> </settings>
-
写入log4j配置文件
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/zhang.log 指定文件位置
log4j.appender.file.MaxFileSize=10mb 最大10mb
log4j.appender.file.Threshold=DEBUG 最低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
-
在测试中使用log4j
static Logger logger = Logger.getLogger(UserTest.class); 在要使用的类中添加logger静态变量
log4j.rootLogger = level,appenderName1,appenderName2, .....
level是日志记录的优先级,分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL
或者自定义的级别,默认优先级:`ALL < DEBUG < INFO
Log4j建议只使用四个级别,优先级从高到低分别是ERROR、WARN、INFO、DEBUG
appenderName则是日志的输出位置
7、注解的形式
-
在接口上直接添加注释即可
//通过ID获取用户 @Select("select * from user where id = #{id}") User getUserById(int id);
优点:相较于xml实现较为简单,并且更符合面向对象编程
缺点:在一些复杂的环境下没有xml好用(多表查询)
增删改查均类似
注解主要依靠反射来实现
8、**Mybatis执行过程
主要通过源码进行分析
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
这是MybatisUtils中的代码
- 首先我们使用Resources获取配置文件信息,返回一个流
- new一个SqlSessionFactoryBuilder来build这个配置文件流
- 最后返回一个sqlSessionFactory
查看.build(inputStream);的源码:
public SqlSessionFactory build(InputStream inputStream) {
return this.build((InputStream)inputStream, (String)null, (Properties)null);
}
查看build方法具体实现--》
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
SqlSessionFactory var5;
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
var5 = this.build(parser.parse());
} catch (Exception var14) {
throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException var13) {
}
}
return var5;
}
注意:XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
它使用了XMLConfigBuilder来解析整个配置文件获取配置文件信息
最后调用
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
返回一个SqlSessionFactory
再来查看Mapper方法的具体实现
public void getUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
User userById = userDao.getUserById(1);
System.out.println(userById);
sqlSession.close();
}
通过debug这段代码可以得知
sqlSession存储这config中所有的信息并且其中有事务transaction,和执行器executor
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JKF5MYqX-1586163754302)(C:\Users\广远\AppData\Roaming\Typora\typora-user-images\image-20200402124548893.png)]
9、多对一查询
9.1、搭建环境
在数据库mybatis中执行查询语句:
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')
创建学生和老师表,学生的tid是外键对应老师表中id
在项目中创建dao、entity、mapper
实体类
import lombok.Data;
@Data
public class Student {
private Integer id;
private String name;
private Teacher teacher;
}
import lombok.Data;
@Data
public class Teacher {
private Integer id;
private String name;
}
@Date是使用lombok插件,在idea中下载并且在maven中导入相关包即可使用
作用是减少实体类的代码,使用了@Date,就不用写get、set等方法
具体的mapper.xml中代码
9.2、实现代码
<!-- 方式一,在association中再查一次-->
<resultMap id="StudentTeacher" type="Student">
<!-- 关联一个类的时候使用association-->
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getStudents" resultMap="StudentTeacher">
select * from student
</select>
<select id="getTeacher" resultType="Teacher" >
select * from teacher where id=#{id}
</select>
<resultMap id="StudentTeacher2" type="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
</association>
</resultMap>
<select id="getStudents2" resultMap="StudentTeacher2">
select s.id sid,s.name sname,t.name tname from student s inner join teacher t on s.tid = t.id
</select>
这是两种不同方式实现多对一的查询
方式一类似于sql的子查询
property是学生类中老师属性
column是对应的学生表中字段名称java
javaType是老师类
select的意思是使用tid执行下面的代码
select * from teacher where id=#{id}
从而查询出学生的所有信息
方式二是多表查询
property是学生类中老师属性
javaType是老师类
其中的result 则是表明老师表中的name对应的是sql语句中的tname
10、一对多查询
整体和上面一样实体类进行改变a
@Data
public class Student {
private Integer id;
private String name;
private Integer tid;
}
@Data
public class Teacher {
private Integer id;
private String name;
private List<Student> students;
}
由于一个老师可以有多个学生,而一个学生对应一个老师,所以在老师中写入List就是一对多的查询
TeacherDao中写入接口
public interface TeacherDao {
List<Teacher> getTeachers();
}
TeacherMapper中则这样写
<mapper namespace="com.zhang.dao.TeacherDao">
<select id="getTeachers" resultMap="TeacherStudent">
select t.id tid,t.name tname,s.id sid,s.name sname
from teacher t inner join student s on t.id = s.tid;
</select>
<resultMap id="TeacherStudent" type="Teacher">
<!--复杂的属性我们需要单独处理 对象:association 集合:collection
javaType是指定的属性的类型
集合中的泛型信息,使用ofType获取
-->
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
</collection>
</resultMap>
</mapper>
注意为什么一对多和多对一使用的resultMap不同,因为在多对一中,是使用学生类对老师进行存储,学生对应一个老师所以是一个对象使用association,而一对多中老师中存储这学生集合,所以应该写的是collection
一个对象可以用javaType而一个集合则使用ofType
在这里我只写出多表查询的方式,因为子查询的方法个人觉得过于繁琐
11、动态sql
通常在写查询或者更新语句的时候总会用到多个条件去判断或者直接查询全部
但是我们写多个条件的时候如果想要实现类似java重载的效果就需要写很多个大同小异的语句
在这里我们就可以使用动态sql让一个sql根据参数的不同执行出不同的结果
11.1、if条件判断
首先搭建环境:
CREATE TABLE `blog`(
`id` VARCHAR(50) NOT NULL COMMENT '博客id',
`title` VARCHAR(100) NOT NULL COMMENT '博客标题',
`author` VARCHAR(30) NOT NULL COMMENT '博客作者',
`create_time` DATETIME NOT NULL COMMENT '创建时间',
`views` INT(30) NOT NULL COMMENT '浏览量'
)ENGINE=INNODB DEFAULT CHARSET=utf8
创建一个blog表
在项目中创建相关的class
@Data
public class Blog {
private String id;
private String title;
private String author;
private Date createTime;
private Integer views;
}
public interface BlogDao {
void insertBlog(Blog blog);
List<Blog> checkBlog(Map map);
List<Blog> checkBlogChoose(Map map);
}
在test中编写代码insert到表中两个数据
06336909067f4f80b95aca2adfb59333 赵云七进七出 三国说 2020-04-02 20:36:13 1000
0d8cfc11be5841a98503a5a0ca4b6312 关羽败走麦城 三国说 2020-04-02 20:44:17 1000
编写查询类
@Test
public void checkBlog() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogDao blogDao = sqlSession.getMapper(BlogDao.class);
Map map = new HashMap<>();
// map.put("title","赵云七进七出");
map.put("author","三国说");
List<Blog> blogs = blogDao.checkBlog(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
mapper中的代码:
<select id="checkBlog" parameterType="map" resultType="blog">
select * from blog
<where>
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
</select>
-
通过map可以传递任何自己想要判断的数据
-
的作用是当第一个判断不成立,第二个判断成立的时候不会产生
select * from blog where and author = ?
这样的代码,where标签会自动判断需不需要该and
11.2、Choose判断
@Test
public void checkBlogChoose() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogDao blogDao = sqlSession.getMapper(BlogDao.class);
Map map = new HashMap<>();
map.put("title","赵云七进七出");
map.put("author","三国说");
map.put("views",1000);
List<Blog> blogs = blogDao.checkBlogChoose(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
<select id="checkBlogChoose" parameterType="map" resultType="blog">
select * from blog
<where>
<choose>
<when test="title != null">
title = #{title}
</when>
<when test="author!=null">
and author =#{author}
</when>
<otherwise>
and views = #{views}
</otherwise>
</choose>
</where>
</select>
- Choose的作用是类似于java中的swtich从中选择一个条件成立
- 当有多个条件成立的时候会使用第一个条件,这也是和IF最大的不同
- 标签则是当前面都不成立的时候使用它
11.3forEach语句
foreach语句可以帮我们省下写一些重复的sql片段
比如
select * from blog where (id = 1 or id = 2 or id = 3)
在foreach中就可以这样使用
<select id="checkBlogForEach" parameterType="map" resultType="blog">
select * from blog
<where>
<foreach collection="ids" item="id" open="(" close=")" separator="or">
id = #{id}
</foreach>
</where>
</select>
-
collection=“ids”,ids是一个集合,代表的就是想要查询的id集合
-
item=“id” ,id是ids中的每一项
-
open是代码以(开头,close是以)结尾,一般是使用()
-
separator则是使用什么来分割每个id
test中的代码:
@Test public void checkBlogForEach(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogDao blogDao = sqlSession.getMapper(BlogDao.class); Map map = new HashMap<>(); List<String> ids = new ArrayList<>(); ids.add("06336909067f4f80b95aca2adfb59333"); map.put("ids",ids); List<Blog> blogs = blogDao.checkBlogForEach(map); for (Blog blog : blogs) { System.out.println(blog); } sqlSession.close(); }
动态sql本质就是根据不同条件生成不同的sql
12、缓存
12.1、简介
缓存:当我们查询出来的东西想要过一段时间再用,不用重新连接数据库再进行查询,我们就可以把数据放到缓存中,方便下次直接使用,缓存能大大提高我们的效率,因为很多情况下我们都是在做重复的查询
什么样的数据可以使用缓存:
- 经常查询并且不经常改变的数据
Mybatis中的缓存
默认Mybatis中使用两种缓存:一级缓存和二级缓存
- 默认情况下,只有一级缓存开启(sqlSession级别的缓存,又叫本地缓存)
- 二级缓存需要手动开启,他是基于namespace的缓存
- 为了提高扩展性,Mybatis定义了缓存接口Cache。我们可以通过实现Cache来自定义耳机缓存
12.2、一级缓存
在一次连接中,连续使用了两次相同的查询方法就会使用缓存
@Test
public void getUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
User userById = userDao.getUserById(1);
System.out.println(userById);
System.out.println("---------------------");
//sqlSession.clearCache();//清理缓存
User userById1 = userDao.getUserById(1);
System.out.println(userById1);
sqlSession.close();
}
设置中打开控制台日志,当运行了测试后就会发现两次sql调用只显示了一条语句,就表明第二次查询查询到的是缓存中的东西
有几种方式不会使用一级缓存:
- 两次查询不同的数据
- 两次查询之间,调用了增删改(当调用了增删改以后,数据库中的数据可能会改变所以缓存会失效,即使增删改的内容和自己查询的内容没有任何关联)
- 两次查询之间,清理了缓存(比如上面的代码将sqlSession.clearCache()取消注释后就是将一级缓存清空,第二次查询就依旧需要到数据库中查询)
12.3、二级缓存
二级缓存也叫全局缓存,一级缓存的作用域太低,就需要二级缓存
基于namespace的缓存,一个名称空间对应一个二级缓存
工作机制
- 一个会话查询一个数据就会被放在一级缓存中
- 如果当前会话被关闭了,这个会话的一级缓存就消失了,开启二级缓存后,一级缓存消息就会将数据保存在二级缓存中
- 新的会话查询信息就可以从二级缓存中获取内容
如何使用二级缓存:
-
在mybatis配置文件中开始二级缓存
<setting name="cacheEnabled" value="true"/>
-
在要使用二级缓存的mapper中加上
<cache/>
当然也可以定义一些缓存的属性
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
eviction代表清除策略:
LRU
– 最近最少使用:移除最长时间不被使用的对象。FIFO
– 先进先出:按对象进入缓存的顺序来移除它们。SOFT
– 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK
– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
flushInterval是每隔多久刷新一遍缓存单位是毫秒
size表示最多可以存储结果对象或列表的 512 个引用
测试代码:
@Test
public void getUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
User userById = userDao.getUserById(1);
System.out.println(userById);
System.out.println("---------------------");
sqlSession.close();
SqlSession sqlSession1 = MybatisUtils.getSqlSession();
UserDao userDao1 = sqlSession1.getMapper(UserDao.class);
User userById1 = userDao1.getUserById(1);
System.out.println(userById1);
sqlSession1.close();
}
开启两个sqlsession
在第二个sqlsession调用以前第一个sqlSession查询完并关闭了,使用第二个sqlsession就会读取二级缓存
注意必须要第一个sqlSession关闭了才可以
如果是
@Test
public void getUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
User userById = userDao.getUserById(1);
System.out.println(userById);
System.out.println("---------------------");
SqlSession sqlSession1 = MybatisUtils.getSqlSession();
UserDao userDao1 = sqlSession1.getMapper(UserDao.class);
User userById1 = userDao1.getUserById(1);
System.out.println(userById1);
sqlSession.close();
sqlSession1.close();
}
两个同时关闭不会调用二级缓存,即便在中间加上第一个sqlSession.closeCache()也不可以
12.4、缓存原理
开启二级缓存后,用户查询时先查询的是二级缓存有没有,再查询一级缓存有没有,最后查询数据库
二级缓存存在于Mapper中,一级缓存则是存在于sqlsession中
eviction代表清除策略:
LRU
– 最近最少使用:移除最长时间不被使用的对象。FIFO
– 先进先出:按对象进入缓存的顺序来移除它们。SOFT
– 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK
– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
flushInterval是每隔多久刷新一遍缓存单位是毫秒
size表示最多可以存储结果对象或列表的 512 个引用
测试代码:
@Test
public void getUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
User userById = userDao.getUserById(1);
System.out.println(userById);
System.out.println("---------------------");
sqlSession.close();
SqlSession sqlSession1 = MybatisUtils.getSqlSession();
UserDao userDao1 = sqlSession1.getMapper(UserDao.class);
User userById1 = userDao1.getUserById(1);
System.out.println(userById1);
sqlSession1.close();
}
开启两个sqlsession
在第二个sqlsession调用以前第一个sqlSession查询完并关闭了,使用第二个sqlsession就会读取二级缓存
注意必须要第一个sqlSession关闭了才可以
如果是
@Test
public void getUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
User userById = userDao.getUserById(1);
System.out.println(userById);
System.out.println("---------------------");
SqlSession sqlSession1 = MybatisUtils.getSqlSession();
UserDao userDao1 = sqlSession1.getMapper(UserDao.class);
User userById1 = userDao1.getUserById(1);
System.out.println(userById1);
sqlSession.close();
sqlSession1.close();
}
两个同时关闭不会调用二级缓存,即便在中间加上第一个sqlSession.closeCache()也不可以
12.4、缓存原理
开启二级缓存后,用户查询时先查询的是二级缓存有没有,再查询一级缓存有没有,最后查询数据库
二级缓存存在于Mapper中,一级缓存则是存在于sqlsession中
从数据查询出的数据首先放在一级缓存中,sqlsession关闭后才会将一级缓存中的数据放入二级缓存