简介
特性
- MyBatis是支持定制化SQL、存储过程以及高级映射的优秀的持久层框架。
- MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。
- MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和ava的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。
- MyBatis是一个半自动的ORM(Object Relation Mapping)框架。
与其他持久化层技术对比
JDBC
- SQL夹杂在ava代码中耦合度高,导致硬编码内伤。
- 维护不易且实际开发需求中SQL有变化,频繁修改的情况多见。
- 代码冗长,开发效率低
Hibernate和JPA
- 操作简便,开发效率高。
- 程序中的长难复杂SQL需要绕过框架。
- 内部自动生产的SQL,不容易做特殊优化。
- 基于全映射的全自动框架,大量字段的POJO进行部分映射时比较因难。
- 反射操作太多,导致数据库性能下降。
搭建MyBatis
在resources下创建目录的时候应该使用/
分割,而不是使用.
,否则创建出来的是假包。
以包为单位引入映射文件要求mapper接口所在的包要和映射文件所在的包一致且mapper接口要和映射文件的名字一致。
核心配置文件
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf8"/>
需要设置字符集,否则会出现错误。
<?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?characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="159123zxc"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
</configuration>
创建mapper接口
MyBatis中的mapper接口相当于以前的dao。但是区别在于,mapper仅仅是接口,我们不需要提供实现类。
public interface UserMapper {
int insertUser();
}
面向接口配置文件
MyBatis面向接口编程的两个一致,映射文件的namespace要和mapper接口的全类名一致;映射文件中的SQL语句的id要和mapper接口的中的方法名一致。
<?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.atguigu.mybatis.mapper.UserMapper">
<insert id="insertUser">
insert into t_user values(null,'admin','1234560',23,'男','[email protected]')
</insert>
</mapper>
测试
SqlSession:代表Java程序和数据库之间的对话。
注意在修改完成后需要提交,程序不会帮我们自动提交,也可以在openSession
处设置参数为true自动提交。
查询功能的标签必须设置resultType设置默认的映射关系或resultMap设置自定义的映射关系。可以在配置文件中设置<typeAliases>
标签设置类型别名,写起来更方便。,或者<package>
标签以包为单位,将包下所有的类型设置默认的类型别名。
public class MyBatisTest {
@Test
public void testMyBatis() throws Exception {
//加载核心配置文件
InputStream is = Resources.getResourceAsStream("mybatis.xml");
//获取SqlSessionFactoryBuilder
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//获取SqlSessionFactory
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
//获取SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取mapper接口对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//测试功能
int result = mapper.insertUser();
System.out.println("result:"+result);
//提交事务
sqlSession.commit();
}
}
MyBatis获取参数值的两种方式
MyBatis获取参数值的两种方式:${}
和#{}
。${}
的本质是字符串拼接,#{}
的本质是占位符赋值。
${}
使用字符串拼接的方式拼接sql,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号;但是#{}
使用占位符赋值的方式拼接sql,此时为字符串类型或日期类型的字段进行赋值时,可以自动添加单引号。
获取参数
- mapper接口方法的参数为单个的字面量类型,可以通过
${}
和#{}
以任意的名称获取参数值,但是需要注意${}
的单引号问题。 - mapper接口方法的参数为多个时,此时MyBatis会将这些参数放在一个map集合中,以两种方式进行存储:以arg0,arg1...为键,以参数为值;以param1,param2...为键,以参数为值。因此只需要通过
#{}
和${}
以键的方式访问值即可,但是需要注意${}
的单引号问题。 - 若mapper接口方法的参数有多个时,可以手动将这些参数放在一个map中存储,只需要通过
#{}
和${}
以键的方式访问值即可,但是需要注意${}
的单引号问题。 - mapper接口方法的参数是实体类类型的参数,只需要通过
#{}
和${}
以属性的方式访问属性值即可,但是需要注意${}
的单引号问题。 - 使用@Param注解命名参数,
User checkLoginByParam(@Param("username") String username, @Param("password") String password);
,相当于第二种和第三种情况的结合。此时MyBatis会将这些参数放在一个map集合中,以两种方式进行存储:以@Param注解的值为键,以参数为值;以param1,param2...为键,以参数为值。
实例
public void testGetUserByUsername(){
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class);
User user = mapper.getUserByUsername("admin");
System.out.println(user);
}
<!--获取单个参数-->
<select id="getUserByUsername" resultType="User">
select * from t_user where username = #{username}
</select>
<!--获取多个参数-->
<select id="checkLogin" resultType="User">
select * from t_user where username = #{arg0} and password = #{arg1}
</select>
Sql语句
@MapKey
//将每条数据转换的Map集合作为值,以某个字段的值作为
@MapKey("id")
Map<String,Object> getAllUserToMap();
模糊查找
<!--使用${}的模糊查找-->
<select id="getUserByLike" resultType="User">
select * from t_user where username like '%${username}%'
</select>
<!--使用#{}和concat的模糊查找-->
<select id="getUserByLike" resultType="User">
select * from t_user where username like concat('%',#{username},'%')
</select>
<!--最常用的方式-->
<select id="getUserByLike" resultType="User">
select * from t_user where username like "%"#{username}"%"
</select>
批量删除
注意#{}
会自动添加单引号,因此使用${}
。
<delete id="deleteMore">
delete from t_user where id in (${ids})
</delete>
动态设置表名
<select id="getUserByTableName" resultType="User">
select * from ${tableName}
</select>
获取功能自增的主键
因为添加操作的返回值是固定的,因此将主键值作为对象的一个属性返回。
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
insert into t_user values(null,#{username},#{password},#{age},#{sex},#{email})
</insert>
常见问题
字段名和属性名不一致问题
在遇到属性值不一致的情况并不会报错,只是会忽略为不一致的属性赋值。
使用字段别名
<select id="getAllEmp" resultType="Emp">
select eid,emp_name empName,age,sex,email from t_emp
</select>
全局设置转换
在MyBatis的配置文件中设置将下划线转换为驼峰命名法。
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
使用resultmMap
<resultMap id="empResultMap" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
</resultMap>
多对一映射问题
级联方式
<resultMap id="empAndDeptResultMap" type="Emp">
<id property="eid" column="eid"></id>
<result property="dept.did" column="did"></result>
<result property="dept.deptName" column="dept_name"></result>
</resultMap>
<select id="getEmpAndDept" resultMap="empAndDeptResultMap">
select * from t_emp left join t_dept on t_emp.did = t_dept.did where t_emp.eid = #{eid}
</select>
使用标签
<resultMap id="empAndDeptResultMapTwo" type="Emp">
<id property="eid" column="eid"></id>
<association property="dept" javaType="Dept">
<id property="did" column="did"></id>
<result property="deptName" column="dept_name"></result>
</association>
</resultMap>
分步查询
首先查询出对应部门的did,然后再调用下一条Sql语句去查询dept信息。
分步查询的好处是延迟加载,第一步只会调用第一天Sql语句,只有在需要dept信息时才会执行第二条Sql语句。但是必须在核心配置文件中设置全局信息,设置lazyLoadingEnabled
开启和aggressiveLazyLoading
关闭。可以在属性中的fetchType
设置单条语句的查询策略是立即加载还是延迟加载。
<!-- EmpMapper.xml-->
<resultMap id="empAndDeptByStepResultMap" type="Emp">
<id property="eid" column="eid"></id>
<!-- select:设置分步查询的Sql的唯一标识,column设置分步查询的条件-->
<association property="dept"
select="com.atguigu.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
column="did" fetchType="eager"></association>
</resultMap>
<!-- Emp getEmpAndDeptByStepOne(@Param("eid") Integer eid);-->
<select id="getEmpAndDeptByStepOne" resultMap="empAndDeptByStepResultMap">
select * from t_emp where eid = #{eid}
</select>
<!-- DeptMapper.xml-->
<select id="getEmpAndDeptByStepTwo" resultType="Dept">
select * from t_dept where did = #{did}
</select>
一对多问题
collection标签
<resultMap id="deptAndEmpResultMap" type="Dept">
<id property="did" column="did"></id>
<result property="deptName" column="dept_name"></result>
<collection property="emps" ofType="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
</collection>
</resultMap>
<!-- Dept getDeptAndEmp(@Param("did") Integer did);-->
<select id="getDeptAndEmp" resultMap="deptAndEmpResultMap">
select * from t_dept left join t_emp on t_dept.did = t_emp.did where t_dept.did = #{did}
</select>
分步查询
<!-- DeptMapper.xml-->
<resultMap id="deptAndEmpByStepResultMap" type="Dept">
<id property="did" column="did"></id>
<result property="deptName" column="dept_name"></result>
<collection property="emps"
select="com.atguigu.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo"
column="did"></collection>
</resultMap>
<!-- Dept getDeptAndEmpByStepOne(@Param("did") Integer did);-->
<select id="getDeptAndEmpByStepOne" resultMap="deptAndEmpByStepResultMap">
select * from t_dept where did = #{did}
</select>
<!-- EmpMapper.xml-->
<select id="getDeptAndEmpByStepTwo" resultType="Emp">
select * from t_emp where did = #{did}
</select>
动态Sql
MyBatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能,它存在的意义是为了解决拼接SQL语句字符串时的痛点问题。
if
根据标签中test属性所对应的表达式决定标签中的内容是否需要拼接到SQL。
<select id="getEmpByCondition" resultType="Emp">
select * from t_emp where 1=1
<if test="empName != null and empName != ''">
emp_name = #{empName}
</if>
<if test="age != null and age != ''">
and age = #{age}
</if>
<if test="sex != null and sex != ''">
and sex = #{sex}
</if>
<if test="email != null and email != ''">
and email = #{email}
</if>
</select>
where
where标签中有内容时,会自动生成where关键字,并且将内容前多余的and或or去掉。当where标签中没有内容时,此where标签没有任何效果。注意where标签不能将内容后面的多余的and和or去掉。
<select id="getEmpByCondition" resultType="Emp">
select * from t_emp
<where>
<if test="empName != null and empName != ''">
emp_name = #{empName}
</if>
<if test="age != null and age != ''">
and age = #{age}
</if>
<if test="sex != null and sex != ''">
and sex = #{sex}
</if>
<if test="email != null and email != ''">
and email = #{email}
</if>
</where>
</select>
trim
若标签中有内容时,prefix|suffix:将trim标签中内容前面或后面添加指定内容;suffixOverrides|prefixOverrides:将trim标签中内容前面或后面去掉指定内容。若标签没有内容时,trim标签没有任何效果。
<select id="getEmpByCondition" resultType="Emp">
select * from t_emp
<trim prefix="where" suffixOverrides="and|or">
<if test="empName != null and empName != ''">
emp_name = #{empName} and
</if>
<if test="age != null and age != ''">
age = #{age} and
</if>
<if test="sex != null and sex != ''">
sex = #{sex} and
</if>
<if test="email != null and email != ''">
email = #{email}
</if>
</trim>
</select>
choose、when、otherwise
choose、when、otherwise相当于if...else。
<select id="getEmpByChoose" resultType="Emp">
select * from t_emp
<where>
<choose>
<when test="empName != null and empName != ''">
emp_name = #{empName}
</when>
<when test="age != null and age != ''">
age = #{age}
</when>
<when test="sex != null and sex != ''">
sex = #{sex}
</when>
<when test="email != null and email != ''">
email = #{email}
</when>
<otherwise>
did = 1
</otherwise>
</choose>
</where>
</select>
foreach
foreach可以用于批量删除和批量添加。
<delete id="deleteMoreByArray">
delete from t_emp where eid in
<foreach collection="eids" item="eid" separator="," open="(" close=")">
#{eid}
</foreach>
</delete>
<!--第二种写法-->
<delete id="deleteMoreByArray">
delete from t_emp where eid in
<foreach collection="eids" item="eid" separator="or">
eid = #{eid}
</foreach>
</delete>
<!--批量添加-->
<insert id="insertMoreByList">
insert into t_emp values
<foreach collection="emps" item="emp" separator=",">
(null,#{emp.empName},#{emp.age},#{emp.sex},#{emp.email},null)
</foreach>
</insert>
Sql标签
将常用的Sql语句用Sql标签封装,需要使用时用include标签引用。
<sql id="empColumns">eid,emp_name,age,sex,email</sql>
<!--使用include标签引用-->
delete <include refid="empColumns"></include> from t_emp
缓存
一级缓存
一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问。
一级缓存默认开启。
一级缓存失效的四种情况
- 不同的SqlSession对应不同的一级缓存。
- 同一个SqlSession但是查询条件不同。
- 同一个SqlSession两次查询期间执行了任何一次增删改操作。
- 同一个SqlSession两次查询期间手动清空了缓存。
二级缓存
二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取。
二级缓存需要手动开启。
使二级缓存失效的情况:两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效。
二级缓存开启
- 在核心配置文件中,设置全局配置属性cacheEnabled="true",默认为true,不需要设置。
- 在映射文件中设置标签
<cache/>
。 - 二级缓存必须在SqlSession关闭或提交之后有效。
- 查询的数据所转换的实体类类型必须实现序列化的接口。
二级缓存配置
参数 | 作用 |
---|---|
eviction | 缓存回收策略 |
flushInterval | 刷新间隔,单位毫秒 |
size | 引用数目,代表缓存最多可以存储的对象 |
readOnly | true是只读缓存;false是读写缓存 |
缓存查询顺序
- 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。
- 如果二级缓存没有命中,再查询一级缓存。
- 如果一级缓存也没有命中,则查询数据库。
- SqlSession关闭之后,一级缓存中的数据会写入二级缓存。
MyBatis的逆向工程
逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源:Java实体类、Mapper接口和Mapper映射文件。
添加依赖和插件
<build>
<!-- 构建过程中用到的插件 -->
<plugins>
<!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.0</version>
<!-- 插件的依赖 -->
<dependencies>
<!-- 逆向工程的核心依赖 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.2</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.8</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
创建逆向工程的配置文件
文件名必须是:generatorConfig.xml
。
设置完成后点击MAVEN->对应项目->Plugins->mybatis-generator->mybatis-generaator:generate。
生成后需要自行生成构造方法和toString方法。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!--
targetRuntime: 执行生成的逆向工程的版本
MyBatis3Simple: 生成基本的CRUD(清新简洁版)
MyBatis3: 生成带条件的CRUD(奢华尊享版)
-->
<context id="DB2Tables" targetRuntime="MyBatis3Simple">
<!-- 数据库的连接信息 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf8"
userId="root"
password="123456">
</jdbcConnection>
<!-- javaBean的生成策略-->
<javaModelGenerator targetPackage="com.atguigu.mybatis.pojo" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- SQL映射文件的生成策略 -->
<sqlMapGenerator targetPackage="com.atguigu.mybatis.mapper" targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- Mapper接口的生成策略 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.atguigu.mybatis.mapper" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 逆向分析的表 -->
<!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
<!-- domainObjectName属性指定生成出来的实体类的类名 -->
<table tableName="t_emp" domainObjectName="Emp"/>
<table tableName="t_dept" domainObjectName="Dept"/>
</context>
</generatorConfiguration>
分页插件
插件依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
设置分页插件
在mybatis-config.xml
文件处设置插件。
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
使用分页
需要在查询功能之前开启分页,在查询功能之后获取分页相关信息。
public void testPageHelper(){
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
PageHelper.startPage(2, 4);
List<Emp> list = mapper.selectByExample(null);
list.forEach(emp -> System.out.println(emp));
} catch (IOException e) {
e.printStackTrace();
}
}
标签:mapper,缓存,emp,MyBatis,where,select
From: https://www.cnblogs.com/xiqin-huang/p/17898648.html