1 Mybatis简介
- MyBatis是一款优秀的持久层框架(Dao)
- 它支持自定义 SQL、存储过程以及高级映射。
- MyBatis免除了几乎所有的 JDBC代码、设置参数和获取结果集的工作。
- MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java pojo(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
- 官方文档是最好的学习资料!Mybatis官方文档
2 快速入门
2.1 数据导入
CREATE DATABASE /*!32312 IF NOT EXISTS*/`mybatis_db` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `mybatis_db`;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`address` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
insert into `user`(`id`,`username`,`age`,`address`) values (1,'UZI',19,'上海'),(2,'PDD',25,'上海');
2.2 导入依赖
<dependencies>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--log4j日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
在build中配置resources,来防止我们资源导出失败的问题
<!--在build中配置resources来防止我们资源导出失败的问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
2.3 编写核心配置
在资源目录下创建: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>
<properties resource="jdbc.properties"/>
<settings>
<!--开启自动驼峰命名映射-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<package name="com.cycyong.pojo"></package>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--获取配置文件中配置的对应的值来设置连接相关参数-->
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!-- 定义mapper接口所在的包。要求xml文件存放的路径和mapper接口的包名要对应 -->
<mappers>
<package name="com.cycyong.mapper"/>
</mappers>
</configuration>
jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis_db?&useSSL=false&serverTimezone=UTC&characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
2.4 定义接口及对应的xml映射文件
com.cycyong.mapper.UserMapper.java
public interface UserMapper {
List<User> findAll();
}
com.cycyong.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">
<mapper namespace="com.cycyong.mapper.UserMapper">
<select id="findAll" resultType="com.cycyong.pojo.User">
select * from user
</select>
</mapper>
由于配置了扫描路径,所以xml文件也可放在mapper下
2.5 编写测试类
获取SqlSession,通过SqlSession获取UserMapper调用对应的方法
public static void main(String[] args) throws IOException {
@Test
public void test01() throws IOException {
//定义mybatis配置文件的路径
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取Sqlsession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper实现类对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//调用方法测试
List<User> userList = userMapper.findAll();
for (User user:userList) {
System.out.println(user);
}
//释放资源
sqlSession.close();
}
}
2.6 测试
成功查询出结果!
3 参数获取
3.1 一个参数
3.1.1 基本参数
-
我们可以使用#{}直接来取值,写任意名字都可以获取到参数。但是一般用方法的参数名来取。
-
parameterType可写可不写,mybatis会自动推断其类型
-
#{}必须和实体类对应,大小写必须一样
例如:
接口中方法定义如下:
User findById(Integer id);
xml中内容如下:
<select id="findById" resultType="com.cycyong.pojo.User">
select * from user where id = #{id}
</select>
3.1.2 pojo
- 我们可以使用POJO中的属性名来获取对应的值。
- #{}放pojo的属性名即可
例如:
接口中方法定义如下:
User findByUser(User user);
xml中内容如下:
<select id="findByUser" resultType="com.cycyong.pojo.User">
select * from user where id = #{id} and username = #{username} and age = #{age} and address = #{address}
</select>
3.1.3 Map
我们可以使用map中的key来获取对应的值。
例如:
接口中方法定义如下:
User findByMap(Map map);
xml中内容如下:
<select id="findByMap" resultType="com.cycyong.pojo.User">
select * from user where id = #{id} and username = #{username} and age = #{age} and address = #{address}
</select>
方法调用:
Map map = new HashMap();
map.put("id",2);
map.put("username","PDD");
map.put("age",25);
map.put("address","上海");
userMapper.findByMap(map);
3.2 多个参数
Mybatis会把多个参数放入一个Map集合中,默认的key是argx和paramx这种格式。
例如:
接口中方法定义如下:
User findByCondition(Integer id,String username);
我们虽然可以使用对应的默认key来获取值,但是这种方式可读性不好。我们一般在方法参数前使用@Param
来设置参数名。则xml以@Param
对应的参数名对应。
例如:
接口中方法定义:
User findByCondition(@Param("id") Integer id,@Param("userName") String username);
最终map中的键值对如下:
{id=2, param1=2, userName=PDD, param2=PDD}
所以我们就可以使用如下方式来获取参数
<select id="findByCondition" resultType="com.cycyong.pojo.User">
select * from user where id = #{id} and username = #{userName}
</select>
3.3 总结
建议如果只有一个参数的时候不用做什么特殊处理。如果是有多个参数的情况下一定要加上@Param
来设置参数名。加上@Param
之后,参数是按照注解的名字和xml对应。
最重要的是一定要参数(接口参数、pojo的属性)和xml中的#{}对应!
4 核心类
4.1 SqlSessionFactory
SqlSessionFactory是一个SqlSession的工厂类,用来获取SqlSession对象。
成员方法如下:
SqlSession openSession();
//获取SqlSession对象,传入的参数代表创建的SqlSession是否自动提交
SqlSession openSession(boolean autoCommit);
4.2 SqlSession
SqlSession 提供了在数据库执行 SQL 命令所需的所有方法 。它还提供了事务的相关操作。
- 增删改需要提交事务
- 查不需要提交事务
成员方法如下:
T getMapper(Class<T> type);//获取mapper对象
void commit();//提交事务
void rollback();//回滚事务
void close();//释放资源
5 实现CRUD
5.1 增加
①接口中增加相关方法
void insertUser(User user);
②映射文件UserMapper.xml增加响应的标签
<insert id="insertUser">
insert into user values(null,#{username},#{age},#{address})
</insert>
注意:要记得提交事务。
SqlSession sqlSession = sqlSessionFactory.openSession(true);
或者
sqlSession.commit();
5.2 删除
①接口中增加相关方法
void deleteById(Integer id);
②映射文件UserMapper.xml增加响应的标签
<delete id="deleteById">
delete from user where id = #{id}
</delete>
注意:要记得提交事务。
5.3 查找
①接口中增加相关方法
User findById(Integer id);
②映射文件UserMapper.xml增加响应的标签
<select id="findById" resultType="com.cycyong.pojo.User">
select * from user where id = #{id}
</select>
5.4 修改
①接口中增加相关方法
void updateUser(User user);
②映射文件UserMapper.xml增加响应的标签
<update id="updateUser">
UPDATE USER SET age = #{age} , username = #{username},address = #{address} WHERE id = #{id}
</update>
注意:要记得提交事务。
6 配置文件详解
- mybatis-config.xml
- mybatis的配置文件包含了会深深影响Mybatis行为的设置和属性信息
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
6.1 环境配置
mybatis可以配置成适应多种环境
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
学会使用配置多套运行环境!
mybatis默认的事务管理器就是jdbc
,连接池:POOLED
6.2 属性详情
6.2.1 properties
可以使用properties读取properties配置文件。使用其中的resource属性来设置配置文件的路径。
然后使用${key}来获取配置文件中的值
例如:
在resources目录下有jdbc.properties文件,内容如下:
jdbc.driver =com.mysql.cj.jdbc.Driver
jdbc.url =jdbc:mysql://localhost:3306/mybatis_db?&useSSL=false&serverTimezone=UTC&characterEncoding=utf-8
jdbc.username =root
jdbc.password=jianjian123456
在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>
<!--设置配置文件所在的路径-->
<properties resource="jdbc.properties"></properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--获取配置文件中配置的对应的值来设置连接相关参数-->
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
</configuration>
6.2.2 settings
可以使用该标签来设置进行一些设置
例如:
数据库 ------> java实体类
A_COLUMN ------> aColumn
<settings>
<!--开启自动驼峰命名映射-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
具体的设置参考:setting配置详情
6.2.3 typeAliases
可以用来设置给全类名设置别名,简化书写。一般设置一个包下的类全部具有默认别名。默认别名是类目首字母小写。例如:com.cycyong.pojo.User别名为user
<typeAliases>
<package name="com.cycyong.mapper"></package>
</typeAliases>
6.2.4 environments
配置数据库相关的环境,例如事物管理器,连接池相关参数等。
<!--设置默认环境-->
<environments default="development">
<!--设置该环境的唯一标识-->
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--获取配置文件中配置的对应的值来设置连接相关参数-->
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
6.2.5 mappers
该标签的作用是加载映射的,加载方式有如下几种(主要使用第四种):
④将包内的映射器接口实现全部注册为映射器,例如:
<!-- 定义mapper接口所在的包。要求xml文件存放的路径和mapper接口的包名要对应 -->
<mappers>
<package name="com.cycyong.mapper"/>
</mappers>
其他几种不常用!
7 打印日志
①log4j配置 在resources目录下创建log4j.properties文件,内容如下:
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=c:/mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
log4j.rootLogger=debug, stdout
②引入依赖
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
8 #{}和${}的区别
如果使用#{}.他是预编译的sql可以防止SQL注入攻击
如果使用${}他是直接把参数值拿来进行拼接,这样会有SQL注入的危险
如果使用的是#{}来获取参数值日志如下:
Preparing: select * from user where id = ? and username = ? and age = ? and address = ?
Parameters: 2(Integer), 快乐风男(String), 29(Integer), 北京(String)
如果使用${}来获取参数值日志如下:
Preparing: select * from user where id = 2 and username = 快乐风男 and age = 29 and address = 北京
9 动态SQL
在实际开发中的SQL语句没有之前的这么简单,很多时候需要根据传入的参数情况动态的生成SQL语句。Mybatis提供了动态SQL相关的标签去使用。
9.1 if
可以使用if标签进行判断,条件成立才会把if标签中的内容拼接进sql语句中。
例如:
<select id="findByCondition" resultType="com.cycyong.pojo.User">
select * from user
where id = #{id}
<if test="username!=null">
and username = #{username}
</if>
</select>
如果参数username为null则执行的sql为:
select * from user where id = ?
如果参数username不为null则执行的sql为:
select * from user where id = ? and username = ?
注意:在test属性中表示参数的时候不需要写#{},写了会出问题。
9.2 trim(where set)
① where
可以使用where标签动态的拼接where并且去除前缀的and或者or。
例如:
<select id="findByCondition" resultType="com.cycyong.pojo.User">
select * from user
<where>
<if test="id!=null">
id = #{id}
</if>
<if test="username!=null">
and username = #{username}
</if>
</where>
</select>
如果id和username都为null,则执行的sql为:
select * from user
如果id为null,username不为null,则执行的sql为:
select * from user where username = ?
②set
可以使用set标签动态的拼接set并且去除后缀的逗号。
例如:
<update id="updateUser">
UPDATE USER
<set>
<if test="username!=null">
username = #{username},
</if>
<if test="age!=null">
age = #{age},
</if>
<if test="address!=null">
address = #{address},
</if>
</set>
where id = #{id}
</update>
如果调用方法时传入的User对象的id为2,username不为null,其他属性都为null则最终执行的sql为:
update user set username = ? where id = ?
9.3 foreach
可以使用foreach标签遍历集合或者数组类型的参数,获取其中的元素拿来动态的拼接SQL语句。
例如:
方法定义如下 :
List<User> findByIds(@Param("ids") Integer[] ids);
如果期望动态的根据实际传入的数组的长度拼接SQL语句。例如传入长度为4个数组最终执行的SQL为:
select * from User WHERE id in( ? , ? , ? , ?, ? )
则在xml映射文件中可以使用以下写法:
<select id="findByIds" resultType="com.cycyong.pojo.User">
select * from User
<where>
<foreach collection="ids" open="id in(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
collection:表示要遍历的参数。
open:表示遍历开始时拼接的语句
item:表示给当前遍历到的元素的取的名字
separator:表示每遍历完一次拼接的分隔符
close:表示最后一次遍历完拼接的语句
注意:如果方法参数是数组类型,默认的参数名是array
,如果方法参数是list集合默认的参数名是list。建议遇到数组或者集合类型的参数统一使用@Param
注解进行命名。
9.4 choose、when、otherwise
当我们不想使用所有的条件,而只是想从多个条件中选择一个使用时。可以使用choose系列标签。类似于java中的switch。
例如:
接口中方法定义如下:
List<User> selectChose(User user);
期望:
- 如果user对象的id不为空时就通过id查询
- 如果id为null,username不为null就通过username查询
- 如果id和username都会null就查询id为3的用户
xml映射文件如下:
<select id="selectChose" resultType="com.cycyong.pojo.User">
select * from user
<where>
<choose>
<when test="id!=null">
id = #{id}
</when>
<when test="username!=null">
username = #{username}
</when>
<otherwise>
id = 3
</otherwise>
</choose>
</where>
</select>
小技巧:
-
choose类似于java中的switch
-
when类似于java中的case
-
otherwise类似于java中的dufault
一个choose标签中最多只会有一个when中的判断成立。从上到下去进行判断。如果成立了就把标签体的内容拼接到sql中,并且不会进行其它when的判断和拼接。如果所有的when都不成立则拼接otherwise中的语句。
10 SQL片段抽取
我们在xml映射文件中编写SQL语句的时候可能会遇到重复的SQL片段。这种SQL片段我们可以使用sql
标签来进行抽取。然后在需要使用的时候使用include
标签进行使用。
例如:
<sql id="baseSelect" >id,username,age,address</sql>
<select id="findAll" resultType="com.cycyong.pojo.User">
select <include refid="baseSelect"/> from user
</select>
最终执行的sql为: select id,username,age,address from user