【问题标题】:Hibernate inserts extra records in the databaseHibernate 在数据库中插入额外的记录
【发布时间】:2021-06-09 18:34:01
【问题描述】:

我正在开发一个使用 Spring MVCJSPHibernateMySQL 构建的简单应用程序。

我有两个实体:UserUserDetail

用户

@Entity
@Table(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;
    
    @Column(name = "name")
    @NotNull(message = "required field")
    @Size(min = 3, message = "should contain at-least 3 characters")
    private String name;
    
    @Column(name = "salary")
    @NotNull(message = "required field")
    @Min(value = 0, message = "should be a valid positive number")
    private int salary;
    
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
    @Valid
    private List<UserDetail> addressList;
    
    ...
}

用户详情

@Entity
@Table(name = "user_detail")
public class UserDetail {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;
    
    @Column(name = "address")
    @NotNull(message = "required field")
    @Size(min = 5, message = "should contain at-least 5 characters")
    private String address;
    
    @ManyToOne(cascade = {
        CascadeType.DETACH, CascadeType.MERGE,
        CascadeType.PERSIST, CascadeType.REFRESH}
    )
    @JoinColumn(name = "user_id")
    private User user;

    ...
}

UserUserDetail 实体之间存在 one-to-many 关系 - 单个用户可以有多个地址。

前端有一个表单,其中包含以下字段:

  • 姓名
  • 工资
  • 地址(可以是多个地址,用户可以通过按钮添加更多输入字段)

从前端提交表单后,我会验证表单数据,一旦确定表单数据有效,我就会调用将数据插入MySQL 数据库的方法。

问题

当表单仅提交一个地址时,数据正确保存在数据库中。

问题是表单提交时包含多个地址。例如,如果使用两个地址提交表单,则数据正确插入到user 表中,但在user_detail 表中,而不是插入两条记录,而是插入了 3 条记录。

以下是user_detail表数据不正确的截图:

在上面的截图中,id 为 1 的用户有两个地址,但插入了三行,中间一行与用户没有关联,即user_idnull

下图显示了hibernate在控制台记录的sql语句:

如果提交的表单数据中有3个地址,下面的截图显示了user_detail表中插入的数据:

“测试地址2”和“测试地址3”重复。

下图显示了hibernate在控制台记录的sql语句:

以下方法用于插入数据:

public static void saveUserData(User user) {
    SessionFactory factory = new Configuration()
                                .configure("hibernate.cfg.xml")
                                .addAnnotatedClass(User.class)
                                .addAnnotatedClass(UserDetail.class)
                                .buildSessionFactory();
    
    Session session = factory.getCurrentSession();
    
    try {
        session.beginTransaction();
        
        session.save(user);
        
        UserDetail userDetail;
        
        for (int i = 0; i < user.getAddressList().size(); i++) {
            userDetail = user.getAddressList().get(i);
            userDetail.setUser(user);
            
            session.save(userDetail);
            
            // hibernate batch insert
            // 10 --> hibernate's batch size
            if (i % 10 == 0) {
                session.flush();
                session.clear();
            }
        }
    
        session.getTransaction().commit();
        
    } catch (HibernateException | ConstraintViolationException e) {
         if (session.getTransaction() != null) {
             session.getTransaction().rollback();
         }
         
         System.out.println(e.getLocalizedMessage()); 
    } 
    finally {
        session.close();
        factory.close();
    }
}

问题

为什么休眠会在user_detail 表中插入额外的记录?我该如何解决这个问题?

版本:

  • Spring (5.3.4)
  • Hibernate (5.4.29.Final)

【问题讨论】:

    标签: java mysql spring hibernate spring-mvc


    【解决方案1】:

    你真的需要批量插入吗?它对日志根本不起作用!您必须向数据库 URL 添加一个附加参数并为 Hibernate 启用它。

    无批量插入

        @Entity
        @Table(name = "USERS")
        public class User {
            
            @Id
            @GeneratedValue(strategy = GenerationType.IDENTITY)
            @Column(name = "ID")
            private Long id;
            
            @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
            private List<UserDetail> userDetails = new ArrayList<>();
            
            @Transient
            public void addUserDetail(UserDetail detail) {
                detail.setUser(this);
                userDetails.add(detail);
            }      
    
    
        }
        
        @Entity
        @Table(name = "USER_DETAILS")
        public class UserDetail {
            
            @Id
            @GeneratedValue(strategy = GenerationType.IDENTITY)
            @Column(name = "id")
            private Long id;
            
            
            @ManyToOne(fetch = FetchType.LAZY)
            @JoinColumn(name = "USER_ID")
            private User user;
        
        }
    
    for each userDetail
        user.add(userDetail)
    
    save(user);
    

    所以只需保存一个用户。

    1. 表名使用复数形式
    2. 表名使用大写
    3. 使用Long 作为id(所以它可以为空)
    4. 使用orphanRemoval
    5. 不要在@ManyToOne 使用级联
    6. 在任何地方使用fetch = FetchType.LAZY

    批量插入

    检查User 根本没有详细信息

    保存用户

    为每个细节设置用户

    并用批处理保存详细信息

    【讨论】:

    • 感谢您的回答。我通过在循环内调用setUser setter 并仅保存User 解决了这个问题。但我仍然不确定 是什么 究竟导致了这个问题?我在@ManyToOne 上仍有级联,所以这似乎不是问题的原因。 (自从我开始使用 hibernate 以来已经有大约 10 天了,所以很多关于它的事情对我来说还没有意义。)
    • @Yousaf 也许这个级联并不重要。也许当你做session.save(userDetail) 时它起作用了。我根本不使用所有这些级联。所以我不确定:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-18
    • 1970-01-01
    • 2018-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-06
    相关资源
    最近更新 更多