系列文章
SSM之MyBatis 01 —— 第一个MyBatis程序、增删改查(模糊查询)
SSM之MyBatis 02 —— 配置文件说明、日志工厂、分页(Limit和RowBounds)
SSM之MyBatis 03 —— 使用注解开发、Lombok、多对一&一对多处理
SSM之MyBatis 04 —— 动态SQL、缓存Cache
文章目录
四、配置文件说明
官方文档中也有详细解释:https://mybatis.org/mybatis-3/zh/getting-started.html
配置标签有规定顺序:
- properties
- settings
- typeAliases
- typeHandlers
- objectFactory
- objectWrapperFactory
- reflectorFactory
- plugins
- environments
- databaseIdProvider
- mappers
1、核心配置文件(mybatis-config.xml)
mybatis-config.xml就是MyBatis的核心配置文件,常把它放在resources目录下。
2、环境配置(environments)
MyBatis 可以配置成适应多种环境 ,每个environment标签就是一套环境,但但每个 SqlSessionFactory 实例只能选择一种环境(通过default选择默认环境)。 MyBatis默认的事务管理器是JDBC,且默认选择连接池方式。
mybatis-config.xml
<environments default="development">
<environment id="development">
.........
<!-- 事务管理器 包括JDBC(提交和回滚)、MANAGED(这个几乎不做什么) -->
<transactionManager type="JDBC"/>
<!-- 数据源:UNPOOLED(无连接池)、POOLED(连接池)、JNDI -->
<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>
为了指定创建哪种环境,只要将它作为可选的参数传递给 SqlSessionFactoryBuilder 即可, 如果忽略了环境参数,那么将会加载默认环境。
//加载自选环境
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties);
//加载默认环境
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, properties);
3、属性(properties)
先在resources目录下创建一个配置文件properteis
db.properties(根据自己数据库)
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8&useSSL=false
username=root
password=123456
在mybatis-config.xml中写properties,通过${}引用properties的变量,注意区别mapper.xml中SQL引入变量使用的是#{}。
- 通过properties标签,可以直接引入外部文件
- 可以在properties标签中增加属性,比如下面就是用的标签里的name和pwd
- 如果引入的外部properties文件和标签增加的属性同名,则先使用外部引入文件里的。
<properties resource="db.properties">
<property name="name" value="root"/>
<property name="pwd" value="123456"/>
</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="${name}"/>
<property name="password" value="${pwd}"/>
</dataSource>
</environment>
</environments>
4、类型别名(typeAliases)
<select id="getUserList" resultType="com.zcy.pojo.User">
select * from mybatis.user;
</select>
在mapper.xml中写SQL时,实体类需要指定完整的包名,这显得比较麻烦,可以通过别名解决。别名有三种方式。
1、mybatis-config.xml中,用User 代替 com.zcy.pojo.User,
<typeAliases>
<typeAlias type="com.zcy.pojo.User" alias="User"/>
</typeAliases>
2、mybatis-config.xml中,扫描包下所有实体类,用首字母小写的类名作为其别名
<typeAliases>
<package name="com.zcy"/>
</typeAliases>
3、在方式2的基础上,在对应的类中加上注解,则用注解的别名
@Alias("User")
public User{
.....
}
一般,实体类较少时用方式一,实体类多时用方式二,如果实体类多但又想自定义别名,就方式三。
同时,MyBatis有一些内置的Java类型别名,它们不区分大小写。(就是基本类型前有下划线,包装类则没有,比如int—>_int,而Integer—>intger)
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
5、设置(setting)
主要用下面三种,后面会具体说明,其他的了解一下就行。
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
</settings>
6、其他配置
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件) 可以使得mybatis更加简化,这里不详细叙述
- mybatis-generator-core
- mybatis-plus
- 通用mapper
7、映射器(mappers)
MapperRegister:注册绑定我们的mapper文件
方式一:使用相对于类路径的资源引用【推荐使用】
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D5FJVAWq-1613964565557)(Mybatis.assets/1613912723800.png)]
<mappers>
<mapper resource="com/zcy/dao/UserMapper.xml"/>
</mappers>
方式二:使用映射器接口实现类的完全限定类名(通俗点就是,利用接口类找到Mapper)
<mappers>
<mapper class="com.zcy.dao.UserMapper"/>
</mappers>
注意:mapper文件必须和接口类同名,并且要和接口类放在同一个位置
方式三:将包内的映射器接口实现全部注册为映射器(通俗点就是,指定一个包,将旗下全部接口注册)
<mappers>
<package name="com.zcy.dao"/>
</mappers>
和方式二一样,需要注意mapper文件要和接口类同名且放在一起
8、生命周期和作用域
生命周期和作用域是至关重要的,因为错误的使用会导致非常严重的并发问题!!
SqlSessionFactoryBuilder
- 目的是生成SqlSessionFctory, 一旦创建了 SqlSessionFactory,就不再需要它了 。
- 最佳作用域是方法作用域 (即作为局部变量)
SqlSessionFactory
- 可以把它想象为数据库连接池
- SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
- 最佳作用域是应用作用域 (即作为全局变量)
- 使用单例模式或者静态单例模式(即该变量是唯一的)
SqlSession
- 它就是连接到连接池的一个请求
- SqlSession 的实例不是线程安全的,因此是不能被共享的
- 最佳的作用域是请求或方法作用域 (作为局部变量)
- 用完之后赶紧关闭,否则资源会被占用
这里面的每一个Mapper就代表一个具体的业务(执行了一件事)!
五、解决属性名和字段名不一致问题
之前使用MyBatis的时候,实体类的属性和数据库中的字段都是一一对应的。
当实体类属性和数据库字段不一致怎么办呢?将类的成员名改变
重新查询时就会出错,名称不一致查询出来就会为空!
具体原因:
/*我们写的SQL*/
<select id="getUserById" parameterType="int" resultType="User">
select * from mybatis.user where id = #{id};
</select>
/*实际的SQL和我们User中不一致,就无法将name和username匹配上*/
select id, name, password from mybatis.user where id = #{id};
解决方法:
- 比较苯的方法是在SQL中也用别名,将nameas username,passwordas pwd来保证一致
select id, name as username, password as pwd from mybatis.user where id = #{id};
2.使用resultMap(结果集映射)
在mapper中增加resultMap标签
<mapper namespace="com.zcy.dao.UserMapper">
<!-- 结果集映射 id:该resultMap的名称,type:返回的类型-->
<resultMap id="UserMapper" type="User">
<!-- 对于实体类中和数据库字段相同的属性,不需要映射,例如它们都有id -->
<!-- property:类的属性名,column:数据库的字段名 -->
<result property="username" column="name"/>
<result property="pwd" column="password"/>
</resultMap>
<!-- 此时就不需要指定resultType -->
<select id="getUserById" parameterType="int" resultMap="UserMapper">
select * from mybatis.user where id = #{id};
</select>
</mapper>
六、日志
6.1、日志工厂
如果一个数据库操作出现了异常,我们通常会将其作为日志输出,在之前没用MyBatis的时候,我们在实现类中通过sout输出。
曾经:sout、debug
现在:日志工厂
- SLF4J
- LOG4J 【掌握,需要导入包】
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING【掌握,标准日志工厂】
- NO_LOGGING
在MyBatis的核心配置文件中,添加setting标签
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
比起没用日志,显然多了很多东西,但这些显得比较杂乱,所以我们用后面的Log4j
6.2、Log4j
什么是Log4j?
- 可以控制日志信息输送的目的地是控制台、文件、GUI组件…
- 可以控制每一条日志的输出格式
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
- 这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码
1、先导入log4j的包
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2、log4j.properties,log4j的配置文件(这里使用部分,可百度更详细的)
#将等级为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/zcy.log
#如果文件超过10mb,就分到新文件
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
3、配置log4j为日志的实现
<!-- 配置日志 -->
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
直接使用,和之前没太大区别,但更详细了。
4、简单使用
- 在要使用日志的类中导入包 org.apache.log4j.Logger
- 创建日志对象 Logger,参数为当前类的class
- 控制日志级别进行输出
package dao;
import com.zcy.dao.UserMapper;
import com.zcy.pojo.User;
import com.zcy.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.Test;
public class UserMapperTest {
static Logger logger = Logger.getLogger(UserMapperTest.class);
@Test
public void testLogger(){
//常用这三种
logger.info("info:进入的testLogger");
logger.debug("debug:进入的testLogger");
logger.error("error:进入的testLogger");
}
}
控制台结果:
同时之前log4j.properties中配置了输出到文件:
七、分页
7.1、Limit 分页
分页是为了减少数据的处理量,在SQL中我们常使用limit进行分页
语法:select * from user limit startIndex, pageSize
例如
select * from user limit 0, 2 表示从第0个数据开始,查两个
select * from user limit 2, 2 表示从第2个数据开始,查两个
select * from user limit 4 表示从第0个数据开始,查到4
使用MyBatis实现分页,其核心仍然是SQL
1、接口
public interface UserMapper {
//通过limit实现分页,多个参数用Map
public List<User> getUserByLimit(Map<String, Integer> map);
}
2、UserMappe.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">
<mapper namespace="com.zcy.dao.UserMapper">
<!-- 结果集映射 -->
<resultMap id="UserMapper" type="User">
<!-- User类中的id和数据库id同名,而username和password不同名 -->
<result property="username" column="name"/>
<result property="pwd" column="password"/>
</resultMap>
<!-- 通过Limit分页,map是Map的别名 -->
<select id="getUserByLimit" parameterType="map" resultMap="UserMapper">
select * from mybatis.user limit #{startIndex}, #{pageSize} ;
</select>
</mapper>
3、测试
package dao;
import com.zcy.dao.UserMapper;
import com.zcy.pojo.User;
import com.zcy.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.Test;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class UserMapperTest {
static Logger logger = Logger.getLogger(UserMapperTest.class);
@Test
public void testLimit(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("startIndex", 0);
map.put("pageSize", 2);
List<User> userList = userMapper.getUserByLimit(map);
for (User user : userList)
System.out.println(user);
sqlSession.close();
}
}
结果:
7.2、RowBounds 分页
不在Mapper的SQL中写分页,而是通过Java代码实现,但这种方式不推荐,因为它不依赖于Mapper了。
1、接口
package com.zcy.dao;
import com.zcy.pojo.User;
import java.util.List;
import java.util.Map;
public interface UserMapper {
//通过RowBounds
public List<User> getUserByRowBounds();
}
2、UserMappe.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">
<mapper namespace="com.zcy.dao.UserMapper">
<!-- 结果集映射 -->
<resultMap id="UserMapper" type="User">
<!-- User类中的id和数据库id同名,而username和password不同名 -->
<result property="username" column="name"/>
<result property="pwd" column="password"/>
</resultMap>
<!-- 通过RowBounds分页 -->
<select id="getUserByRowBounds" resultMap="UserMapper">
select * from mybatis.user;
</select>
</mapper>
3、测试
package dao;
import com.zcy.dao.UserMapper;
import com.zcy.pojo.User;
import com.zcy.utils.MyBatisUtils;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.Test;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class UserMapperTest {
static Logger logger = Logger.getLogger(UserMapperTest.class);
@Test
public void getUserByRowBounds(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
//参数:对应于startIndex,pageSize
RowBounds rowBounds = new RowBounds(0, 2);
//之所以说RowBounds过时,就是因为现在已经不再直接调用接口方法的方式,而是用getMapper
//参数:接口的方法,接口方法的参数,rowBounds分页
List<User> userList = sqlSession.selectList(
"com.zcy.dao.UserMapper.getUserByRowBounds", null, rowBounds);
for (User user : userList)
System.out.println(user);
sqlSession.close();
}
}
结果:
7.3、分页插件 PageHelpler
具体使用方法:https://github.com/pagehelper/Mybatis-PageHelper/blob/master/README_zh.md
1、引入分页插件(Jar包或者maven)
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.10</version>
</dependency>
2、在mybatis-config.xml配置PageHelper
<!--
plugins在配置文件中的位置必须符合要求,否则会报错,顺序如下:
properties?, settings?,
typeAliases?, typeHandlers?,
objectFactory?,objectWrapperFactory?,
plugins?,
environments?, databaseIdProvider?, mappers?
-->
<plugins>
<!-- com.github.pagehelper为PageHelper类所在包名 -->
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 使用下面的方式配置参数,后面会有所有的参数介绍 -->
<property name="param1" value="value1"/>
</plugin>
</plugins>