报错信息如下:
当使用Hibernate和Lombok处理表与表之间的关系时,在插入数据时可能会遇到栈溢出错误。这篇博客将详细讨论此问题的原因,并提供解决办法。
标题: Hibernate+Lombok进行表与表之间关系时插入数据时栈溢出
问题背景
Hibernate是一个Java持久化框架,通过对象关系映射(ORM)的方式,将Java对象映射到关系型数据库中的表。Lombok是一个Java库,通过自动代码生成的方式,简化了Java类的编写。
在使用Hibernate和Lombok时,我们通常使用注解来描述表与表之间的关系,例如一对多、多对一等关联关系。然而,有时在插入数据时会遇到栈溢出错误,这可能导致开发者困惑和疑惑。
源代码:
@Data
public class LinkMan {private int uid;private String username;private String tel;private String gender;Customer customer;}
@Data
public class Customer {private int cid;private String name;private String address;Set<LinkMan> linkManSet = new HashSet<>();
}
@Testpublic void relationTest() {Session session = HibernateUtil.getCurrSession();Transaction transaction = session.beginTransaction();Customer customer = new Customer();customer.setName("腾讯");customer.setAddress("深圳");Customer customer1 = new Customer();customer1.setName("阿里巴巴");customer1.setAddress("杭州");LinkMan linkMan = new LinkMan();linkMan.setUsername("李彦宏");linkMan.setTel("110");linkMan.setGender("男");LinkMan linkMan1 = new LinkMan();linkMan1.setUsername("陈迪凯");linkMan1.setTel("119");linkMan1.setGender("男");LinkMan linkMan2 = new LinkMan();linkMan2.setUsername("马化腾");linkMan2.setTel("120");linkMan2.setGender("女");LinkMan linkMan3 = new LinkMan();linkMan3.setUsername("张小龙");linkMan3.setTel("114");linkMan3.setGender("男");customer.getLinkManSet().add(linkMan);customer.getLinkManSet().add(linkMan1);linkMan.setCustomer(customer);linkMan1.setCustomer(customer);customer1.getLinkManSet().add(linkMan2);customer1.getLinkManSet().add(linkMan3);linkMan2.setCustomer(customer1);linkMan3.setCustomer(customer1);//保存数据session.save(customer);session.save(customer1);session.save(linkMan);session.save(linkMan1);session.save(linkMan2);session.save(linkMan3);transaction.commit();session.close();}
<?xml version="1.0" encoding="utf-8" ?>
<!-- 引入核心配置文件约束 -->
<!DOCTYPE hibernate-configuration PUBLIC"-//Hibernate/Hibernate Configuration DTD 3.0//EN""http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration><!-- 配置hibernate核心配置文件 --><session-factory><!-- 1、数据库信息 必需 --><property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property><property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate_study?serverTimezone=GMT&useSSL=false&characterEncoding=utf8</property><property name="hibernate.connection.username">root</property><property name="hibernate.connection.password">root</property><!-- 2、hibernate信息 非必需 --><!-- 在控制台输出hibernate底层生成的SQL语句 --><property name="show_sql">true</property><!-- 格式化hibernate底层生成的SQL语句 --><property name="format_sql">true</property><!-- 数据库方言 --><propertyname="hibernate.dialect">org.hibernate.dialect.MySQL8Dialect</property><!-- 设置事务的隔离级别值为1:事务隔离级别为“读未提交”值为2:事务隔离级别为“读已提交”值为3:事务隔离级别为“可重复读”值为4:事务隔离级别为“可串行化”--><property name="hibernate.connection.isolation">4</property><!-- session绑定本地线程 --><property name="current_session_context_class">thread</property><!-- hibernate自动创建数据库表,如果表已经存在,则更新表,如果不存在,则创建表格 --><property name="hibernate.hbm2ddl.auto">update</property><!-- 3、引用hibernate映射关系配置文件,mapping标签必须在property标签后面 必需 --><mapping resource="mapper/user.hbm.xml"/><mapping resource="mapper/customer.hbm.xml"/><mapping resource="mapper/linkman.hbm.xml"/></session-factory>
</hibernate-configuration>
<?xml version="1.0" encoding="utf-8" ?>
<!-- 引入映射关系配置文件约束 -->
<!DOCTYPE hibernate-mapping PUBLIC"-//Hibernate/Hibernate Mapping DTD 3.0//EN""http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping><!-- 配置对象和数据库表之间的映射关系name:实体类的全路径名table:数据库表的名称--><class name="icu.chiou.entity.User" table="tb_user"><!-- 配置主键映射关系name:实体类中的属性名column:数据库表中主键名称,column属性可以省略,如果省略,则自动生成的数据库表主键名称和实体类名称一致--><id name="id" column="id"><!-- 主键生成策略 --><generator class="native"/></id><!-- 配置其他属性的映射关系 --><property name="username" column="username"/><property name="password" column="password"/></class>
</hibernate-mapping>
查证问题
经过多方查证,发现栈溢出错误与Lombok的@Data
注解有关。@Data
注解是Lombok提供的一个实用注解,它自动生成了一些常用方法,包括equals()
、hashCode()
和toString()
等。然而,问题出现在hashCode()
方法的生成上。
在默认情况下,Lombok的@Data
注解会重写hashCode()
方法,并使用所有字段来计算哈希码。这其中包括了关联对象的字段,从而导致在计算哈希码时发生循环调用,最终导致栈溢出错误:
- 调用this.getLinkManSet();。
- linkmanset里面又包含customer
- 调用this.getCustomer();里面又包含linkman
为了更好地理解这个问题,我们可以查看编译后的类文件。编译后的类文件会展示出Lombok生成的代码。以下是一个示例:
Customer.class
public int hashCode() {int PRIME = true;int result = 1;result = result * 59 + this.getCid();Object $name = this.getName();result = result * 59 + ($name == null ? 43 : $name.hashCode());Object $address = this.getAddress();result = result * 59 + ($address == null ? 43 : $address.hashCode());Object $linkManSet = this.getLinkManSet();result = result * 59 + ($linkManSet == null ? 43 : $linkManSet.hashCode());return result;}
LinkMan.class
public int hashCode() {int PRIME = true;int result = 1;result = result * 59 + this.getUid();Object $username = this.getUsername();result = result * 59 + ($username == null ? 43 : $username.hashCode());Object $tel = this.getTel();result = result * 59 + ($tel == null ? 43 : $tel.hashCode());Object $gender = this.getGender();result = result * 59 + ($gender == null ? 43 : $gender.hashCode());Object $customer = this.getCustomer();result = result * 59 + ($customer == null ? 43 : $customer.hashCode());return result;}
从上述示例中可以看出,hashCode()
方法中使用了关联对象otherEntity
的hashCode()
方法,这可能导致循环调用,最终触发栈溢出错误。
解决办法
针对上述问题,我们可以采取以下解决办法:
- 使用
@Getter
和@Setter
代替@Data
:@Data
注解是Lombok提供的一个快捷注解,它包含了@Getter
、@Setter
、@EqualsAndHashCode
和@ToString
等注解
的功能。但在处理关联对象时,我们可以避免使用@Data
注解,而是手动添加@Getter
和@Setter
注解。这样就能避免在hashCode()
方法中出现循环调用的问题。
修改后的示例代码如下:
@Getter
@Setter
public class LinkMan {private int uid;private String username;private String tel;private String gender;Customer customer;
}
通过手动添加@Getter
和@Setter
注解,我们可以避免在hashCode()
方法中引发循环调用的问题,从而解决栈溢出错误。
结论
在使用Hibernate和Lombok处理表与表之间的关系时,特别是在涉及关联对象的hashCode()
方法中,需要小心使用Lombok的@Data
注解。通过使用@Getter
和@Setter
注解,我们可以避免栈溢出错误,并确保对象关系映射的正确性。
探讨了栈溢出错误的原因和解决办法后,我们希望这篇博客能帮助到遇到类似问题的开发者们。
如果你在使用Hibernate和Lombok过程中遇到其他问题,或者有其他相关的疑问,欢迎在评论区留言,我们将竭诚为您解答。