Mybatis框架课程第一天
本日目标:
掌握基于Mapper接口的Mybatis入门使用
熟悉Mybatis使用中配置文件说明,对重要的配置文件中参数配置需要掌握
掌握Mybatis配置文件的相关(常用)配置
熟悉Mybatis动态sql语句的使用
所有开发者,编写映射文件的语句时,必须先在mysql中运行测试,然后复制到映射文件修改占位
回顾
1.基于Mapper接口中使用的设计模式
创建SqlSessionFactory时使用了构建者设计模式
生产SqlSession时使用了工厂模式
获取Mapper接口代理对象时使用了代理模式
2.基于Mapper接口获取代理对象执行流程
参考资源的截图:
注意:一定使用idea打开项目一起跟随点击查看
补充:
基于注解开发映射配置:(可以没有映射文件)
public interface UserMapper {
@Select("select * from user")
List<User> findAll();
@Insert("insert into user(username,birthday,sex,address) value(#{username},#{birthday},#{sex},#{address})")
int addUser(User user);
@Update("update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}")
int updateUser(User user);
@Delete("delete from user where id=#{id}")
int deleteById(Integer id);
}
使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。
选择何种方式来配置映射,以及认为是否应该要统一映射语句定义的形式,完全取决于你和你的团队。 换句话说,永远不要拘泥于一种方式,你可以很轻松的在基于注解和 XML 的语句映射方式间自由移植和切换。(实际开发中,基于单表简单操作可以选择基于注解映射语句,复杂语句建议使用xml映射)
补充:IDEA开发Mybatis插件
1. 基于Mapper接口开发Mybatis补充
1.1 新增操作的问题扩展:
获取新增用户的主键的返回值:在新增用户后,同时还要返回当前新增用户的id值,在插入数据后通过实体的get方法获取主键值
1.1.1 自增主键返回(mysql数据库)
因为id是由数据库的自动增长来实现的,所以就相当于我们要在新增后将自动增长(auto_increment)的值返回。
第一种:
<!--新增后将表中指定主键列(keyColumn)的值赋值给参数实体中指定的属性(keyProperty)
Generated 生成
useGeneratedKeys="true" 使用 生成 主键 为真
-->
<!--在sql语句上添加这三个东西 useGeneratedKeys="true" keyColumn="id" keyProperty="id"
这个 keyColumn="id"表示数据库中的字段是id。 这个 keyProperty="id" 表示实体的属性是id
-->
<insert id="addUser" parameterType="User" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>
第二种:
<insert id="saveUser" parameterType="User">
<!-- 配置保存时获取插入的id
keyColumn:指定数据库列名
keyProperty:指定将查询到的主键值设置到parameterType对象的那个属性
order:指定语句执行顺序,值有before和after,如果不指定就按照定义顺序
resltType:指定结果类型
-->
<!-- 注意那个 order="AFTER",表示它是执行在插入的sql语句之后的
那个 last_insert_id() 是mysql底层的一个方法,返回的就是我们需要的自增主键
-->
<selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
select last_insert_id();
</selectKey>
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>
1.1.2 非自增主键返回(使用mysql的uuid())
使用mysql的uuid()函数生成主键,需要修改表中id字段类型为varchar,长度设置成35位。
同样实体主键id的类型需要为String
<!--数据库没有设置自动自动的方式新增时,返回新增后的主键值-->
<insert id="addUser4" parameterType="User_uuid">
<!-- 使用mysql的uuid()生成主键
执行过程:
首先通过uuid()得到主键,然后将主键设置到user对象的id属性中
-->
<!--
执行过程:
注意到那个order="BEFORE",表示它在下面的那个sql语句执行之前执行。首先通过uuid()得到主键,然后将主键设置到user对象的id属性中(也就是把得到的这个主键,赋值给了 #{id} 括号里面的这个id)
-->
<selectKey keyColumn="id" keyProperty="id" resultType="java.lang.String" order="BEFORE">
select uuid();
</selectKey>
insert into user(id,username,birthday,sex,address) values(#{id},#{username},#{birthday},#{sex},#{address})
</insert>
如果基于java获取uuid值,那么就直接使用insert语句就好
1.1.3 非自增主键返回(使用oracle的序列)(了解)
通过oracle的序列生成主键:
<!--
注意到那个 order="BEFORE"。
上面的mysql用的是 select uuid(); 下面的这个Oracle用到的是 SELECT 序列名.nextval()
-->
<selectKey keyColumn="id" keyProperty="id" order="BEFORE" resultType="java.lang.String">
SELECT 序列名.nextval()
</selectKey>
insert into user(id,username,birthday,sex,address) value(#{id},#{username},#{birthday},#{sex},#{address})
2.1 用户模糊查询
2.1.1 在持久层接口中添加模糊查询方法
/**
* 根据名称模糊查询
* @param username
* @return
*/
List<User> findByName(String username);
2.1.2 在用户的映射配置文件中配置
<!-- 根据名称模糊查询 -->
<select id="findByName" resultType="User" parameterType="String">
select * from user where username like #{username}
</select>
2.1.3 加入模糊查询的测试方法
@Test
public void findByName() throws IOException {
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建构建者对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3.创建SqlSession工厂对象
SqlSessionFactory factory = builder.build(in);
//4.创建SqlSession对象
SqlSession session = factory.openSession();
//5.创建Dao的代理对象
UserDao userDao = session.getMapper(UserDao.class);
//6.执行查询方法
List<User> users = userDao.findByName("%王%");
for(User user : users){
System.out.println(user);
}
//7.事务提交
session.commit();
//8.释放资源
session.close();
in.close();
}
在控制台输出的执行SQL语句如下:
我们在配置文件中没有加入%来作为模糊查询的条件,所以在传入字符串实参时,就需要给定模糊查询的标识%。配置文件中的#{username}也只是一个占位符,所以SQL语句显示为“?”。
2.1.4 模糊查询的另一种配置方式
<!--第一步:修改SQL语句的配置,配置如下:-->
<!-- 根据名称模糊查询 -->
<select id="findByName" parameterType="string" resultType="User">
select * from user where username like '%${value}%'
</select>
<!-- 我们在上面将原来的#{}占位符,改成了${value}。一般简单值参数都使用value参数名-->
//第二步:测试,如下:
/**
* 测试模糊查询操作
*/
@Test
public void testFindByName(){
//6.执行查询一个方法
List<User> users = userDao.findByName("王");
for(User user : users){
System.out.println(user);
}
}
在控制台输出的执行SQL语句如下:
可以发现,我们在程序代码中就不需要加入模糊查询的匹配符%了,这两种方式的实现效果是一样的,但执行的语句是不一样的。
#{}和${}的区别:
${}是字符串拼接,和jdbc的拼接一致,会引起SQL注入攻击
#{}是占位符,类似jdbc中使用?,可以避免注入攻击,因为如果传入字符串值会在值外出设置’ ’
建议:一般不建议使用 ${},会引起注入攻击,但是特殊的操作可以使用,也不要直接使用
使用 ${} 的话会导致 sql 注入。
什么是 SQL 注入呢?比如 select * from user where username = ${value}
value 应该是一个数值吧。然后如果对方传过来的是 zhangsan and name = tom。,如果是${}底层是拼接,把SQL语句直接写进来了。
select * from user where username = zhangsan and name = tom
#select * from user where username = zhangsan;drop table user
如果是#{}
select * from user where username = 'zhangsan and name = tom'
#select * from user where username = 'zhangsan;drop table user'
如果是攻击性的语句呢 zhangsan;drop table user,直接把表给删了
补充:使用模糊查询时,如果也需要解决注入攻击问题,可以使用mysql的内置函数:
concat(’%’,#{username},’%’)
<select id="findByName" parameterType="string" resultType="User">
select * from user where username like concat('%',#{username},'%')
</select>
2. Mybatis的参数深入(熟悉)
2.1 parameterType配置参数
2.1.1 使用说明
一般是单个参数时会使用该属性指定输入参数类型,多个参数一般可以不写(底层自动封装为Map)有框架底层提供默认的占位名标识参数(arg0,arg1,…或param1,param2,…)
使用标签的parameterType属性来设定。该属性的取值可以是基本类型,引用类型(例如:String类型),还可以是实体类类型(POJO类)。同时也可以使用实体类的包装类.
2.1.2 注意事项
基本类型和String我们可以直接写类型名称,也可以使用包名.类名的方式,例如:java.lang.String或string。 实体类类型,目前我们只能使用全限定类名。 究其原因,是mybaits在加载时已经把常用的数据类型注册了别名,从而我们在使用时可以不写包名,而我们的是实体类并没有注册别名,所以必须写全限定类名。 在mybatis的官方文档的说明
这些都是支持的默认别名。我们也可以从源码角度来看它们分别都是如何定义出来的。 可以参考TypeAliasRegistery.class的源码。
2.1.3 注册实体类别名
在mybatis-config.xml中配置
单个别名注册
<typeAliases>
<typeAlias type="com.yaorange.entity.User" alias="User"/>
</typeAliases>
缺点:每个pojo类都要去配置。
解决方案:使用扫描包,扫描指定包下的所有类,扫描之后的别名就是类名(不区分大小写),建议使用的时候和类名一致。
<typeAliases>
<!--type:实体类的全路径。alias:别名,通常首字母大写-->
<!--<typeAlias type="com.yaorange.entity.User" alias="User"/>-->
<package name="com.yaorange.entity"/>
</typeAliases>
补充:可以同时借助在实体类上使用注解@Alias(“自定义别名”),来定义实体别名,而不使用默认类名别名
3. Mybatis的输出结果封装
3.1 resultType配置结果类型
resultType属性可以指定结果集的类型,它支持基本类型和实体类类型。
我们在前面的CRUD案例中已经对此属性进行过应用了。
需要注意的是,它和parameterType一样,如果注册过类型别名的,可以直接使用别名。没有注册过的必须使用全限定类名。例如:我们的实体类此时必须是全限定类名com.yaorange.entity.Users
同时,当是实体类名称是,还有一个要求,实体类中的属性名称必须和查询语句中的结果集列名保持一致,否则无法实现封装。
3.1.1 特殊情况示例
3.1.1.1 修改实体类
实体类代码如下:(此时的实体类属性和数据库表的列名已经不一致了)
public class User implements Serializable {
private Integer userId;
private String userName;
private Date userBirthday;
private String userSex;
private String userAddress;
//省略getter和setter方法
}
3.1.1.2 测试查询结果
@Test
public void findAll() throws IOException {
//6.执行查询方法
List<User> users = userDao.findAll();
for(User user : users){
System.out.println(user);
}
}
为什么userName名称会有值呢?
因为:mysql在windows系统中不区分大小写!
3.1.1.3 修改映射配置 (熟悉)
使用别名查询 ,让sql语句返回的结果集字段名保持和实体属性名一致
<!-- 配置查询所有操作 -->
<select id="findAll" resultType="User">
select id as userId,username as userName,birthday as userBirthday, sex as userSex,address as userAddress from user
</select>
运行结果:
思考: 如果我们的查询很多,都使用别名的话写起来岂不是很麻烦,有没有别的解决办法呢? 请看下一小节。
3.2 resultMap结果类型 (掌握)
resultMap标签可以在查询的列名和实体类的属性名不一致时建立对应关系。从而实现封装。
在select标签中使用resultMap属性指定引用替换原resultType属性即可。同时resultMap可以实现将查询结果映射为复杂类型的pojo,比如在查询结果映射对象中包括pojo和list实现一对一查询和一对多查询。
3.2.1 定义resultMap
<!--建立User实体和数据库表的对应关系
type属性:指定实体类的全限定类名
id属性:给定一个唯一标识,是给查询select标签引用用的。
-->
<resultMap type="com.yaorange.entity.User" id="userMap">
<id column="id" property="userId"/>
<result column="username" property="userName"/>
<result column="sex" property="userSex"/>
<result column="address" property="userAddress"/>
<result column="birthday" property="userBirthday"/>
</resultMap>
<!--
id标签:用于指定主键字段
result标签:用于指定非主键字段
column属性:用于指定数据库列名
property属性:用于指定实体类属性名称
-->
3.2.2 映射配置
<!-- 配置查询所有操作 -->
<select id="findAll" resultMap="userMap">
select * from user
</select>
3.2.3 测试结果
@Test
public void testFindAll() {
List<User> users = userDao.findAll();
for(User user : users) {
System.out.println(user);
}
}
4. 全局配置文件
4.1 配置内容
配置有顺序,顺序错误会报错
4.1.1 mybatis-config.xml中配置的内容和顺序
4.2 properties(属性)
在使用properties标签配置时,我们可以采用两种方式指定属性配置。
4.2.1 第一种 (不推荐使用)
不使用properties文件方式
<properties>
<property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="jdbc.url" value="jdbc:mysql://localhost:3306/eesy"/>
<property name="jdbc.username" value="root"/>
<property name="jdbc.password" value="1234"/>
</properties>
4.2.2 第二种(推荐方式)
4.2.2.1 在classpath(maven项目的java和resources都是classpath路径)下定义db.properties文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb
jdbc.username=root
jdbc.password=mysqlpass
4.2.2.2 properties标签配置
<!-- 配置连接数据库的信息 resource属性:用于指定properties配置文件的位置,要求配置文件必须在类路径下 resource="jdbcConfig.properties"-->
<properties resource="db.properties"></properties>
4.2.3 此时我们的dataSource标签就变成了引用上面的配置
使用${key名}引用属性值
<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>
4.3 settings设置(建议使用)
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
测试:
先修改数据库字段遵循驼峰命名:
然后进行查询测试,看是否能返回实体数据
开启前:
开启后:
补充:log4j的使用
前提:引入log4j的依赖;并且在resources(classpath)中添加log4j.properties;然后在mybatis-config.xml那里去配置一下。
<settings>
<!-- 开启log4j来记录日志 -->
<setting name="logImpl" value="log4j"/>
</settings>
4.4 typeAliases(类型别名)
在前面我们讲的Mybatis支持的默认别名,我们也可以采用自定义别名方式来开发。
4.4.1 自定义别名:
在mybatis-config.xml中配置:
<typeAliases>
<!-- 单个别名定义 -->
<typeAlias alias="user" type="com.itheima.domain.User"/>
<!-- 批量别名定义,扫描整个包下的类,别名为类名(首字母大写或小写都可以) -->
<package name="com.yaorange.entity"/>
<package name="其它包"/>
</typeAliases>
4.5 mappers(映射器)
4.5.1 mapper resource=""(可以使用)
使用相对于类路径的资源 如:类路径也就是src或者被标识为Resources Root的目录
<mapper resource="sqlmap/UserDao.xml" />
4.5.2 mapper class=" " (不建议)
使用mapper接口类路径 如:
<mapper class="com.yaorange.mapper.UserDao"/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
4.5.3 package name="" (实际开发中使用的方式)
注册指定包下的所有mapper接口 如:
<package name="com.yaorange.mapper"/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
实现方式:在config(或resources)定义和mapper接口一样的包名来存储映射文件
4.6 补充:typeHandlers(类型处理器)
类型处理器的作用:在执行语句时,需要将java类型数据存入数据库或将数据库数据设置给java类型属性,用于对数据库数据类型和java数据类型进行互转
如果处理一般的数据那么默认的类型处理器完全够用,但是如果存在特殊的数据时就需要自行定义类型处理器
官方默认存在类型处理器:https://mybatis.org/mybatis-3/zh/configuration.html#typeHandlers
自定义案例需求:
数据库字段favorites是varchar类型,存储爱好,值(‘吃,喝,玩,乐’),java实体属性是List
自定义处理器步骤:
1)定义类实现 org.apache.ibatis.type.TypeHandler
接口, 或继承一个很便利的类 org.apache.ibatis.type.BaseTypeHandler
,泛型为需要转换的java类型
2)在类上使用注解@MappedJdbcTypes(JdbcType.类型关键字):指定转换的数据库类型,使用@MappedTypes(java类型.class):指定java的数据类型
3)在自定义类中重写处理方法
4)注册当前处理器
<!-- mybatis-config.xml -->
<typeHandlers>
<typeHandler handler="自定义类型处理器全路径"/>
</typeHandlers>
参考代码:
实体:
package com.yaorange.entity;
import java.util.Date;
import java.util.List;
public class User_type {
private Integer id;
private String username;// 用户姓名
private List<String> favorites;//爱好
//省略getter和Setter方法
}
数据库结构:
自定义类型处理器代码:
package com.yaorange.typeHandlers;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import org.apache.ibatis.type.TypeHandler;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
@MappedJdbcTypes(JdbcType.VARCHAR)//指定转换的数据库类型
@MappedTypes(List.class)//指定转换的java数据类型
public class ListAsVarcharTypeHandler implements TypeHandler<List<String>> {
@Override
//更新操作使用使用该方法给占位符转配参数
public void setParameter(PreparedStatement preparedStatement, int i, List<String> paramters, JdbcType jdbcType) throws SQLException {
// i:语句中占位符序号
// paramters:参数值
// 默认底层:preparedStatement.setString(i,paramters);
//将参数中传入的List<String>数据转换为自定义的字符串数据
StringBuffer buffer = new StringBuffer();
for (String str : paramters) {
buffer.append(str).append(",");
}
preparedStatement.setString(i,buffer.toString());
}
@Override
//查询时获取到字符串值转换实体需要的List<String>的逻辑
//遍历结果集时,获取指定列数据有两种方式,要么通过列名获取,要么通过列索引获取
public List<String> getResult(ResultSet resultSet, String columnName) throws SQLException {
String favoritesString = resultSet.getString(columnName);// 获取数据库中字符串值
if(favoritesString != null){
return Arrays.asList(favoritesString.split(","));//将字符串转换拆分为数组并转换为List集合
}
return null;
}
@Override
public List<String> getResult(ResultSet resultSet, int columnIndex) throws SQLException {
String favoritesString = resultSet.getString(columnIndex);// 获取数据库中字符串值
if(favoritesString != null){
return Arrays.asList(favoritesString.split(","));//将字符串转换拆分为数组并转换为List集合
}
return null;
}
@Override
public List<String> getResult(CallableStatement callableStatement, int columnIndex) throws SQLException {
String favoritesString = callableStatement.getString(columnIndex);// 获取数据库中字符串值
if(favoritesString != null){
return Arrays.asList(favoritesString.split(","));//将字符串转换拆分为数组并转换为List集合
}
return null;
}
}
全局配置文件配置:
<!--配置注册当前处理器-->
<typeHandlers>
<!--注册自定义类型处理器-->
<typeHandler handler="com.yaorange.typeHandlers.ListAsVarcharTypeHandler"/>
</typeHandlers>
准备Mapper接口和映射文件:
public interface UserMapper_type {
List<User_type> getAll();
int addUser(User_type user);
}
<?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.yaorange.mapper.UserMapper_type">
<select id="getAll" resultType="User_type">
select * from user_type;
</select>
<insert id="addUser" parameterType="User_type">
insert into user_type(username,favorites) values(#{username},#{favorites})
</insert>
</mapper>
测试类代码:
package com.yaorange.typeHandlers;
import java.util.ArrayList;
import com.yaorange.entity.User_type;
import com.yaorange.mapper.UserMapper;
import com.yaorange.mapper.UserMapper_type;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class TypeHandlerTest {
private SqlSession sqlSession;
@Before
public void start() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sessionFactory.openSession();
}
@After
public void end(){
sqlSession.commit();
sqlSession.close();
}
@Test
public void addUserTest() throws IOException {
UserMapper_type mapper = sqlSession.getMapper(UserMapper_type.class);
User_type user_type = new User_type();
user_type.setUsername("zhangsan");
ArrayList<String> favorites = new ArrayList<>();
favorites.add("吃");
favorites.add("喝");
favorites.add("玩");
user_type.setFavorites(favorites);
int i = mapper.addUser(user_type);
if(i > 0){
System.out.println("新增成功");
}
}
@Test
public void getAllTest() throws IOException {
UserMapper_type mapper = sqlSession.getMapper(UserMapper_type.class);
List<User_type> users = mapper.getAll();
// System.out.println(users.get(0).getFavorites().size());
for (User_type user : users) {
System.out.println(user);
}
}
}
5. Mybatis的动态SQL语句(熟悉)
if:
适用于动态的包含where字句的一部分
if的特点是
当if的判断条件满足时,添加if标签中的字句
当if的判断条件不满足时,什么都不添加
choose:
适用于:
当判断的条件为true时,执行一个语句
当判断的条件为false时,执行另一个语句
where和set:
where的作用:
1、当where标签中的语句不为空时,会在语句之前拼接上“where”关键字
2、假如where标签中的语句是以“and”或者“or”开头,那么这个“and”或者“or”会被省略掉
set的作用:
1、假如set标签中的语句不为空,那么会在开头拼接“set”关键字
2、假如set标签中的语句是以“,”结尾,那么这个“,”会被去掉
Trim:
prefix:前缀,在赶回的字符串之前添加什么内容
suffix:后缀,在返回的字符串之后添加什么内容
prefixoverrides:当字符串以其中的内容开头时,该内容会被覆盖
suffixoverrides:当字符串以其中的内容结尾时,该内容会被覆盖
Foreach:
注意:当接口中方法的参数为数组或者集合时,mybatis会自动的把该参数封装成一个map
map的key为参数类型,首字母小写
默认:数组的key为“array”,集合的key为“list”,map的key为“map”
collection:参数在封装成的map中的key值
open:表示字符串以什么开始
close:表示字符串以什么结束
item:给集合中的每一个元素起的别名
separator:集合中元素的分隔符
Mybatis的映射文件中,前面我们的SQL都是比较简单的,有些时候业务逻辑复杂时,我们的SQL是动态变化的,此时在前面的学习中我们的SQL就不能满足要求了。
参考的官方文档,描述如下:
5.1 动态SQL之 if 标签
我们根据实体类的不同取值,使用不同的SQL语句来进行查询。比如在id如果不为空时可以根据id查询,如果username不为空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到。
5.1.1 持久层Dao接口
/**
* 根据用户信息,查询用户列表
* @param user
* @return
*/
List<User> findByUser(User user);
5.1.2 持久层Dao映射配置
<select id="findByUser" resultType="User" parameterType="User">
select * from user where 1=1
<if test="username!=null and username != '' ">
and username like concat('%',#{username},'%')
</if>
<if test="address != null">
and address like '%${address}%'
</if>
</select>
注意:if标签的test属性中写的是对象的属性名, 另外要注意where 1=1 的作用:当你的 字段 值为空时,保证 sql语句还能一直运行不报错
5.1.3 测试
@Test
public void testFindByUser() {
User u = new User();
u.setUsername("王");
u.setAddress("四川");
//6.执行操作
List<User> users = userDao.findByUser(u);
for(User user : users) {
System.out.println(user);
}
}
5.2 动态SQL之 choose 标签
choose有点类似于Java中的switch,常常配合when和otherwise一起来使用。我们来看一个简单的例子:
<select id="getUser2" resultMap="u">
SELECT * FROM user2 WHERE 1=1
<choose>
<when test="id!=null">
AND id=#{id}
</when>
<when test="address!=null">
AND address=#{address}
</when>
<when test="username!=null">
AND user_name LIKE concat('%',#{username},'%')
</when>
<otherwise>
AND 10>id
</otherwise>
</choose>
</select>
在查询条件中,如果用户传来了id,那么我就查询该id的数据,如果用户传来了address,那么我就我们添加address的查询条件,如果用户传来了username,那么我就添加username的查询条件,最后如果用户任何一个查询条件都没有添加进来,那么默认查询条件就是查询id小于10的所有数据。
5.3 Mybatis中简化编写的SQL片段 (掌握)
Sql中可将重复的sql提取出来,使用时用标签引用即可,最终达到sql重用的目的。
5.3.1 定义代码片段
<!-- 抽取重复的语句代码片段 -->
<sql id="defaultSql">
select * from user
</sql>
5.3.2 引用代码片段
<!-- 配置查询所有操作 -->
<select id="findAll" resultType="user">
<include refid="defaultSql"/>
</select>
<!-- 根据id查询 -->
<select id="findById" resultType="UsEr" parameterType="int">
<include refid="defaultSql"/>
where id = #{uid}
</select>
实际开发中一般使用标签定义字段列表
5.4 动态SQL之where标签
为了简化上面where 1=1的条件拼装,我们可以采用where标签来简化开发。
5.4.1 持久层Dao映射配置
<sql id="defaultSql">
select * from user
</sql>
<!-- 根据用户信息查询 -->
<select id="findByUser" resultType="User" parameterType="User">
<include refid="defaultSql"/>
<where>
<if test="username!=null and username != '' ">
and username like #{username}
</if>
<if test="address != null">
and address like #{address}
</if>
</where>
</select>
注意:使用标签,其中包含的标签的语句都建议添加上and,mybatis底层会自动根据情况删除and,但是不会添加and(因为不一定是and,后面也可能是or。它不知道是添加and还是or,所以不能添加,但是可以删除)
5.5 动态标签之set标签
set是我们在更新表的时候使用的元素,通过set元素,我们可以逐字段的修改一条数据,如下:
<update id="update">
UPDATE user2
<set>
<if test="username!=null">
user_name=#{username},
</if>
<if test="password!=null">
password=#{password}
</if>
</set>
WHERE id=#{id}
</update>
在set元素中,如果遇到了逗号,系统会自动将之去除。
5.6 动态标签之bind标签
使用bind元素我们可以预先定义一些变量,然后在查询语句中使用,如下:
<select id="getUserByName" resultMap="u">
<bind name="un" value="username+'%'"></bind>
SELECT* FROM user2 WHERE user_name LIKE #{un}
</select>
5.7 动态标签之foreach标签(熟悉)
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句或批量新增的时候)
传入参数说明:
如果传入参数是List,set,Array,Map,Properties,实际使用时外部标签一般都不写parameterType属性就好,默认全部使用map存储,效率会高一些
子标签的表达式:
数组类型的参数传入时:子标签表达式中,使用代表参数的名称为array(foreach标签collection=“”或if标签的test)(底层:map.put(“array”,我们传入的数组值对象))
Map类型的参数传入时:首先在关联方法中参数前面使用的注解@Param自定义参数别名,子标签中的表达式使用注解中定义的别名
@Param("users") -->foreach标签 collection=“users”或if标签的test=“users......”
List类型的参数传入时:子标签表达式中,使用代表参数的名称为list(foreach标签collection=“”或if标签的test)
多个参数传入时:底层会进行特殊处理,按照参数顺序依次以指定名称作为key**(arg0,arg1…param1,param2…),**以实参列表值为value,如:map.put(“arg0”,id值)或map.put(“param1”,id值)
实际开发中:对于接口方法存在多个参数或集合参数时,一般都是在mapper接口的方法参数前使用@Param(“参数别名”),底层使用map.put(“注解指定别名”,标识参数值)
5.7.1 需求
传入多个id查询用户信息,用下边两个sql实现:
SELECT * FROM USERS WHERE id =10 OR id =89 OR id=16
SELECT * FROM USERS WHERE id IN (10,89,16)
这样我们在进行范围查询时,就要将一个集合中的值,作为参数动态添加进来。 这样我们将如何进行参数的传递?
5.7.2 持久层Dao接口
/**
* 根据id集合查询用户
*/
List<User> findUserByArray(Integer[] ids);
List<User> findUserByList(List<Integer> ids);
//使用注解自定义参数别名
List<User> findUserByMap(@Param("ids") Map<String,Integer> map);
User findUserByIdAndUsername(Integer id,String userame);
//使用注解自定义别名
User findUserByIdAndUsername1(@Param("userId") Integer id,@Param("username") String userame);
5.7.3 持久层Dao映射配置
<?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.yaorange.mapper.UserMapper_sql">
<!--定义公共字段标签-->
<sql id="allColumn">
id,username,birthday,sex,address
</sql>
<!--foreach:遍历参数传入的数组,以指定open开头,以指定的close结尾,以指定separator分隔数据,以指定item名称为数据占位别名-->
<select id="findUserByArray" resultType="User">
select <include refid="allColumn"/> from user
<where>
<if test="array != null and array.length > 0">
<foreach collection="array" open="id in (" close=")" separator="," item="userID">
#{userID}
</foreach>
</if>
</where>
</select>
<select id="findUserByList" resultType="User">
select <include refid="allColumn"/> from user
<where>
<if test="list != null and list.size() > 0">
<foreach collection="list" open="id in (" close=")" separator="," item="userID">
#{userID}
</foreach>
</if>
</where>
</select>
<select id="findUserByMap" resultType="User">
select <include refid="allColumn"/> from user
<where>
<if test="ids != null and ids.size() > 0">
<foreach collection="ids" open="id in (" close=")" separator="," index="key" item="value">
#{value}
</foreach>
</if>
</where>
</select>
<select id="findUserByIdAndUsername" resultType="User">
select <include refid="allColumn"/> from user
<where>
<if test="arg0 != null and arg0 != ''">
and id=#{arg0}
</if>
<if test="arg1 != null and arg1 != ''">
and username=#{arg1}
</if>
</where>
</select>
<select id="findUserByIdAndUsername1" parameterType="map" resultType="User">
select <include refid="allColumn"/> from user
<where>
<if test="userId != null and userId != ''">
and id=#{userId}
</if>
<if test="username != null and username != ''">
and username=#{username}
</if>
</where>
</select>
</mapper>
5.7.4 编写测试方法
package com.yaorange;
import com.yaorange.entity.User;
import com.yaorange.entity.User_other;
import com.yaorange.entity.User_uuid;
import com.yaorange.mapper.UserMapper;
import com.yaorange.mapper.UserMapper_sql;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
public class SqlTest {
private SqlSession sqlSession;
@Before
public void start() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sessionFactory.openSession();
}
@After
public void end(){
sqlSession.commit();
sqlSession.close();
}
@Test
public void findByUserTest() {
UserMapper_sql mapper = sqlSession.getMapper(UserMapper_sql.class);
User user = new User();
// user.setId(41);
user.setUsername("王五");
List<User> users = mapper.findByUser(user);
for (User u : users) {
System.out.println(u);
}
}
@Test
public void findByUser1Test() {
UserMapper_sql mapper = sqlSession.getMapper(UserMapper_sql.class);
User user = new User();
user.setId(41);
// user.setUsername("王五");
List<User> users = mapper.findByUser1(user);
for (User u : users) {
System.out.println(u);
}
}
@Test
public void findUserByArrayTest() {
UserMapper_sql mapper = sqlSession.getMapper(UserMapper_sql.class);
Integer[] ids = {41,43,49};
List<User> users = mapper.findUserByArray(ids);
for (User u : users) {
System.out.println(u);
}
}
@Test
public void findUserByListTest() {
UserMapper_sql mapper = sqlSession.getMapper(UserMapper_sql.class);
Integer[] ids = {41,43};
List<User> users = mapper.findUserByList(Arrays.asList(ids));
for (User u : users) {
System.out.println(u);
}
}
@Test
public void findUserByMapTest() {
UserMapper_sql mapper = sqlSession.getMapper(UserMapper_sql.class);
Map<String, Integer> map = new HashMap<>();
map.put("zhangsan",41);
map.put("lisi",49);
List<User> users = mapper.findUserByMap(map);
for (User u : users) {
System.out.println(u);
}
}
@Test
public void findUserByIdAndUsernameTest() {
UserMapper_sql mapper = sqlSession.getMapper(UserMapper_sql.class);
// User user = mapper.findUserByIdAndUsername(41,null);
// User user = mapper.findUserByIdAndUsername(null,"王五");
User user = mapper.findUserByIdAndUsername(43,"王五");
System.out.println(user);
}
@Test
public void findUserByIdAndUsername1Test() {
UserMapper_sql mapper = sqlSession.getMapper(UserMapper_sql.class);
User user = mapper.findUserByIdAndUsername1(49,null);
// User user = mapper.findUserByIdAndUsername1(null,"王五");
// User user = mapper.findUserByIdAndUsername1(43,"王五");
System.out.println(user);
}
}
补充:使用多个参数的接口方法定义和语句中使用(掌握)
第一种:基于Mybatis底层默认参数命名[arg0,arg1,…]或[param1,param2,…]
User findUserByUsernameAndPassword(String username,String password);
<select id="findUserByUsernameAndPassword" resultType="User">
select * from user where username=#{arg0} and password=#{arg1};
</select>
或:
<select id="findUserByUsernameAndPassword" resultType="com.yaorange.entity.User">
select * from user where username=#{param1} and password=#{param2};
</select>
第二种:使用Map存储多个参数
使用Map存储多个参数值,在语句中直接使用Map中的key标识占位别名(类似于使用一个特殊的对象,map的key就等同于对象的属性名)
User findByMap(Map<String,Object> params);
<select id="findByMap" resultType="User">
select <include refid="allColumns"/> from user where id=#{id} and name=#{name}
</select>
测试代码:
Map<String, Object> map = new HashMap<String, Object>();
map.put("id",41);
map.put("name","张三");
List<User> users = mapper.findByMap(map);
for (User user : users) {
System.out.println(user);
}
运行的语句:
第三种:使用注解标识参数,给参数定义别名(常用方式)
User findUser(@Param("username") String val1,@Param("pass") String val2);
<select id="findUserByUsernameAndPassword" resultType="com.yaorange.entity.User">
select <include refid="allColumns"/> from user where username=#{username} and password=#{pass};
</select>
面试题
1.MyBatis的好处是什么
-
MyBatis把sql语句从Java源程序中独立出来, 放在单独的XML文件中编写,给程序的维护带来了很大便利。 (我们以前直接用原生JDBC操作数据库是怎样?面试题)
-
MyBatis封装了底层JDBC API的调用细节,并能自动将结果集转换成Java Bean对象,大大简化了Java数据库编程的重复工作。
-
因为MyBatis需要程序员自己去编写sql语句,程序员可以结合数据库本身的特点灵活控制sql语句,因此能够实现比hibernate等全自动orm框架更高的查询效率,能够完成复杂查询。
2.MyBatis核心处理类
SqlSession:用于直接执行语句,或者返回mapper接口的代理对象,用以调用接口方法操作语句
参考链接:https://thinkwon.blog.csdn.net/article/details/101293216
1、SqlSessionFactoryBuilder
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2.创建SqlSessionFactory的构建者对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//3.使用构建者创建工厂对象SqlSessionFactory
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(in);
每一个MyBatis的应用程序的入口是SqlSessionFactoryBuilder。
它的作用是通过XML配置文件创建Configuration对象(当然也可以在程序中自行创建),然后通过build方法创建SqlSessionFactory对象。没有必要每次访问Mybatis就创建一次SqlSessionFactoryBuilder,通常的做法是创建一个全局的对象就可以了。
mybatis-config.xml中的配置,最后把xml解析成Configuration这个类。
SqlSessionFactoryBuilder根据传入的数据流(XML)生成Configuration对象,然后根据Configuration对象创建默认的SqlSessionFactory实例。
2、SqlSessionFactory
SqlSessionFactory对象的主要功能是创建SqlSession对象,和SqlSessionFactoryBuilder对象一样,没有必要每次访问Mybatis就创建一次SqlSessionFactory,通常的做法是创建一个全局的对象就可以了。SqlSessionFactory对象一个必要的属性是Configuration对象,它是保存Mybatis全局配置的一个配置对象,通常由SqlSessionFactoryBuilder从XML配置文件创建。
//4.使用SqlSessionFactory生产SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
3、SqlSession
SqlSession对象的主要功能是完成一次数据库的访问和结果的映射,它类似于数据库的session概念,由于不是线程安全的,所以SqlSession对象的作用域需限制方法内。SqlSession的默认实现类是DefaultSqlSession,它有两个必须配置的属性:Configuration和Executor。Configuration前文已经描述这里不再多说。SqlSession对数据库的操作都是通过Executor来完成的。
SqlSession :默认创建DefaultSqlSession 并且开启一级缓存,创建执行器 、赋值。
SqlSession有一个重要的方法getMapper,顾名思义,这个方式是用来获取Mapper对象的。
什么是Mapper对象?根据Mybatis的官方手册,应用程序除了要初始并启动Mybatis之外,还需要定义一些接口,接口里定义访问数据库的方法,存放接口的包路径下需要放置同名的XML配置文件。
SqlSession的getMapper方法是联系应用程序和Mybatis纽带,应用程序访问getMapper时,Mybatis会根据传入的接口类型和对应的XML配置文件生成一个代理对象,这个代理对象就叫Mapper对象。应用程序获得Mapper对象后,就应该通过这个Mapper对象来访问Mybatis的SqlSession对象,这样就达到里插入到Mybatis流程的目的。
4、Executor
Executor对象在创建Configuration对象的时候创建,并且缓存在Configuration对象里。Executor对象的主要功能是调用StatementHandler访问数据库,并将查询结果存入缓存中(如果配置了缓存的话)。
5、StatementHandler
StatementHandler是真正访问数据库的地方,并调用ResultSetHandler处理查询结果。
6、ResultSetHandler
处理查询结果。
3.解决数据库字段名和实体类属性名不一致的问题
查询数据的时候,发现查不到userName的信息,
User{id=‘2’, userName=‘null’, sex=0, birthday=‘1993-09-05’}
原因:数据库的字段名是user_name,POJO中的属性名字是userName。两端不一致,造成mybatis无法填充对应的字段信息。
解决方案1:在sql语句中使用as别名:
<select id="queryUserById" resultType="com.pojo.User">
select
tuser.id as id,
tuser.user_name as userName,
tuser.birthday as birthday,
tuser.sex as sex
from
tb_user tuser
where tuser.id = #{id};
</select>
解决方案2: 参考后面的resultMap –mapper具体的配置的时候
一定对照查询语句的结果集定义
<resultMap id="userMap" type="User_Other">
<!--说明数据库表主键列关联的实体类主键属性
column,表示数据库中的字段。property,表示实体中的字段
-->
<id column="id" property="userId"/>
<!--配置非主键列-->
<result column="username" property="userName"/>
<result column="birthday" property="userBirthday"/>
<result column="sex" property="userSex"/>
<result column="address" property="userAddress"/>
</resultMap>
解决方案3:参考驼峰匹配 — mybatis-config.xml 的时候
<!--Underscore,表示“下划线,强调”。CamelCase,“驼峰式命名法”。Camel,“骆驼”-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
**4.#{}和${}**的区别是什么?
#{}是预编译处理,${}是字符串替换。
Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
Mybatis在处理 时 , 就 是 把 {}时, 就是把 时,就是把{}直接替换成变量的值。 底层通过字符串拼接
使用#{}可以有效的防止SQL注入,提高系统安全性。
#{}和${}的区别:
${}是字符串拼接,和jdbc的拼接一致,会引起SQL注入攻击
#{}是占位符,类似jdbc中使用?,可以避免注入攻击,因为如果传入字符串值会在值外出设置’ ’
建议:一般不建议使用 ${},会引起注入攻击,但是特殊的操作可以使用,也不要直接使用
使用 ${} 的话会导致 sql 注入。什么是 SQL 注入呢?利用现有应用程序,将(恶意)的SQL命令注入到后台数据库引擎执行的能力。比如 select * from user where username = v a l u e 。 v a l u e 应 该 是 一 个 数 值 吧 。 然 后 如 果 对 方 传 过 来 的 是 z h a n g s a n a n d n a m e = t o m 。 如 果 是 {value} 。 value 应该是一个数值吧。然后如果对方传过来的是 zhangsan and name = tom。如果是 value。value应该是一个数值吧。然后如果对方传过来的是zhangsanandname=tom。如果是{}底层是拼接,把SQL语句直接写进来了。
select * from user where username = zhangsan and name = tom
#select * from user where username = zhangsan;drop table user
如果是#{}
select * from user where username = 'zhangsan and name = tom'
#select * from user where username = 'zhangsan;drop table user'
如果是攻击性的语句呢 zhangsan;drop table user,直接把表给删了
${}
select * from user where username = zhangsan;drop table user
如果是#{}
select * from user where username = 'zhangsan;drop table user'
补充:使用模糊查询时,如果也需要解决注入攻击问题,可以使用mysql的内置函数:
concat(’%’,#{username},’%’)
<select id="findByName" parameterType="string" resultType="User">
select * from user where username like concat('%',#{username},'%')
</select>
5.MyBatis和ORM的区别(Object Relational Mapping)
Object-Relationl Mapping,它的作用是在关系型数据库和对象之间作一个映射,这样,我们在具体的操作数据库的时候,就不需要再去和复杂的SQL语句打交道,只要像平时操作对象一样操作它就可以了
mybatis属于半orm,因为sql语句需要自己写。
与其他比较标准的 ORM 框架(比如 Hibernate )不同, mybatis 并没有将 java 对象与数据库关联起来,而是将 java 方法与 sql 语句关联起来,mybatis 允许用户充分利用数据库的各种功能,例如存储、视图、各种复杂的查询以及某些数据库的专有特性。
自己写 sql 语句的好处是,可以根据自己的需求,写出最优的 sql 语句。灵活性高。但是,由于是自己写 sql 语句,导致平台可移植性不高。MySQL 语句和 Oracle 语句不同
6.MyBatis实现分页功能
1) 原始方法,使用 limit,需要自己处理分页逻辑:
对于 mysql 数据库可以使用 limit ,如:
select * from table limit 5; --返回前5行
select * from table limit 0,5; --同上,返回前5行
select * from table limit 5,10; --返回6-15行
对于 oracle 数据库可以使用 rownum ,如:
–如:从表Sys_option(主键为sys_id)中从第10条记录开始检索20条记录,语句如下
SELECT * FROM (SELECT ROWNUM R,t1.* From Sys_option where rownum < 30 ) t2
Where t2.R >= 10
2) 使用PageHelper插件,这是目前比较常见的方法:
由于篇幅太长详细过程看原文:www.cnblogs.com/digdeep/p/4608933.html
7.MyBatis接口绑定的几种方式
接口绑定有两种方式
1、使用注解,在接口的方法上面添加@Select@Update等注解,里面写上对应的SQL语句进行SQL语句的绑定。
2、通过映射文件xml方式进行绑定,指定xml映射文件中的namespace对应的接口的全路径名
什么时候用注解绑定?什么时候用xml绑定?
当SQL语句比较简单的时候,使用注解绑定就可以了,当SQL语句比较复杂的话,使用xml方式绑定,一般用xml方式绑定比较多
8.MyBatis映射文件中A标签引用B标签,如果B标签在A的后面定义,可以吗?
虽然 Mybatis 解析 Xml 映射文件是按照顺序解析的,但是,被引用的 B 标签依然可以定义在任何地方,Mybatis 都可以正确识别。
原理:
Mybatis 解析 A 标签时,发现引用了 B 标签,未解析到 B 标签,此时会把 A 标签标记为未解析状态;
继续解析下面内容,把剩下解析完之后,再解析标记为未解析的标签;
此时已解析到 B 标签,此时再解析A标签时,B标签已经存在,A 标签也就顺利解析完成。
9. MyBatis不同映射文件中的id是否可以重复?
可以重复,但是需要映射文件的namespace不同
不同的 Xml 映射文件,如果配置了 namespace
,那么 id
可以重复;如果没有配置 namespace
,那么 id
不能重复。
原因就是 namespace
+id
是作为 Map<String, MapperStatement>
的 key使用的,如果没有 namespace
,就剩下 id
,那么,id
重复会导致数据互相覆盖。
有了 namespace
,自然 id
就可以重复,namespace
不同,namespace
+id
自然也就不同。
10.MyBatis缓存机制
缓存机制减轻数据库压力,提高数据库性能
mybatis的缓存分为两级:一级缓存、二级缓存
一级缓存:
一级缓存为 sqlsesson 缓存,缓存的数据只在 SqlSession 内有效。在操作数据库的时候需要先创建 SqlSession 会话对象,在对象中有一个 HashMap 用于存储缓存数据,此 HashMap 是当前会话对象私有的,别的 SqlSession 会话对象无法访问。
具体流程:
第一次执行 select 完毕会将查到的数据写入 SqlSession 内的 HashMap 中缓存起来
第二次执行 select 会从缓存中查数据,如果 select 同传参数一样,那么就能从缓存中返回数据,不用去数据库了,从而提高了效率
注意:
1、如果 SqlSession 执行了 DML 操作(insert、update、delete),并 commit 了,那么 mybatis 就会清空当前 SqlSession 缓存中的所有缓存数据,这样可以保证缓存中的存的数据永远和数据库中一致,避免出现差异
2、当一个 SqlSession 结束后那么他里面的一级缓存也就不存在了, mybatis 默认是开启一级缓存,不需要配置
3、 mybatis 的缓存是基于 [namespace:sql语句:参数] 来进行缓存的,意思就是, SqlSession 的 HashMap 存储缓存数据时,是使用 [namespace:sql:参数] 作为 key ,查询返回的语句作为 value 保存的
二级缓存:
二级缓存是mapper
级别的缓存,也就是同一个 namespace 的 mapper.xml ,当多个 SqlSession 使用同一个 Mapper 操作数据库的时候,得到的数据会缓存在同一个二级缓存区域
二级缓存默认是没有开启的。需要在 setting 全局参数中配置开启二级缓存
开启二级缓存步骤:
1、mybatis-config.xml
配置全局变量开启二级缓存
<settings>
<setting name="cacheEnabled" value="true"/><!-- 默认是false:关闭二级缓存 -->
<settings>
2、在userMapper.xml
中配置
<!-- 当前mapper下所有语句开启二级缓存 -->
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>
这里配置了一个 LRU算法 缓存,并每隔60秒刷新,最大存储512个对象,而返回的对象是只读的
若想禁用当前select
语句的二级缓存,添加 useCache="false"
修改如下:
<select id="getCountByName" parameterType="string" resultType="int" useCache="false">
</select>
具体流程:
1.当一个sqlseesion
执行了一次select
后,在关闭此session
的时候,会将查询结果缓存到二级缓存
2.当另一个sqlsession
执行select
时,首先会在他自己的一级缓存中找,如果没找到,就回去二级缓存中找,找到了就返回,就不用去数据库了,从而减少了数据库压力提高了性能
注意:
1、如果 SqlSession
执行了 DML 操作(insert、update、delete)
,并 commit
了,那么 mybatis
就会清空当前mapper
缓存中的所有缓存数据,这样可以保证缓存中的存的数据永远和数据库中一致,避免出现差异
2、mybatis
的缓存是基于[namespace:sql语句:参数]
来进行缓存的,意思就是,SqlSession
的 HashMap
存储缓存数据时,是使用 [namespace:sql:参数]
作为 key
,查询返回的语句作为 value
保存的。
11.简述 Mybatis 的动态 SQL,列出常用的 6 个标签及作用
动态 SQL 是 MyBatis 的强大特性之一 基于功能强大的 OGNL 表达式。
动态 SQL 主要是来解决查询条件不确定的情况,在程序运行期间,根据提交的条件动态的完成查询。
常用的标签:
: 进行条件的判断
:在判断后的 SQL 语句前面添加 WHERE 关键字,并处理 SQL 语句开始位置的AND 或者 OR 的问题
:可以在 SQL 语句前后进行添加指定字符 或者去掉指定字符.
: 主要用于修改操作时出现的逗号问题
:类似于 java 中的 switch 语句。在所有的条件中选择其一
:迭代操作
12.Mybatis 结果集的映射方式有几种,并分别解释每种映射方式如何使用。
自动映射 ,通过 resultType 来指定要映射的类型即可。
自定义映射 ,通过 resultMap 来完成具体的映射规则,指定将结果集中的哪个列映射到对象的哪个属性。
13.简述 Mybatis 提供的两级缓存,以及缓存的查找顺序
(1)MyBatis 的缓存分为一级缓存和 二级缓存。一级缓存是 SqlSession 级别的缓存,默认开启。
二级缓存是 NameSpace 级别(Mapper)的缓存,多个 SqlSession 可以共享,使用时需要进行配置开启。
(2)缓存的查找顺序:二级缓存 => 一级缓存 => 数据库
14.简述如何在 myBatis 中的增删改操作获取到对数据库的影响条数
直接在 Mapper 接口的方法中声明返回值即可
15.解释 MyBatis 中 @Param 注解的作用
通过该注解来指定 Mybatis 底层在处理参数时封装 Map 使用的key,方便在 SQL 映射文件中取参数。