创作不易,各位看官点赞收藏.
简介:MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。简单来说,mybatis就是用来操作数据库的,可以使程序猿更加容易书写 dao 层。
搭建环境:导入依赖、创建数据库表
user(id,name,address)
。
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.11version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.4.6version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.21version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.12version>
dependency>
编写配置文件:mybatis-config.xml,XML 配置文件中包含了对 MyBatis 系统的核心设置,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager)。
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis?characterEncoding=utf-8&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
mappers>
configuration>
编写 Mapper 接口:
public interface UserMapper {
// 查询所有的用户
List<User> findAllUser();
}
编写 mapper.xml:
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jx.app.mybatis.mapper.UserMapper">
<select id="findAllUser" resultType="com.jx.app.mybatis.entity.User">
select * from user
select>
mapper>
编写 Mybatis 工具类:
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();
}
}
// 获取sqlSession
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
测试查询结果:
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.findAllUser();
System.out.println(users);
}
environments:配置数据库连接配置环境。
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis?characterEncoding=utf-8&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
dataSource>
environment>
environments>
propertes:可以引入 properties 文件,然后通过某种方式去访问文件中的值。
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql:///mybatis?characterEncoding=utf-8&serverTimezone=UTC
username=root
password=123456
<properties resource="jdbc.properties"/>
settings:Mybatis 的全局配置,还有很多配置,可以去官网上查看。
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
typeAliases:取别名标签,给实体类取一个别名,在映射结果时就可以使用别名就不再使用全限定类名。
<typeAliases>
<typeAlias type="com.jx.app.mybatis.entity.User" alias="User"/>
<package name="com.jx.app.mybatis.entity"/>
typeAliases>
mappers:引入 mapper 映射文件。
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
<package name="com.jx.app.mybatis.mapper"/>
mappers>
在 mapper 映射文件获取接口的参数有两种方式,${}
和 #{}
。
${}
: 本质是字符串拼接,将参数与 SQL 语句进行简单拼接,这种方式可能存在 SQL 注入问题,使用这种方式需要注意字符串的单引号拼接。#{}
:本质是占位符方式来拼接 SQL,自动给参数加上单引号,这种方式防止 SQL 注入问题。单个字面量参数:
// 参数 name 是单个字面量
User getUserByName(String name);
<select id="getUserByName" resultType="com.jx.app.mybatis.entity.User">
SELECT *
FROM user u
WHERE u.user_name = #{name}
select>
多个字面量参数:
// 存在多个参数
User getUser(String id, String name);
<select id="getUser" resultType="com.jx.app.mybatis.entity.User">
SELECT *
FROM user u
WHERE u.id = #{param1} AND u.user_name = #{param2}
select>
map 传参:
// 参数是一个map
User getUserByMap(Map<String,String> map);
<select id="getUserByMap" resultType="com.jx.app.mybatis.entity.User">
SELECT *
FROM user u
WHERE u.id = #{id} AND u.user_name = #{username}
select>
实体类传参:
// 参数是一个是实体类
int insert(User user);
<insert id="insert">
INSERT INTO user values(null,#{userName},#{password},#{money})
insert>
@param
注解:可以命名参数,标识参数后依然后把参数放入 map 中,可以通过命名参数的 key 来获取参数值。
// @Param 将参数进行取别名命名
User getUser(@Param("id") String id, @Param("name") String name);
<select id="getUser" resultType="com.jx.app.mybatis.entity.User">
SELECT *
FROM user u
WHERE u.id = #{id} AND u.user_name = #{name}
select>
查询结果为实体对象:
// 接口返回值为实体对象
User getUserByName(String name);
<select id="getUserByName" resultType="com.jx.app.mybatis.entity.User">
SELECT *
FROM user u
WHERE u.user_name = #{name}
select>
注意:返回值是一个实体类对象时,返回结果只能是一条数据或者 null,如果查询出来是多条数据就会报错。
查询结果为集合:
// 接口返回值为集合类型,并指定对应泛型
List<User> getUserByName(@Param("name") String name);
<!-- resultType:需要指定集合中泛型对应类的全限定类名或者别名-->
<select id="getUserByName" resultType="com.jx.app.mybatis.entity.User">
SELECT * FROM user u
</select>
注意:如果查询出来一条或多条结果就会封装到集合中,如果没有结果就会返回空集合但是不会返回 null。
查询结果为 map 集合:
// 接口返回值是map类型
Map<String,Object> getUserToMap();
<select id="getUserToMap" resultType="java.util.Map">
SELECT * FROM user u
select>
注意:查询结果为 map 只能查询一条数据,查询出多条就会报错,而且如果某个字段是 null,这个字段不会封装到 map 中。
多条结果封装成 map:
/**
* 如果查询多条数据也想封装成 map,@MapKey 可以指定查询出来那个字段可以作为 key,
* 然后这一条数据又会封装成一个 map 作为 value 存储进去
*/
@MapKey("id")
Map<String, Map<String,Object>> getUserToMap();
获取自增主键:在插入数据时,可以获取插入数据后对应的自动递增的 id。
// 插入元素
int insert(User user);
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user values(null,#{userName},#{password},#{money})
insert>
resultMap 字段关系映射:查询出来的结果与实体类上的字段名称不一致,我们就需要通过 resultMap 属性去自定义映射关系。
方式一:字段起别名,可以在 SQL 语句中把字段名通过起别名方式与属性名一一对应。
方式二:使用 resultMap 映射,将查询出来字段名与对象属性名进行一个自定义映射。
<select id="getUserList" resultMap="UserListMap">
SELECT * FROM user u
select>
<resultMap id="UserListMap" type="com.jx.app.mybatis.entity.User">
<id column="id" property="id"/>
<result column="user_name" property="userName"/>
<result column="password" property="password"/>
<result column="money" property="money"/>
resultMap>
注意:如果查询出来字段名与属性名对应不上,不会报错,只是他们对应属性值都是对象默认值。
两个实体:Student 和 Teacher,一个学生有一个老师,一个老师有多个学生。
@Data
public class Student {
private String id;
private String studentName;
private Teacher teacher;
}
@Data
public class Teacher {
private String id;
private String teacherName;
private List<Student> students;
}
方式一:使用连接查询查询对学生对应的老师信息,然后使用级联属性映射。
<select id="getStudentById" resultMap="StudentMap">
SELECT * FROM student s
LEFT JOIN teacher t ON s.t_id = t.id
WHERE s.id = #{id}
select>
<resultMap id="StudentMap" type="com.jx.app.mybatis.entity.Student">
<id column="id" property="id"/>
<result column="student_name" property="studentName"/>
<result column="t_id" property="teacher.id"/>
<result column="teacher_name" property="teacher.teacherName"/>
resultMap>
方式二:使用连接查询查询对学生对应的老师信息,然后通过 resultMap 进行关系映射。
<select id="getStudentById" resultMap="StudentMap">
SELECT * FROM student s
LEFT JOIN teacher t ON s.t_id = t.id
WHERE s.id = #{id}
select>
<resultMap id="StudentMap" type="com.jx.app.mybatis.entity.Student">
<id column="id" property="id"/>
<result column="student_name" property="studentName"/>
<association property="teacher" javaType="com.jx.app.mybatis.entity.Teacher">
<id column="t_id" property="id"/>
<result column="teacher_name" property="teacherName"/>
association>
resultMap>
方式三:通过子查询去查询老师信息,然后通过 resultMap 进行关系映射。
<select id="getStudentById" resultMap="StudentMap">
SELECT * from student WHERE id = #{id}
select>
<resultMap id="StudentMap" type="com.jx.app.mybatis.entity.Student">
<id property="id" column="id"/>
<result property="studentName" column="student_name"/>
<association property="teacher" select="com.jx.app.mybatis.mapper.UserMapper.getTeacher" column="t_id"/>
resultMap>
<select id="getTeacher" resultType="com.jx.app.mybatis.entity.Teacher">
SELECT * from teacher where id = #{t_id}
select>
注意:子查询有一个好处就是可以设置关联对象的延迟加载,只有在使用到延迟对象时才会去执行对应的子查询 SQL 语句然后返回结果。
。方式一:通过连接查询出老师对应的所有学生信息,然后通过 resultMap 进行关系映射。
<select id="getTeacherById" resultMap="TeacherMap">
SELECT * FROM teacher t
LEFT JOIN student s ON t.id = s.t_id
WHERE t.id = #{id}
select>
<resultMap id="TeacherMap" type="com.jx.app.mybatis.entity.Teacher">
<id property="id" column="id"/>
<result property="teacherName" column="teacher_name"/>
<collection property="students" ofType="com.jx.app.mybatis.entity.Student">
<id property="id" column="s_id"/>
<result property="studentName" column="student_name"/>
collection>
resultMap>
方式二:分步查询老师信息和学生信息,然后通过 resultMap 映射关联。
<select id="getTeacherById" resultMap="TeacherMap">
SELECT * FROM teacher t
WHERE t.id = #{id}
select>
<resultMap id="TeacherMap" type="com.jx.app.mybatis.entity.Teacher">
<id property="id" column="id"/>
<result property="teacherName" column="teacher_name"/>
<collection property="students" ofType="com.jx.app.mybatis.entity.Student"
select="com.jx.app.mybatis.mapper.UserMapper.getStudent"
column="id">
collection>
resultMap>
<select id="getStudent" resultType="com.jx.app.mybatis.entity.Student">
select * from student where t_id = #{tid}
select>
根根据特定的条件动态拼接 SQL 语句的功能,它就是解决在拼接 SQL 时字符串问题。
if 标签:会根据标签的 test 属性对应表达式返回 true 或者 false 决定是否拼接到 SQL 语句中。
// 使用 if 进行多条件查询
List<User> DynamicSQL(User user);
<select id="DynamicSQL" resultType="com.jx.app.mybatis.entity.User">
select * from user
where 1=1
<if test="userName != null and userName != ''">
and user_name = #{userName}
if>
<if test="money != null and money != ''">
and money = #{money}
if>
select>
where 标签:动态生成 where 条件语句,根据拼接 SQL 在前面添加或去掉 and、or 关键字,如果没有条件就不会有 where 关键字。
<select id="DynamicSQL" resultType="com.jx.app.mybatis.entity.User">
select * from user
<where>
<if test="userName != null and userName != ''">
and user_name = #{userName}
if>
<if test="money != null and money != ''">
or money = #{money}
if>
where>
select>
trim 标签:如果拼接 SQL 字符串有内容可以在内容前后添加内容,如果没有内容也没有任何效果。
<select id="DynamicSQL" resultType="com.jx.app.mybatis.entity.User">
select * from user
<trim prefix="where" suffix="" prefixOverrides="and|or" suffixOverrides="">
<if test="userName != null and userName != ''">
and user_name = #{userName}
if>
<if test="money != null and money != ''">
or money = #{money}
if>
trim>
select>
chose…when…otherwise 标签:相当于 if…eles if…else,指定条件选择一个进行执行,其它内容不执行。
<select id="DynamicSQL" resultType="com.jx.app.mybatis.entity.User">
select * from user
<where>
<choose>
<when test="userName != null and userName != ''">
user_name = #{userName}
when>
<when test="money != null and money != ''">
money = #{money}
when>
<otherwise>
id = #{id}
otherwise>
choose>
where>
select>
注意:这个条件选择执行只会满足一个,要么是 when 中的一个要么就是 otherwise。
foreach 标签:如果参数是集合类型,可以通过标签遍历集合中内容,例如进行批量删除、增加等操作。
// 批量删除
int deleteBatch(@Param("ids") List<String> ids);
<delete id="deleteBatch">
delete from user
<where>
id in
<foreach collection="ids" item="id" close=")" open="(" separator="," index="i">
#{id}
foreach>
where>
delete>
sql 标签:对常用的 SQL 语句进行一个抽取,如果在其它地方使用直接引用。
<sql id="CommonSQL">
id,user_name,password,money
sql>
<select id="selectAll" resultType="com.jx.app.mybatis.entity.User">
select <include refid="CommonSQL"/> from user
select>
一级缓存:这个级别缓存是 SqlSession 级别的缓存,通过同一个 SqlSession 对象去查询数据,那么在第二次查询就会去缓存中获取而不会去查询数据库,这种缓存是默认开启的。
@Test
public void test4(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper1 = sqlSession.getMapper(UserMapper.class);
List<User> allUser1 = userMapper1.findAllUser();
System.out.println(allUser1);
// 即使是不同 mapper 对象,但是是同一个 sqlSession,那么第二次就会去缓存中取数据
UserMapper userMapper2 = sqlSession.getMapper(UserMapper.class);
List<User> allUser2 = userMapper2.findAllUser();
System.out.println(allUser2);
}
一级缓存失效:
// 手动清空缓存
sqlSession.clearCache();
二级缓存:二级缓存是 SqlSessionFactory 级别的缓存,通过同一个 SqlSessionFactory 创建的 SqlSession 去查询结果会被缓存,那么第二次去执行相同查询就会去缓存中查询,二级缓存需要手动开启。
<settings>
<setting name="cacheEnabled" value="true"/>
settings>
<cache />
@Test
public void test5(){
SqlSession sqlSession1 = MybatisUtils.getSqlSession();
SqlSession sqlSession2 = MybatisUtils.getSqlSession();
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
List<User> users1 = mapper1.selectAll();
System.out.println(users1);
sqlSession1.commit();
// 同一个 SqlSessionFactory 获取的不同 SqlSession 查询结果会被缓存
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
List<User> users2 = mapper2.selectAll();
System.out.println(users2);
}
注意事项:
二级缓存相关设置:在 mapper 映射文件中的 cache 标签可以设置以下属性。
<cache eviction="LRU" flushInterval="1000" size="10" readOnly="false"/>
Mybatis 缓存查询顺序:
第三方缓存工具 EHCache:
<dependency>
<groupId>org.mybatis.cachesgroupId>
<artifactId>mybatis-ehcacheartifactId>
<version>1.2.1version>
dependency>
<ehcache
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<diskStore path="E:\ehcache"/>
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="true"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"/>
<cache
name="cloud_user"
eternal="false"
maxElementsInMemory="5000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
memoryStoreEvictionPolicy="LRU"/>
ehcache>
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
导入依赖:
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelperartifactId>
<version>5.3.3version>
dependency>
设置 mybatis 插件:
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"/>
plugins>
使用方式:
@Test
public void test6(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 开启分页,在下一个查询语句会进行分页拦截,参数一:页码,参数二:一页数量
PageHelper.startPage(1,5);
List<User> users2 = mapper.selectAll();
// 参数是查询后的 list 结果
PageInfo<User> pageInfo = new PageInfo<>(users2);
System.out.println(pageInfo.getList());
}