hibernate执行流程
- Hibernate 启动,Configruation 会读取并加载 Hibernate 核心配置文件和映射文件钟的信息到它实例对象中。
- 通过 Configuration 对象读取到的配置文件信息,创建一个 SessionFactory 对象,该对象中保存了当前数据库的配置信息、映射关系等信息。
- 通过 SessionFactory 对象创建一个 Session 实例,建立数据库连接。Session 主要负责执行持久化对象的增、删、改、查操作,创建一个 Session 就相当于创建一个新的数据库连接。
- 通过 Session 对象创建 Transaction(事务)实例对象,并开启事务。Transaction 用于事务管理,一个 Transaction 对象对应的事务可以包含多个操作。需要注意的是,Hibernate 的事务默认是关闭的,需要手动开启和关闭。
- Session 接口提供了各种方法,可以对实体类对象进行持久化操作(即对数据库进行操作),例如 get()、load()、save()、update()、saveOrUpdate() 等等,除此之外,Session 对象还可以创建Query 对象 或 NativeQuery 对象,分别使用 HQL 语句或原生 SQL 语句对数据库进行操作。
- 对实体对象持久化操作完成后,必须提交事务,若程序运行过程中遇到异常,则回滚事务。
- 关闭 Session 与 SessionFactory,断开与数据库的连接,释放资源。
hibernate拦截器原理
- hibernate 读写前对数据进行处理,可以加解密、转换等数据转换
-
hibernate拦截器简介
-
Hibernate定义了一个拦截器,位于org.hibernate.Interceptor,提供了一系列的拦截器方法
package org.hibernate; import java.io.Serializable; import java.util.Iterator; import org.hibernate.type.Type; public interface Interceptor { // 加载数据库时调用 boolean onLoad(Object var1, Serializable var2, Object[] var3, String[] var4, Type[] var5) throws CallbackException; // 更新操作时调用 boolean onFlushDirty(Object var1, Serializable var2, Object[] var3, Object[] var4, String[] var5, Type[] var6) throws CallbackException; // 添加操作时调用 boolean onSave(Object var1, Serializable var2, Object[] var3, String[] var4, Type[] var5) throws CallbackException; // 其他方法省略... }
-
但如果直接实现Interceptor,我们还需要实现该接口下面的所有方法,为此Hibernate为我们提供了空拦截器EmptyInterceptor。EmptyInterceptor空拦截器继承自Interceptor拦截器,已经帮我们实现接口内所有的方法,这样就不需要实现所有接口方法了。我们可以定义一个类去继承空拦截器,根据需要去重写空拦截器里面提供的方法。
package org.hibernate; import java.io.Serializable; import java.util.Iterator; import org.hibernate.type.Type; public class EmptyInterceptor implements Interceptor, Serializable { public static final Interceptor INSTANCE = new EmptyInterceptor(); protected EmptyInterceptor() { } public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { } public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) { return false; } // 其他方法省略... }
-
- 常用方法
序号: | 方法: | 描述: |
---|---|---|
1 | onLoad | 在初始化对象之前调用。拦截器可能会更改状态,该状态将被传播到持久对象。请注意,当调用此方法时,实体将是该类的一个未初始化的空实例 |
2 | onSave | 在保存对象之前调用。拦截器可以修改状态,该状态将用于SQL插入并传播到持久对象 |
3 | onFlushDirty | 在冲洗过程中检测到对象变脏时调用。拦截器可以修改检测到的currentState,它将被传播到数据库和持久对象。请注意,并非所有刷新都以与数据库的实际同步结束,在这种情况下,新的currentState将传播到对象,但不一定(立即)传播到数据库。强烈建议拦截器不要修改以前的状态 |
实例
- 实现思路
- 敏感实体类上添加加密注解,可以通过注解区分出哪些实体类需要加密
- 敏感实体类的字段也需要增加加密注解,用于区分哪些字段需要加密
- 利用在Hibernate的拦截器EmptyInterceptor的对应事件,通过反射获取需要处理的实体类和字段,在数据入库前在拦截器对应的方法对数据进行加密处理,在数据读取时在拦截器中对数据进行解密处理
-
实体注解
import java.lang.annotation.*; /** * 需要加解密的表注解,只有添加此注解的表才需要进行加解密 */ @Target(ElementType.TYPE) @Documented @Retention(RetentionPolicy.RUNTIME) public @interface EncryptTable { }
-
属性注解
package com.sitech.interceptor; import java.lang.annotation.*; /** * 加解密表字段,只有添加了此注解的实体类字段才要进行加解密 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface EncryptField { }
-
加密工具
import javax.crypto.*; import javax.crypto.spec.SecretKeySpec; import java.io.UnsupportedEncodingException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; public class AESUtils { public static final String KEYWORD_BJDX = "BJDX"; public static byte[] encrypt(String content, String keyWord) { try { KeyGenerator kgen = KeyGenerator.getInstance("AES"); SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");//指定算法名称(SHA1PRNG),生成SecureRandom 对象 secureRandom.setSeed(keyWord.getBytes()); kgen.init(128, secureRandom); SecretKey secretKey = kgen.generateKey(); byte[] enCodeFormat = secretKey.getEncoded(); SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES"); Cipher cipher = Cipher.getInstance("AES");// 创建密码器 byte[] byteContent = content.getBytes("utf-8"); cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化 byte[] result = cipher.doFinal(byteContent); return result; // 加密 } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } return null; } public static String encryptToHex(String content, String password) { return parseByte2HexStr(encrypt(content, password)); } public static byte[] decrypt(byte[] content, String keyWord) { try { KeyGenerator kgen = KeyGenerator.getInstance("AES"); SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); secureRandom.setSeed(keyWord.getBytes()); kgen.init(128, secureRandom); SecretKey secretKey = kgen.generateKey(); byte[] enCodeFormat = secretKey.getEncoded(); SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES"); Cipher cipher = Cipher.getInstance("AES");// 创建密码器 cipher.init(Cipher.DECRYPT_MODE, key);// 初始化 byte[] result = cipher.doFinal(content); return result; // 加密 } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } return null; } public static byte[] decryptHex(String content, String keyWord) { return decrypt(parseHexStr2Byte(content), keyWord); } public static String parseByte2HexStr(byte buf[]) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < buf.length; i++) { String hex = Integer.toHexString(buf[i] & 0xFF); if (hex.length() == 1) { hex = '0' + hex; } sb.append(hex.toUpperCase()); } return sb.toString(); } public static byte[] parseHexStr2Byte(String hexString) { if (hexString.length() < 1) return null; byte[] byteArray = new byte[hexString.length() / 2]; byte tmpByte; for (int i = 0; i < hexString.length(); i = i + 2) { tmpByte = new Integer(Integer.parseInt(hexString.substring(i, i + 2), 16)).byteValue(); byteArray[i / 2] = tmpByte; } return byteArray; } public static boolean isAesEnStrHes(String valuse) { boolean isAes = false; try { byte[] bytes = decryptHex(valuse, KEYWORD_BJDX); if (bytes == null) { isAes = false; } else { isAes = true; } } catch (Exception e) { isAes = false; } return isAes; } public static void main(String[] args) { // String en_1 = encryptToHex("北北北北北北北北北北北北北北北北北北北北", KEYWORD_BJDX); // System.out.println(en_1); // System.out.println(en_1.length()); // try { // System.out.println(new String(decryptHex(en_1, KEYWORD_BJDX), "UTF-8")); // } catch (UnsupportedEncodingException e) { // e.printStackTrace(); // } System.out.println(AESUtils.isAesEnStrHes("北京")); System.out.println(AESUtils.isAesEnStrHes("ad")); System.out.println(AESUtils.isAesEnStrHes("3BE2C4D4CF9B5980AA312D1966255D1E1DB9959BE613BCDAB7F2469FDF8F147CEE4374B53DB51955593F6D96365E3EF8CB0564A04E49218219C18AE65FF6F4028B4CC97C829ABBFC08443D95DCBF74DD105CC6717863C8DC35ABC158A8FC8E9604E61AD9B01AA598BBAA596D90421C66FFD4CAB9036B15E9F4E3C483466B707E48DD712876FE41DE1D0A584D4153A712")); } }
-
拦截器
import org.hibernate.EmptyInterceptor; import org.hibernate.type.Type; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.io.Serializable; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; @Component public class EncryptInterceptor extends EmptyInterceptor { private final static Logger LOGGER = LoggerFactory.getLogger(EncryptInterceptor.class); /** * 更新时调用 * * @param entity 实体类 * @param id 主键 * @param currentState 当前实体类对应的值 * @param previousState 修改前实体类对应的值 * @param propertyNames 字段名 * @param types 实体类每个属性类型对应hibernate的类型 * @return true | false true才会修改数据 */ @Override public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) { System.out.println("QQQQQQQQQQQQQQQQQQ=================onFlushDirty【IN】"); Object[] newState = dealField(entity, currentState, propertyNames, "onFlushDirty"); return super.onFlushDirty(entity, id, newState, previousState, propertyNames, types); } /** * 加载时调用 * * @param entity 实体类 * @param id 主键 * @param state 实体类对应的值 * @param propertyNames 字段名 * @param types 实体类每个属性类型对应hibernate的类型 * @return true | false true才会修改数据 */ @Override public boolean onLoad(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { System.out.println("QQQQQQQQQQQQQQQQQQ=================onLoad【IN】"); Object[] newState = dealField(entity, state, propertyNames, "onLoad"); return super.onLoad(entity, id, newState, propertyNames, types); } /** * 保存时调用 * * @param entity 实体类 * @param id 主键 * @param state 实体类对应的值 * @param propertyNames 字段名 * @param types 实体类每个属性类型对应hibernate的类型 * @return true | false true才会修改数据 */ @Override public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { System.out.println("QQQQQQQQQQQQQQQQQQ=================onSave【IN】"); Object[] newState = dealField(entity, state, propertyNames, "onSave"); return super.onSave(entity, id, newState, propertyNames, types); } /** * 处理字段对应的数据 * * @param entity 实体类 * @param state 数据 * @param propertyNames 字段名称 * @return 解密后的字段名称 */ private Object[] dealField(Object entity, Object[] state, String[] propertyNames, String type) { List<String> annotationFields = getAnnotationField(entity); LOGGER.info("调用方法:{}, 需要加密的字段:{}", type, annotationFields); System.out.println("================调用方法:{}, 需要加密的字段:{}" + type + annotationFields); // 遍历字段名和加解密字段名 for (String aField : annotationFields) { for (int i = 0; i < propertyNames.length; i++) { if (!propertyNames[i].equals(aField)) { continue; } // 如果字段名和加解密字段名对应且不为null或空 if (state[i] == null || Objects.equals(state[i].toString(), "")) { continue; } if ("onSave".equals(type) || "onFlushDirty".equals(type)) { if (!AESUtils.isAesEnStrHes(state[i].toString())) { LOGGER.info("当前字段:{}, 加密前:{}", aField, state[i]); state[i] = AESUtils.encryptToHex(state[i].toString(), AESUtils.KEYWORD_BJDX); LOGGER.info("当前字段:{}, 加密后:{}", aField, state[i]); } } else if ("onLoad".equals(type)) { if (AESUtils.isAesEnStrHes(state[i].toString())) { LOGGER.info("当前字段:{}, 解密前:{}", aField, state[i]); state[i] = new String(AESUtils.decryptHex(state[i].toString(), AESUtils.KEYWORD_BJDX)); LOGGER.info("当前字段:{}, 解密后:{}", aField, state[i]); } } } } return state; } /** * 获取实体类中带有注解EncryptField的变量名 * * @param entity 实体类 * @return 需要加解密的字段 */ private List<String> getAnnotationField(Object entity) { // 判断当前实体类是否有加解密注解 Class<?> entityClass = entity.getClass(); System.out.println("================entityClass" + entityClass); if (!entityClass.isAnnotationPresent(EncryptTable.class)) { return Collections.emptyList(); } List<String> fields = new ArrayList<>(); // 获取实体类下的所有成员并判断是否存在加解密注解 Field[] declaredFields = entityClass.getDeclaredFields(); for (Field field : declaredFields) { EncryptField encryptField = field.getAnnotation(EncryptField.class); if (Objects.isNull(encryptField)) { continue; } fields.add(field.getName()); System.out.println("================entityClass" + entityClass); } return fields; } }
- hibernate拦截器配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> ... <bean id="myEntityInterceptor" class="com.sitech.interceptor.EncryptInterceptor"/> ... <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> ... <property name="entityInterceptor" ref="myEntityInterceptor"></property> ... </bean> ... <context:annotation-config/> </beans>
- 注意事项
- 1.数据处理注意形如null空的判断
- 2.hibernate有缓存配置的时候注意取和写数据的原始数据的判断和处理
- 3.使用sql串执行时观察是否该方案还适用
- 其他