排查及处理hibernate自动更新(无缘故的)实体数据及表数据的根本问题,hibernate脏读更新,注意非常实用

这篇具有很好参考价值的文章主要介绍了排查及处理hibernate自动更新(无缘故的)实体数据及表数据的根本问题,hibernate脏读更新,注意非常实用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

排查及处理hibernate自动更新实体数据及表数据的根本问题

这里我默认大家都对hibernate的实体时态问题有所了解,并且知道hibernate是有内存快照机制,会自动更新数据库。
但是我们在开发过程中总是会无缘无故就更新数据库了,导致会在一些只读的spring事务中报错。这使我们抓狂,往往遇到这个情况我们仔细查看代码后也没有发现哪里有给实体对象赋值。
首先这种情况一定是实体对象在持久太的时候有某些字段被重新赋值了,导致脏读,内存快照判定为实体数据与快照不一致,这样hibernate才会去更新数据库。

大家注意,上面描述的情况并不是我们主动去赋值的而是实体对象被二次查询了,对象中有某个字段的哈希地址值被重新分配了导致的。

1、找到那些被重新赋值的字段

我们先打开hibernate的脏读日志,这样日志里就会记录哪些实体有因为脏读而更新数据库了

2023-07-20 16:43:12,374 [http-nio-8281-exec-2] TRACE o.h.e.i.DefaultFlushEntityEventListener.logDirtyProperties(651)- Found dirty properties [[com.xt.entity.Ad#4902]] : [Ljava.lang.String;@7ac65666

并在org.hibernate.event.internal.DefaultFlushEntityEventListener#logDirtyProperties打上断点
排查及处理hibernate自动更新(无缘故的)实体数据及表数据的根本问题,hibernate脏读更新,注意非常实用,hibernate,java,后端

//spring配置文件中添加这个配置,开启hibernate的脏读日志
logging.level.org.hibernate.event.internal.DefaultFlushEntityEventListener=TRACE



//这是相关的应用
import cn.hutool.core.util.StrUtil;
import org.hibernate.exception.SQLGrammarException;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceContext;
import javax.persistence.Table;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.Metamodel;


//使用一下代码扫描项目中所有和mysql有关的实体类,这个代码会把出现脏读实体字段找出,
//进入到我们上面打的断点中org.hibernate.event.internal.DefaultFlushEntityEventListener#logDirtyProperties
	@Autowired
	private EntityManagerFactory entityManagerFactory;

	@PersistenceContext
	private EntityManager entityManager;

	@Override
	@Transactional(readOnly = true)
	public List<Object> dirty1() {
		//找到所有的实体
		Metamodel metamodel = entityManagerFactory.getMetamodel();
		Set<EntityType<?>> entities = metamodel.getEntities();
		String tableName = "" ;
		String daoName  = "" ;
		String sql  = "" ;
		Integer i = 0;
		//遍历
		for (EntityType<?> entityType : entities) {
			//拿到实体名
			daoName = lowerFirst(entityType.getName());
			sql = "SELECT * FROM "+daoName + " limit 1 ";
			Table annotation = entityType.getJavaType().getAnnotation(Table.class);
			if (annotation!=null){
				//拿到实体的表名
				tableName = annotation.name();
				System.out.println("Entity Name: " + daoName + ", Table Name: " + tableName);
				if (StrUtil.isNotBlank(tableName)){
					sql = "SELECT * FROM " + tableName + " limit 1 ";
				}
			}else {
				System.out.println("Entity Name: " + daoName + ", Table Name: null"  );
			}
			//拿到实体的dao对象
			if (SpringUtils.getApplicationContext().containsBean(daoName+"DaoImpl")){
				i++;
				Object daoName1 = SpringUtils.getBean(daoName+"DaoImpl");
				try {
					//进行两次不同途径的查询,这样就会造成同一个实体但其字段的哈希地址值不同
					List<Object> all = ((BaseDao)daoName1).findList(null,1,null,null);

					List<Map> mapList = QueryUtil.mapList(sql, new HashMap<>(), entityManager);
					System.out.println(String.format("findList:%s QueryUtil:%s",mapList.size(),all.size()));
				}catch (SQLGrammarException e){
					e.printStackTrace();
				} catch (Exception e){
					e.printStackTrace();
				}
			}
		}
		System.out.println("dirty end "+i);
		return null;
	}

	/**
	 * 将字符串的首字母转小写
	 * @param str 需要转换的字符串
	 * @return
	 */
	private static String lowerFirst(String str) {
		// 同理
		char[] cs=str.toCharArray();
		cs[0]+=32;
		return String.valueOf(cs);
	}

随便找个接口执行上面定义的dirty1方法就可以看到脏读的对象进入到断点了
排查及处理hibernate自动更新(无缘故的)实体数据及表数据的根本问题,hibernate脏读更新,注意非常实用,hibernate,java,后端
图上标记的很清楚,是那个类的那个字段除了问题,简直不要太方便

2、处理有脏读问题的字段或对象

(1)、未重新equals和hashCode

这种就是需要我们重新hashCode的,我们重写一下即可排查及处理hibernate自动更新(无缘故的)实体数据及表数据的根本问题,hibernate脏读更新,注意非常实用,hibernate,java,后端

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;

/**
	 * 重写equals方法
	 *
	 * @param obj
	 *            对象
	 * @return 是否相等
	 */
	@Override
	public boolean equals(Object obj) {
		return EqualsBuilder.reflectionEquals(this, obj);
	}

	/**
	 * 重写hashCode方法
	 *
	 * @return HashCode
	 */
	@Override
	public int hashCode() {
		return HashCodeBuilder.reflectionHashCode(this);
	}
(2)、数据类型有转换的

排查及处理hibernate自动更新(无缘故的)实体数据及表数据的根本问题,hibernate脏读更新,注意非常实用,hibernate,java,后端
这种我们换成List即可

@Converter
	public static class CouponsConverter extends BaseAttributeConverter<List<Long>> {}

	/** 赠送的优惠券 **/
	@Column(name = "coupons")
	@Convert(converter = CouponsConverter.class)
	private List<Long> coupons = new ArrayList<>();

我的项目通过上面的扫描后已经很整洁了,基本不会出现hibernate脏读更新的问题了,希望可以帮到大家文章来源地址https://www.toymoban.com/news/detail-602894.html

到了这里,关于排查及处理hibernate自动更新(无缘故的)实体数据及表数据的根本问题,hibernate脏读更新,注意非常实用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包