目录
2.1创建一张数据表并且创建对应的实体类(在此举例Admin)
2.2导入MyBatis jar包以及mysql数据库驱动包(给出坐标)
1.Mybatis概述
原是Apache的一个开源项目iBatis, 2010年6月这个项目随着开发团队转投GoogleCode 旗下, iBatis3.x正式更名为MyBatis。Mybatis是一款优秀的持久层框架,他避免了几乎所有的JDBC代码手动设置参数以及手动获取结果集中数据的操作,需在dao接口中定义其功能方法,不需要具体实现,可以在对应的sql映射文件(.xml)当中书写sql来具体实现。并且Mybatis支持动态sql以及数据缓存。
2.环境搭建(Mapper接口化开发)
2.1创建一张数据表并且创建对应的实体类(在此举例Admin)
数据表:
实体类(model包下定义Admin类):
public class Admin {
private Integer id;
private String account;
private String password;
public Admin() {
System.out.println("调用了无参构造");
}
public Admin(Integer id, String account, String password) {
System.out.println("调用了带参构造");
this.id = id;
this.account = account;
this.password = password;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "Admin{" +
"id=" + id +
", account='" + account + '\'' +
", password='" + password + '\'' +
'}';
}
}
2.2导入MyBatis jar包以及mysql数据库驱动包(给出坐标)
<!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.2</version> </dependency><!--mysql驱动包--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.16</version> </dependency>
2.3.创建MyBatis全局配置文件
可先在resources下创建mapppers目录以及config.properties和mybatis-config..xml配置文件。
mybatis-config.xml就是我们mybatis的核心配置文件。在其中配置:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--
mybatis的核心配置文件
-->
<!--可以连接很多数据库,定义一个配置一个id,可根据default来改变id来进行更换-->
<environments default="development">
<environment id="development">
<!--数据库事务管理配置-->
<transactionManager type="JDBC"/>
<!--数据源配置 type="POOLED" 使用数据库连接池-->
<dataSource type="POOLED">
<property name="driver" value=""/>
<property name="url" value=""/>
<property name="username" value="}"/>
<property name="password" value=""/>
</dataSource>
</environment>
</environments>
</configuration>
此处我们知道要给数据源中配置信息(value=""),可以根据我们连接的数据库信息直接填写value值,但是以后我们的核心配置文件配置的东西多了,而且想要修改数据源信息寻找就会比较麻烦,所以我们单独把配置信息提取到config.properties文件中,里面以键值对的形式存储值,以后修改数据源信息时直接在此文件中修改即可,非常方便。
在mybatis-config.xml中导入config.properties,然后设置value值。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--
mybatis的核心配置文件
-->
<!--导入配置属性文件(对应的是数据源配置)-->
<properties resource="config.properties"></properties>
<!--可以连接很多数据库,可根据default来更换-->
<environments default="development">
<environment id="development">
<!--数据库事务管理配置-->
<transactionManager type="JDBC"/>
<!--数据源配置 type="POOLED" 使用数据库连接池-->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${uname}"/>
<property name="password" value="${pwd}"/>
</dataSource>
</environment>
</environments>
</configuration>
2.4在dao层中定义接口(举例AdminDao)
//定义访问功能的接口
public interface AdminDao {
/*基本数据类型直接传值即可*/
Admin findAdminById(int id);
}
2.5创建sql映射文件
在mappers目录下定义AdminMapper.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是对应接口的地址-->
<mapper namespace="com.ffyc.mybatispro.dao.AdminDao">
<!--id必须与方法名一致且id唯一 parameterType 参数类型 resultType 返回值类型 -->
<select id="findAdminById" parameterType="int" resultType="com.ffyc.mybatispro.model.Admin">
select id,account,password from admin where id = #{id}
</select>
</mapper>
2.6核心配置文件中配置sql映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--
mybatis的核心配置文件
-->
<!--导入配置属性文件(对应的是数据源配置)-->
<properties resource="config.properties"></properties
<!--可以连接很多数据库,可根据default来更换-->
<environments default="development">
<environment id="development">
<!--数据库事务管理配置-->
<transactionManager type="JDBC"/>
<!--数据源配置 type="POOLED" 使用数据库连接池-->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${uname}"/>
<property name="password" value="${pwd}"/>
</dataSource>
</environment>
<!--<environment id="work">
</environment>-->
</environments>
<!--配置sql映射文件-->
<mappers>
<mapper resource="mappers/AdminMapper.xml"></mapper>
</mappers>
</configuration>
2.7测试以及Api接口说明(定义测试类Test)
public class test {
public static void main(String[] args) throws IOException {
//读取配置文件
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
/*
* 创建SqlSessionFactory对象,用于封装读取的配置文件的信息,由于创建开销较大,所以一个项目中只创建一次。
* 主要的作用是获取到SqlSession对象
* */
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
/*
* sqlSession用于每次和数据库会话时使用,每交互一次,就要创建一个sqlSession对象
* */
SqlSession sqlSession = sessionFactory.openSession();
/*
* 将AdminDao该类的信息传过去,映射获取到一个代理对象,此代理对象是mybatis框架生成的,就表明我要用该对象来调用sql映射文件中的sql
* */
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
/*
* 由代理对象调用映射文件中id名与接口中方法名相同的sql
* */
Admin admin = adminDao.findAdminById(2);
System.out.println(admin);
sqlSession.close(); //关闭
}
}
测试结果:
调用了无参构造
Admin{id=2, account='lisi', password='456'}
2.8总结:
mybatis会将查询到的结果自动封装到给定的对象中,用无参构造自动创建对象。
自动封装结果的条件:
1.开启全局结果自动映射 PARTIAL(默认单张表开启)。
2,查出的数据库列名与对象属性名必须一致,如果不一致,不能映射,可以在sql语句中定义别名使其一致。
3.mapUnderscoreToCamelCase --->true 表示开启驼峰命名自动映射,即从数据库列名 A_COLUMN 映射到Java属性名 aColumn,在mybatis配置文件中配置。
3.优化
3.1起别名
如果我们方法的返回值是model下面的实体类,resultType的地址要写全,比如:com.ffyc.mybatispro.model.Admin,这样就会很繁琐,所以我们在mybatis核心配置文件里进行别名配置,配置之后可以写resultType="Admin"。
3.2打印日志+驼峰命名
如果我们在运行时,想要看到里面的运行细节,就要开启打印日志。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--
mybatis的核心配置文件
-->
<!--导入配置属性文件(对应的是数据源配置)-->
<properties resource="config.properties"></properties>
<!--常用设置-->
<settings>
<!--启用打印日志功能,在运行时,将执行sql的细节记录,标准的打印到控制台-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!--配置类型简称(起别名),对应映射文件当中的resultType-->
<typeAliases>
<package name="com.ffyc.mybatispro.model"/>
</typeAliases>
<!--可以连接很多数据库,可根据default来更换-->
<environments default="development">
<environment id="development">
<!--数据库事务管理配置-->
<transactionManager type="JDBC"/>
<!--数据源配置 type="POOLED" 使用数据库连接池-->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${uname}"/>
<property name="password" value="${pwd}"/>
</dataSource>
</environment>
</environments>
<!--配置sql映射文件-->
<mappers>
<mapper resource="mappers/AdminMapper.xml"></mapper>
</mappers>
</configuration>
3.3封装MybatisUtil类
public class MybatisUtil {
static SqlSessionFactory sessionFactory;
static {
//读取配置文件
Reader reader = null;
try {
reader = Resources.getResourceAsReader("mybatis-config.xml");
} catch (IOException e) {
e.printStackTrace();
}
/*
* 创建SqlSessionFactory对象,用于封装读取的配置文件的信息,由于创建开销较大,所以一个项目中只创建一次。
* 主要的作用是获取到SqlSession对象
* */
sessionFactory = new SqlSessionFactoryBuilder().build(reader);
}
public static SqlSession getSqlSession(){
return sessionFactory.openSession();
}
}
这样我们每次测试时使用getSqlSession()方法就可直接获取到sqlSession对象,防止代码过于冗余。
4.dao层参数传递方式
4.1基本数据类型直接传值即可
例如:Admin findAdminById(int id);
4.2引用数据类型如果需要多个一起传就要定义多个参数
例如:Admin login(@Param("acc")String account, @Param("pwd")String password);
4.3如果传单个没有封装的引用数据类型的话也要定义参数
例如:Admin find(@Param("account")String account);
4.4要传多个参数建议直接封装,直接封装到对象中传过去
例如:Admin login(Admin admin);
注意:参数如果是对象,mapperXML那边用的属性会认为是对象中的属性,比如num!=null,会认为是Admin对象中的num!=null ,可用#{属性名}来获取属性值 。
5.mybatis中的事务管理
事务:每次和数据库连接,发送sql,到执行完后,数据库都会进行控制。
JDBC默认自动提交事务,而mybatis默认自动开启,手动提交,等到dao这边代码执行完无误后,提交完事务后,会发送标识,数据库那边才会进行修改操作(查询除外,查询没有对数据进行修改);
发送的标识:
sqlSession.commit(); //提交事务,执行完增删改数据后都得提交事务,之后,数据库那边才会更改。
6.新增时最终将id封装到对象中返回
我们在新增管理员时,传进去的Admin对象中只需要account和password就行,因为id为主键自增长,但是我们最终拿不到新增的id值,恰恰有时我们又正需要,比如:以后多表关联时添加完一条数据立马就要用对应添加的id,我们可以在sql映射文件中做做手脚———
<!-- 开启将生成的主键列(id)的值添加到封装的对象的对应属性值上 keyProperty: 所要添加的对象的属性 keyColumn: 数据库表中主键对应的列名(字段名) --> <insert id="add" parameterType="Admin" useGeneratedKeys="true" keyProperty="id" keyColumn="id"> insert into admin(account,password) value(#{account},#{password}) </insert>
此时,我们代理对象调用的方法执行完后,我们就可以利用getId()方法拿到我们心心念念的id值了。
(重点)7.#{} 和${}区别
#{参数名}:占位符,是通过预编译的方式传值,更加的安全,防止sql注入,主要用于传值。举例:insert into admin(account,password) value(#{account},#{password})
${参数名}:拼接符,主要用于向sql中传列名,是直接将值拼接上去的。如:Column=num,sql会把num当成数据表中的字段来处理。
举例:select * from admin order by ${Column} //表示根据num字段排序
(重点) 8.特殊结果处理(resultMap)
8.1引入
对于单张表的查询,mybtis默认配置是PARTIAL, 它只会自动映射没有定义嵌套结果映射的字段,如果有关联查询,自动映射就关了,所有映射只能自己定义。
还有一种方法就是在mybatis核心配置文件中的settings中配置————
<setting name="autoMappingBehavior" value="FULL"/>
但是我们并不推荐使用"FULL",FULL会自动映射任何复杂的结果集(无论是否嵌套),看起来很好,但是如果关联表与主表字段有冲突(比如id),一个id会乱映射到多个上去,这就要注重定义属性名必须不重复,这样就特别麻烦。
8.2初步使用+返回值为List集合
在resultMap中可进行自定义的结果映射:
如果返回值类型为List<Admin>,查出来多条数据,会一个个创建Admin对象映射封装,最后会自动生成List集合将多个Admin对象添加进去。
<resultMap id="adminMap" type="Admin">
<id column="id" property="id"></id>
<result column="account" property="account"></result>
<result column="password" property="password"></result>
</resultMap>
<select id="findAll" resultMap="adminMap">
select * from admin
</select>
注意事项:
1.输出映射使用的是 resultMap,而非resultType。
2.resutlMap的id属性是 resutlMap 的唯一标识。
3.id 标签映射主键,result 标签映射非主键。
4.column是查询结果列的名称,property是对象中属性名称。
8.3多表关系搭建
搭建一个学生宿舍模型,一个学生只能选择一个宿舍,但一个宿舍可以入住多个学生。
学生数据表:
宿舍数据表:
学生实体类(省略了构造方法+get+set方法):
public class Student {
private Integer id; //定义类型时建议都定义为包装类型,这样判断条件统一为不等于null
private String name;
private String gender;
private Dorm dorm; //关联关系
private Admin admin; //关联关系
private Date operTime;
}
宿舍实体类(省略了构造方法+get+set方法):
public class Dorm {
private int id;
private String dormNum;
private Admin admin;
private List<Student> students; //一个宿舍对应多个学生信息
}
8.4学生组装查询结果集处理
组装查询就是sql一步到位把所需要的数据全部查询出来,然后自己在resultMap中一个个定义映射。
举例:
dao方法为:Student findStudentById(int id);
sql映射文件:
<resultMap id="studentMap" type="Student">
<id property="id" column="id"></id>
<result property="name" column="name"></result>
<result property="gender" column="gender"></result>
<result property="operTime" column="oper_time"></result>
<!--association 用来封装关联的对象信息,自动会创建javaType(所需要的属性类型)的对象,property是属性名,会将result里组装自定义的嵌套字段来进行映射-->
<association property="dorm" javaType="Dorm">
<result property="dormNum" column="dormnum"></result>
</association>
<association property="admin" javaType="Admin">
<result property="account" column="account"></result>
</association>
</resultMap>
<select id="findStudentById" parameterType="int" resultMap="studentMap">
SELECT
s.id,
s.name,
s.gender,
d.dormnum,
a.account,
s.oper_time
FROM student s LEFT JOIN dorm d ON s.dormid = d.id
LEFT JOIN admin a ON s.adminid = a.id
where s.id = #{id}
</select>
8.5学生嵌套查询结果处理
嵌套查询就是先把主表的所有信息查找出来,然后拿到关联属性的id,利用id在相应关联的表查询所需的数据,再自定义映射封装。
举例:
dao方法为:Student findStudentById1(int id);
sql映射文件:
<resultMap id="studentMap1" type="Student">
<id property="id" column="id"></id>
<result property="name" column="name"></result>
<result property="gender" column="gender"></result>
<result property="operTime" column="oper_time"></result>
<!--根据id再单表查询宿舍和管理员信息,被关联的属性单张表查询,就可以自动创建对象将所查询的值封装到对象返回-->
<association property="dorm" javaType="Dorm" column="dormid" select="findDormById"></association>
<association property="admin" javaType="Admin" column="adminid" select="findAdminById"></association>
</resultMap>
<select id="findStudentById1" parameterType="int" resultMap="studentMap1">
select id,name,gender,dormid,adminid,oper_time from student where id = #{id}
</select>
<select id="findDormById" resultType="Dorm">
select dormnum from dorm where id = #{dormid}
</select>
<select id="findAdminById" resultType="Admin">
select account from admin where id = #{adminid}
</select>
8.6宿舍组装查询结果集处理
在resultMap中,如果所关联的属性是一个对象,那么用association,如果所关联的属性是一个集合,就用collection,比如宿舍对象中有属性学生集合List<Studnet>。
举例:
dao方法为:Dorm findDormById(int id);
sql映射文件:
<resultMap id="dormMap" type="Dorm">
<id property="id" column="id"></id>
<result property="dormNum" column="dormnum"></result>
<association property="admin" javaType="Admin">
<result property="account" column="account"></result>
</association>
<collection property="students" javaType="list" ofType="Student">
<result property="name" column="name"></result>
<result property="gender" column="gender"></result>
</collection>
</resultMap>
<select id="findDormById" parameterType="int" resultMap="dormMap">
SELECT
d.id,
s.name,
s.gender,
d.dormnum,
a.account
FROM dorm d LEFT JOIN admin a ON d.adminid = a.id
LEFT JOIN student s ON d.id = s.dormid
where d.id = #{id}
</select>
返回值只有一个Dorm对象,但一个宿舍里可以有多个学生,sql查询出来有多条,数据里面宿舍信息是相同的,会有多个学生信息,所以就要用collection将查出来的学生信息封装到list集合当中。ofType指的是集合中的数据类型。
8.7宿舍嵌套查询结果集处理
举例:
dao方法为:Dorm findDormById1(int id);
sql映射文件:
<resultMap id="dormMap1" type="Dorm">
<id property="id" column="id"></id>
<result property="dormNum" column="dormnum"></result>
<association property="admin" javaType="Admin" column="adminid" select="findAdminById"></association>
<collection property="students" javaType="list" ofType="Student" column="id" select="findStudentById"></collection>
</resultMap>
<select id="findDormById1" resultMap="dormMap1">
select id,dormnum,adminid from dorm where id = #{id}
</select>
<select id="findAdminById" resultType="Admin">
select account from admin where id = #{adminid}
</select>
<select id="findStudentById" resultType="Student">
select name,gender from student where dormid = #{id}
</select>
9.注解方式实现sql映射
对于单张表查询,如果只是一些简单的sql,我们还定义相应的sql映射文件,这样就有点小题大作了,mybatis支持直接在接口方法上直接加注解sql。
举例:
/*利用注解的方式*/
@Delete("delete from admin where id = #{id}")
void del(int id);
虽然有这种方式,但是一些较为复杂的sql语句,还是推荐定义在sql映射文件中!!!
10.(重点)动态sql
MyBatis的一个强大的特性之一通常是它的动态 SQL 能力。比如我们的综合查询,查询条件时刻不唯一,如果我们利用排列组合,列出它所有可能会用到的sql语句,这就有点破罐子破摔的味道了,如果我们想整合为一条sql,在学习mybatis之前,其实也可以实现,只不过那种拼接方式特别痛苦,现在利用动态sql就可以彻底消灭这种痛苦!
10.1if,where元素
if test="条件"
如果where标签里没有一个符合的,不会有where,如果有符合的,第一个if添加前面的and或者or会自动去掉。
<select id="findStudent" parameterType="Student" resultType="Student">
select * from student
<where>
<if test="name!=null">
and name = #{name}
</if>
<if test="gender!=null">
and gender=#{gender}
</if>
</where>
</select>
10.2trim元素
prefix:指定拼接的关键字
prefixOverrides:第一次去除多余的前缀内容
<select id="findStudent" parameterType="Student" resultType="Student">
select * from student
<trim prefix="where" prefixOverrides="and|or">
<if test="name!=null">
and name = #{name}
</if>
<if test="gender!=null">
and gender=#{gender}
</if>
</trim>
</select>
10.3choose元素
多路选择,相当于 if---else if---else 结构
<select id="findStudent" parameterType="Student" resultType="Student">
select * from student
<trim prefix="where" prefixOverrides="and|or">
<choose>
<when test="num!=null"> /*if*/
num =#{num}
</when>
<when test="name!=null"> /*else if*/
and name = #{name}
</when>
<otherwise>
and gender =#{gender} /*else*/
</otherwise>
</choose>
</trim>
</select>
10.4set元素
set标签可以自动将最后添加的语句后面的,自动舍去。添加不为null的判断是因为我们只能修改自己可以修改的东西,其他的我们看不见则为null。
<update id="update" parameterType="Student">
update student
<set>
<if test="name!=null">
name=#{name},
</if>
<if test="gender!=null">
gender=#{gender},
</if>
</set>
where id =#{id}
</update>
利用trim实现:
也可以使用trim中的suffixOverrides去除最后一个多余的。
<update id="update1" parameterType="Student">
update student
<trim prefix="set" suffixOverrides=",">
<if test="name!=null">
name=#{name},
</if>
<if test="gender!=null">
gender=#{gender},
</if>
</trim>
where id =#{id}
</update>
10.5foreach元素
for each主要用于有in()语句的sql。
collection="属性值" 如果参数类型是List,collection为list ,如果参数类型是array数组的时候,collection为array。
#{id}表示要拼接的数据 ,item表示传来参数中一个个的数据。
参数:List<Integer>
<delete id="delete">
delete from student where id in
<foreach collection="list" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
11.特殊符号处理
在 mybatis 中的 xml 文件中,存在一些特殊的符号,比如:&、<>等,正常书写 mybatis 会报错,需要对这些符号进行转义。
符号 | 转义 |
---|---|
< | < |
> | > |
" | " |
’ | ' |
& | & |
或者使用转义标签,使用时注意缩小范围: <![CDATA[ 特殊符号 ]]>
12.mybatis缓存
Mybatis 内部存储缓存使用一个 HashMap,key 为hashCode+sqlId+Sql 语句。value 为从查询出来映射生成的java 对象。
12.1一级缓存(sqlSession级别,默认开启)
12.2二级缓存(sqlSessionFactory级别)
二级缓存是根据Mapper进行划分区域的,所以有多个缓存区域,在相同Mapper中查询的数据放在同一个区域中 。当SqlSession关闭时,会将数据存入到二级缓存中自己的区域 ,SqlSession执行 insert、update、delete等操作commit提交后会清空其自己相对应的缓存区域,防止脏读。mybatis要开启二级缓存需要在mybatis核心配置文件settings中配置:
启用二级缓存
<setting name="cacheEnabled" value="true"/>
第一步:启用二级缓存
第二步:对象序列化 ,将所涉及的 POJO(Java对象) 类实现序列化接口Java.io.Serializable。
第三步:配置映射文件 在 Mapper 映射文件中添加<cache ...../>,表示此mapper 开启二级缓存。
<!--先进先出缓存,每隔60s清空刷新一次,可以存储512个引用,设置为只读-->
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true" />