Mybatis
JDBC
sql夹在Java代码中,耦合度高,维护不易且实际开发需求中sql有变化,频繁修改情况多
Hibernate和JPA
基于全映射的全自动框架,大量字段的POJO进行部分映射时比较困难,导致数据库性能下降
Mybatis
sql和Java代码分开,功能边界清晰,一个专注业务,一个专注数据
package com.tang.bean;
public class User {
private int id;
private String username;
private String password;
private int permission;
public User() {
}
public User(int id, String username, String password, int permission) {
this.id = id;
this.username = username;
this.password = password;
this.permission = permission;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getPermission() {
return permission;
}
public void setPermission(int permission) {
this.permission = permission;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", permission=" + permission +
'}';
}
}
package com.tang.dao;
import com.tang.bean.User;
public interface UserDao {
public User getUserById(int id);
public int updateUser();
public int deleteUser();
public int insertUser();
}
dataSource.properties
data.driver=com.mysql.cj.jdbc.Driver data.url=jdbc:mysql://localhost:3306/menumanage?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC data.username=root data.password=admin
mybatis-config
<?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="dataSource.properties"></properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<!--配置连接池-->
<dataSource type="POOLED">
<!-- ${} 取出配置文件中的值-->
<property name="driver" value="${data.driver}"/>
<property name="url" value="${data.url}"/>
<property name="username" value="${data.username}"/>
<property name="password" value="${data.password}"/>
</dataSource>
</environment>
</environments>
<!--引入编写的每一个接口的实现文件-->
<mappers>
<mapper resource="UserDao.xml"/>
</mappers>
</configuration>
UserDao.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:名称空间,写接口的全类名,相当于告诉Mybatis这个配置文件是实现哪个接口的
-->
<mapper namespace="com.tang.dao.UserDao">
<!--select:用来定义一个查询操作
id:方法名,相当于这个配置是对某个方法的实现
resultType:指定方法运行后的返回值类型(查询操作必须指定)
-->
<select id="getUserById" resultType="com.tang.bean.User">
select * from usertb where id = #{id}
</select>
<update id="updateUser">
update usertb set username = #{username}, password = #{password}, permission = #{permission} where id = #{id}
</update>
<delete id="deleteUser">
delete from usertb where id = #{id}
</delete>
<insert id="insertUser">
insert into usertb(username, password, permission) value(#{username},#{password},#{permission})
</insert>
</mapper>
查询语句传参问题
现象:
- 单个参数:
- #{任意字符串}
- 多个参数:
- 例如 public User getUserByIdAndName(Integer id, String username);
- #{参数名} 无效
- 原因:如果传入了多个参数,mybatis会自动将这些参数封装在一个map中,封装时使用的key就是参数的索引
- Map<String, Object> map = new Hash<>();
- map.put("1", value);
- map.put("2", value);
解决办法一:使用@Param,告诉mybatis封装map的时候使用指定的key
public User getUserByIdAndName(@Param("id") Integer id, @Param("username") String username);
解决办法二:当参数属于我们业务POJO时,直接传递POJO
解决办法三:封装多个参数为map,直接传递
Oracle指定jdbcType
如果null被当作值来传递,mysql可以插入null,oracle不知道null是什么类型
所以Oracle数据库需要指定jdbcType
id = #{id, jdbcType=INT}
取值
mybatis中的两种取值方式:
#{属性名}:参数预编译的方式,参数的位置都是使用?替代,安全,可反正sql注入攻击
${属性名}:不是参数预编译方式,而是直接和sql语句进行拼串,不安全
select * from usertb where id=${id} and username=#{username}
select * from usertb where id=1 and username=?
查询返回list
如果返回的是集合,resultType中类型为集合中元素类型
<select id="getAllUsers" resultType="com.tang.bean.User"> select * from usertb </select>
返回结果封装为Map
返回单条记录
//单条记录封装为一个map,key为列名,value为值 public Map<String, Object> getUserById(Integer id);
<!--查询返回一个map-->
<select id="getUserById" resultType="map">
select * from usertb where id=#{id}
</select>
返回多条记录
//多条记录封装为一个map,key为这条记录的主键,value为封装好的记录对象
//把查询的记录的id作为key封装一条记录
@MapKey("id")
public Map<Integer, User> getAllUsers();
<!--查询多个返回一个map,注意:resultType的值必须为"com.tang.bean.User",否则map不会将一条记录对象封装为User--> <select id="getAllUsers" resultType="com.tang.bean.User"> select * from usertb </select>
resultMap自定义封装规则
默认mybatis自动封装结果集:按照列名和属性名一一对应的规则(注意:数据库不区分大小写)
如果列名和属性名不一一对应:
(1)开启驼峰命名规则(aaa_bbb映射为aaaBbb)
(2)起别名
<mapper namespace="com.tang.dao.UserDao">
<select id="getUserById" resultType="com.tang.bean.User">
select id,username myname,passwod mypassword,gender mygender from usertb where id=#{id}
</select>
</mapper>
(3)resultMap自定义封装规则
<mapper namespace="com.tang.dao.UserDao">
<select id="getUserById" resultMap="myuser">
select * from usertb where id=#{id}
<select>
<!--自定义结果集(resultMap):自己定义每一列数据和JavaBean的映射规则
type:指定为哪个JavaBean自定义封装规则,值为全类名
id:resultMap的唯一标识
-->
<resultMap type="com.tang.bean.User" id="myuser">
<!--指定主键列的对应规则
column:指定哪一列是主键列
property:指定JavaBean的哪个属性封装该列数据
-->
<id property="id" column="id"/>
<!--普通列-->
<result property="myname" column="username"/>
<result property="mypassword" column="password"/>
<result property="mygender" column="gender"/>
</resultMap>
</mapper>
动态SQL
if标签
test:编写判断条件
<mapper namespace="com.tang.dao.UserDao">
<select id="getUserByCondition" resultMap="myuser">
select * from usertb where
<if test="id!=null">
id > #{id}
</if>
<if test="username != null and username.equals("")">
and username like #{username}
</if>
</select>
</mapper>
trim标签
作用:截取字符串
prefix:为下面的sql整体添加一个前缀
prefixOverrides:删除整体字符串前面多余的字符
suffix:为下面的sql整体删除一个前缀
suffixOverrides:删除整体字符串后面多余的字符
<mapper namespace="com.tang.dao.UserDao">
<select id="getUserByCondition" resultMap="myuser">
select * from usertb
<trim prefix="where" prefixOverrides="and" suffixOverrides="and">
<if test="id!=null">
id > #{id}
</if>
<if test="username != null and username.equals("")">
and username like #{username}
</if>
</trim>
</select>
</mapper>
foreach标签
collection:指定要遍历的集合的key
open:以什么开始
close:以什么结束
item:变量名,每次遍历出来的元素起一个变量名以方便引用
separator:每次遍历的元素的分隔符
index:索引
如果遍历的是一个list,index保存当前遍历的元素的索引,item保存当前遍历的元素的值
如果遍历的是一个map,index保存当前遍历的元素的key,item保存当前遍历的元素的值
public List<User> getUserByIdIn(@Param("ids") List<Interger> ids);
<mapper namespace="com.tang.dao.UserDao">
<select id="getUserByCondition" resultMap="myuser">
select * from usertb where id IN
<foreach collection="ids" open="(" close=")" separator="," item="id_item">
#{id_item}
</foreach>
</select>
</mapper>
set标签
set标签去除可能多余的 ,
<mapper namespace="com.tang.dao.UserDao">
<update id="updateUser">
update usertb set
<set>
<if test="username!=null and !name.equals("")">
username=#{username},
</if>
<if test="password!=null and !password.equals("")">
username=#{password},
</if>
<if test="permission!=null">
permission=#{permission}
</if>
</set>
<where>
id=#{id}
</where>
</update>
</mapper>
缓存
缓存是一般的ORM框架都会提供的功能,目的是提升查询的效率和减少数据库的压力
缓存有关设置
- 全局setting的cacheEnable:配置二级缓存的开关
- select标签的useCache属性:配置这个select是否使用二级缓存
- sql标签的flushCache属性:增删改默认flushCache=true,sql执行后会同时清空一级和二级缓存,查询默认flushCache=false
- sqlSession.clearCache():只是用来清除一级缓存
- 当在某一个作用域进行了 C/U/D 操作后,默认该作用域下所有select中的缓存将被clear
一级缓存
一级缓存也叫本地缓存,mybatis的一级缓存是在会话(SqlSession)层面进行缓存的。
注意:mybatis的一级缓存是默认开启的,不需要任何的配置
一级缓存失效的几种情况:
- 不同的SqlSession对象,使用不同的一级缓存(每个SqlSession都有自己的Map存储数据)
- SqlSession对象第一次执行该sql语句
- 只要执行一次增删改操作,缓存就会被清空
- 手动清空缓存 sqlSession.clearCache();
二级缓存
二级缓存是用来解决一级缓存不能跨会话共享的问题,范围是namespace级别的,可以被多个SqlSession共享(只要是同一个接口里面的相同方法,都可以共享)。如果使用了二级缓存,那么mybatis查询数据的顺序是:二级缓存->一级缓存->数据库
注意:二级缓存默认不开启,需要手动配置。SqlSession关闭或提交之后,一级缓存的数据会放在二级缓存中(即只有SqlSession关闭或提交之后二级缓存中才会有数据)
使用步骤:
- 全局配置文件中开启二级缓存
- 需要使用二级缓存的映射文件处(.xml文件)使用cache配置缓存<cache/>
注意:POJO需要实现Serializable接口
<!--开启全局缓存开关--> <setting name="cacheEnabled" value="true"/>
<mapper namespace="com.tang.dao.UserDao">
<cache/>
........
</mapper>
整合第三方缓存
整合ehcache
- 导包
- ehcache工作要有一个配置文件,文件名叫 ehcache.xml,放在类路径的根目录下
- 在mapper.xml中配置使用自定义的缓存
浙公网安备 33010602011771号