zoukankan      html  css  js  c++  java
  • 关于 Hibernate StackOverflowError 异常的一个解决方法

    关于 Hibernate StackOverflowError 异常的一个解决方法

    踩坑经历

    今天在学习 Hibernate 一对多映射的时候,使用 Hibernate 的建表策略进行创建数据库表。运行测试类自动创建表并添加数据时,出现了个 bug 。报了 StackOverflowError 异常。报错如下:

    下面这个报错是解决后重新模拟的,一开始报错指向的是实体类的 toString 方法,这里是指向 hashCode 方法。但是他们报错的源头都是相同的。

    image-20200729190457548

    环境

    我的实体类也很简单,一个 User 类,一个 Car 类。他们之间的对应关系是一对多。一个用户可以有很多辆小轿车。

    先说下我的环境,抛开环境谈异常都是耍流氓【狗头】。但如果不想看环境可以直接跳到结果处

    实体类使用了Lombok插件,@Data 是自动给加了注解的类生成setter/getter、equals、canEqual、hashCode、toString方法。@AllArgsConstructor 是自动生成全参构造器。@NoArgsConstructor 是自动生成无参构造器

    User

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User implements Serializable {
        /**
         * id
         */
        private Integer id;
        /**
         * 用户名
         */
        private String name;
        /**
         * 密码
         */
        private String password;
        /**
         * 用户拥有的车辆
         */
    
        private Set<Car> cars;
    }
    

    Car

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Car {
        /**
         * 车辆id
         */
        private Integer id;
        /**
         * 车辆名
         */
        private String name;
        /**
         * 车辆颜色
         */
        private String color;
    }
    

    Hibernate 核心配置如下:

    hibernate.cfg.xml

    <!DOCTYPE hibernate-configuration PUBLIC
            "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
            "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
    <hibernate-configuration>
        <session-factory>
            <!-- 四个必填项 -->
            <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
            <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate?useTimezone=true&amp;serverTimezone=GMT%2b8&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;useSSL=false</property>
            <property name="hibernate.connection.username">root</property>
            <property name="hibernate.connection.password">root</property>
    
            <!-- 可选项 -->
            <!-- 显示 SQL 语句 -->
            <property name="hibernate.show_sql">true</property>
            <!-- 显示的 SQL 语句是否格式化,也就是说打印出来的 SQL 语句是一行还是多行显示 -->
            <property name="hibernate.format_sql">true</property>
    
            <!-- 配置方言 -->
            <property name="hibernate.dialect">org.hibernate.dialect.MySQL57Dialect</property>
    
            <!-- 配置建表策略 -->
            <property name="hibernate.hbm2ddl.auto">update</property>
    
            <!-- 配置 mapping -->
            <mapping resource="mapper/User.hbm.xml"/>
            <mapping resource="mapper/Car.hbm.xml"/>
        </session-factory>
    </hibernate-configuration>
    

    Hibernate mapping配置如下:

    User.hbm.xml

    <!DOCTYPE hibernate-mapping PUBLIC
            "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
            "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping package="com.xp.model.entity">
        <!-- orm 映射 实体类和数据表进行映射 -->
        <class name="User" table="user">
            <!-- 映射主键,其中name表示实体类中的成员变量,column 表示对应表中的字段 -->
            <id name="id" column="id">
                <!-- 设置主键策略 -->
                <generator class="native"/>
            </id>
            <!-- 映射字段,其中name表示实体类中的成员变量,column 表示对应表中的字段 -->
            <property name="name" column="name"/>
            <property name="password" column="password"/>
            <!-- 一对多关系映射 -->
            <set name="cars" >
                <key column="cid"/>
                <one-to-many class="Car"/>
            </set>
        </class>
    </hibernate-mapping>
    

    Car.hbm.xml

    <!DOCTYPE hibernate-mapping PUBLIC
            "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
            "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping package="com.xp.model.entity">
        <class name="Car" table="car">
            <!-- 主键映射 -->
            <id name="id" column="id">
                <!-- 设置主键映射规则 -->
                <generator class="native"/>
            </id>
            <!-- 其他字段映射 -->
            <property name="name" column="name"/>
            <property name="color" column="color"/>
            <!-- 多对一关系映射 -->
            <many-to-one name="user" class="User" column="cid"/>
        </class>
    </hibernate-mapping>
    

    测试类

    @Test
    public void test() {
        // 获取会话
        Session session = HibernateUtil.getSession();
        // 初始化用户和汽车
        User user = new User(null, "李老板", "888", null);
        Car car1 = new Car(null, "宝马", "红色", null);
        Car car2 = new Car(null, "奔驰", "白色", null);
        Car car3 = new Car(null, "保时捷", "蓝色", null);
    
        // 让用户拥有三辆车
        Set<Car> cars = new HashSet<>();
        cars.add(car1);
        cars.add(car2);
        cars.add(car3);
        user.setCars(cars);
        // 让车拥有主人
        car1.setUser(user);
        car2.setUser(user);
        car3.setUser(user);
    
        // 开启事务,并将对象中存储的数据写入数据库中
        Transaction transaction = session.beginTransaction();
        session.save(user);
        session.save(car1);
        session.save(car2);
        session.save(car3);
        transaction.commit();
        session.close();
    }
    

    一开始,我以为是 Lombok 的问题(也可以说是Lombok的问题,毕竟这个偷懒导致自己实体类的方法看不到),将 lombok 的注解注释掉然后手动按照常规实体类编写 getter,setter,构造器,toString() ,结果还是报错。一开始用 lombok 时报错时 hashcode 方法,现在报 toString 方法,然后我尝试将 toString 方法去掉,然后就解决了。但是,报错是解决了,但是整个人还是懵的。查了百度,发现其实他们讲的并不是很全:删除掉 toString 中关联关系的对象。然后结合之前的 hashCode 方法的报错以及报错图(在本文最上面)中重复删除创建表,我推测是这样的:

    报错原因及解决方法

    主要原因就是父子表出现了双向属性,会导致 Hibernate 调用方法自动创建表或插入数据的时候死循环,方法不断入栈,最终导致栈内存溢出

    回看刚刚的代码,Lombok 帮我完成了 toString() 和 hashCode() 方法的重写 ,所以这两个方法里面的具体算法我是不知道的。而我自己手动写实体类中的方法时,是用 idea 自动生成的(这年头也没多少人是完全手动写的吧[狗头]),默认toString 是输出所有的成员变量。

    问题就出在这

    我们在方法中使用了关联关系的对象。

    所以解决方法就是:将实体类中事用了关联关系的对象的方法进行修改,不单单是 toString() 方法。比如 hashCode 方法等

  • 相关阅读:
    03-树3 Tree Traversals Again
    Utuntu下Xshell使用+vi使用
    CSDN总结的面试中的十大算法
    EDM(邮件营销)
    腾讯CDC谈扁平化设计
    Graph Search图谱搜索
    LBS 与 GPS 定位之间的区别
    中间件的理解
    夏梦竹谈Hive vs. HBase的区别
    维基百科上—数据仓库、数据挖掘、OLAP三者之间的区别
  • 原文地址:https://www.cnblogs.com/windowsxpxp/p/13399121.html
Copyright © 2011-2022 走看看