ORM 全称:object relation mapping,译为:对象关系映射。
ORM 框架是将对象和数据库表字段建设映射, 并提供 CRUD 操作的 API 的框架。
Java 原生的与数据库连贯的形式是 JDBC, 每次操作须要以下 6 个步骤
- 加载数据库驱动
- 创立连贯
- 创立一个 Statement
- 执行 SQL
- 处理结果集
- 敞开连贯
原生的形式步骤繁琐,开发效率低,市面上有很多的优良的 ORM 框架:
- Hibernate 全自动 ORM 框架,弱化 sql, 甚至不须要思考建表,Hibernate 会依据对象生成表甚至两头表。CURD 个别不须要写 sql。用起来不便,用好很难,有些老的我的项目还在用。
- Mybatis 半自动 ORM 框架,本文的配角,被宽泛应用,它反对自定义 SQL、存储过程以及高级映射。前身是 ibatis, 另外还有一个在此基础上封装的号称为简化开发而生的框架 MyBatis-Plus。提供通用的 CURD,个别操作不须要写 sql。
- JPA 是大 spring 旗下 ORM 框架,特点是依据办法名就能主动实现办法逻辑,你敢信?不信能够看看这篇文章《简略才是美! SpringBoot+JPA》
上面将介绍一些 mybatis 老手进阶知识点,老鸟请走开🤦♂️
嵌套查问
在
resultMap
中嵌套一个查问。通过标签<association>
的select
属性实现。select
的值是另一个<select>
查问的 id,column
属性为关联字段,用来实现关联查问。
-
依据 user_id 查问 user
<select id="getUserWithAddress2" resultMap="BaseResultWithAddress2Map"> select * from `user` where user_id = #{userId} </select>
-
<association> 中嵌套一个 id 为 selectAddressByUserId 的查问,查问这个用户的地址。
<resultMap id="BaseResultWithAddress2Map" type="com.mashu.springmybatis.entity.UserWithAddress"> <id column="user_id" property="userId" jdbcType="INTEGER"/> <result column="name" property="name" jdbcType="VARCHAR"/> <result column="email" property="email" jdbcType="VARCHAR"/> <association property="address" column="user_id" javaType="com.mashu.springmybatis.entity.Address" select="selectAddressByUserId"> </association> </resultMap>
-
id 为 selectAddressByUserId 的查问:依据用户 id 查问地址详情:
<select id="selectAddressByUserId" resultMap="com.mashu.springmybatis.mapper.AddressMapper.BaseResultMap"> select * from address where user_id = #{userId} </select>
嵌套后果
下面的查问会有 N + 1 的问题,就是执行两遍查问,能够应用联表查问解决这个问题,后果集同样是应用
<resultMap>
映射,<association>
标签 +resultMap
属性。具体写法如下:
-
association 标签的 resultMap 属性指向 address 的 resultMap
<resultMap id="BaseResultWithAddressMap" type="com.mashu.springmybatis.entity.UserWithAddress"> <id column="user_id" property="userId" jdbcType="INTEGER"/> <result column="name" property="name" jdbcType="VARCHAR"/> <result column="email" property="email" jdbcType="VARCHAR"/> <association property="address" javaType="com.mashu.springmybatis.entity.Address" resultMap="com.mashu.springmybatis.mapper.AddressMapper.BaseResultMap"> </association> </resultMap>
-
联表查问 sql
<select id="getUserWithAddress" resultMap="BaseResultWithAddressMap"> select * from `user` u join address a on u.user_id and a.user_id where u.user_id = #{userId} </select>
还能够一对多的映射, 将 <association>
换成<collection>
, 实现一个人有多个女朋友的一对多关联查问。
myabtis 会主动合并反复的 user,girlFriends 作为汇合映射到 user 的 girlFriends 属性。
<resultMap id="BaseResultWithGirlFriendsMap" type="com.mashu.springmybatis.entity.UserWithGirlFriends">
<id column="user_id" property="userId" jdbcType="INTEGER"/>
<result column="name" property="name" jdbcType="VARCHAR"/>
<result column="email" property="email" jdbcType="VARCHAR"/>
<collection property="girlFriends" ofType="com.mashu.springmybatis.entity.GirlFriend">
<id column="girl_friend_id" property="girlFriendId" jdbcType="INTEGER"/>
<result column="user_id" property="userId" jdbcType="VARCHAR"/>
<result column="girl_friend_name" property="name" jdbcType="VARCHAR"/>
<result column="age" property="age" jdbcType="INTEGER"/>
<result column="weight" property="weight" jdbcType="INTEGER"/>
<result column="height" property="height" jdbcType="INTEGER"/>
</collection>
</resultMap>
懒加载
除了联表查问解决 N + 1 的问题,mybatis 的懒加载仿佛更好,拿第一个嵌套查问的栗子来说,如果开启了懒加载,
在不应用 address 的时候,只会执行查问 user 的 sql, 不会执行查问 address 的 sql。
只有在应用中 get 了 address 属性才会执行查问 address 的 sql,应用起来也很见简略:
-
yml 配置
mybatis: mapper-locations: classpath:mapper/*Mapper.xml configuration: ## 开启懒加载 lazy-loading-enabled: true ##false:按需加载 aggressive-lazy-loading: false ## 触发加载的办法名 lazy-load-trigger-methods:
-
<association> 加上
fetchType="lazy"
的属性即可。<resultMap id="BaseResultWithAddress2Map" type="com.mashu.springmybatis.entity.UserWithAddress"> <id column="user_id" property="userId" jdbcType="INTEGER"/> <result column="name" property="name" jdbcType="VARCHAR"/> <result column="email" property="email" jdbcType="VARCHAR"/> <association fetchType="lazy" property="address" column="user_id" javaType="com.mashu.springmybatis.entity.Address" select="selectAddressByUserId"> </association> </resultMap>
然而,实现懒加载的问题比拟多😢:
- 如果报错 No serializer found for class org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory..
序列化问题须要在实体类上增加注解@JsonIgnoreProperties(value = {"handler"})
- 如果懒加载失败:查看是否是 lombok 中的 @Data 注解的 toString()导致的
- 查看全局配置是否正确
- 还有在 idea 失败,在 eclipce 胜利的。。。
一二级缓存
一级缓存,一次申请查问两次数据,第二次从缓存中取,mybatis 默认开启
二级缓存,屡次申请查问同一个数据,都能从缓存中取,须要手动开启
-
开启全局配置:
mybatis: mapper-locations: classpath:mapper/*Mapper.xml ## 开启二级缓存 cache-enabled: true
-
增加 useCache=”true” 属性。
<select id="selectByCache" useCache="true" resultMap="BaseResultMap" parameterType="java.lang.Integer"> select user_id, name, email from user where user_id = #{userId,jdbcType=INTEGER} </select>
类型处理器
有时候咱们在入库和出库的时候对字段做一些解决,
比方不反对 utf8mb4 的数据库存储 emoji 表情之前须要本义成 utf8 反对的 unicode 字符编码,出库后须要转化成 emoji 表情。
又或者用户的明码不能明文保留到数据库,入库须要进行一些加密操作。
mybatis 的类型处理器,就能够在入库和出库前对数据做一些操作。
上面举个栗子将邮箱入库 Base64 加密,出库 Base64 解密。
-
自定义类型处理器类继承
BaseTypeHandler
抽象类。public class MyHandler extends BaseTypeHandler<String> { // 入库加密 @Override public void setNonNullParameter(PreparedStatement preparedStatement, int i, String s, JdbcType jdbcType) throws SQLException {preparedStatement.setString(i,Base64Utils.encodeToString(s.getBytes())); } // 出库解密 @Override public String getNullableResult(ResultSet resultSet, String columnName) throws SQLException {String column = resultSet.getString(columnName); return new String(Base64Utils.decode(column.getBytes())); } @Override public String getNullableResult(ResultSet resultSet, int i) throws SQLException {System.out.println(resultSet); return null; } @Override public String getNullableResult(CallableStatement callableStatement, int i) throws SQLException {System.out.println(callableStatement); return null; } }
-
字段增加
typeHandler
属性,并指向自定义类型处理器类的门路<resultMap id="BaseResultMap" type="com.mashu.springmybatis.entity.User"> <id column="user_id" property="userId" jdbcType="INTEGER"/> <result column="name" property="name" jdbcType="VARCHAR"/> <result column="email" property="email" jdbcType="VARCHAR" typeHandler="com.mashu.springmybatis.config.MyHandler"/> </resultMap>