为什么hibernate运行删除和插入可嵌入对象的命令

时间:2014-08-23 20:28:24

标签: java hibernate

我试图创建一个简单的例子来理解基本和可嵌入类型的集合在Hibernate中是如何工作的。

我创建了一个User实体,其中包含一组nickNames和一组addresses。这是我的Java类:

User.java

@Entity
@Table(name = "TB_User")
public class User {
    @Id
    @GeneratedValue
    private int id;
    private String name;
    @ElementCollection
    @CollectionTable(name = "Nicknames", joinColumns = @JoinColumn(name = "user_id"))
    @Column(name = "nickname")
    private Set<String> nickNames = new HashSet<String>();
    @ElementCollection
    @CollectionTable(name = "Addresses", joinColumns = @JoinColumn(name = "user_id"))
    @AttributeOverrides({ @AttributeOverride(name = "street1", column = @Column(name = "fld_street")) })
    public Set<Address> addresses = new HashSet<Address>();

    public User() {
    }

    public User(String name, Address... addresses) {
        this.name = name;
        this.addresses.addAll(Arrays.asList(addresses));
    }

    public void addNickName(String... nickNames) {
        this.nickNames.addAll(Arrays.asList(nickNames));
    }
// Setters & Getters
}

Address.java

@Embeddable
public class Address {
    private String street1;
    public Address() {}
    public Address(String street1) {
        this.street1 = street1;
    }
    @Override
    public String toString() {
        return street1;
    }
// Setters & Getters
}

现在我创建了一个简单的程序来在数据库中创建用户,然后显示用户列表。这是我的代码:

private static void saveUsers() {
        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        session.getTransaction().begin();
        User user = new User("User", new Address("abc"),
                new Address("xyz"));
        user1.addNickName("alpha", "beta");
        session.save(user);
        session.getTransaction().commit();
    }

private static void showUsers() {
    Session session = HibernateUtil.getSessionFactory().getCurrentSession();
    session.getTransaction().begin();
    List<User> users = session.createQuery("from User").list();   
    for (User user : users) {
        System.out.println(user.getName() + " -- > " + user.getNickNames()
                + " --> " + user.getAddresses());
    }    
    session.getTransaction().commit();
}

当我运行这个程序时,我发现hibernate问题出现在以下命令集中:

Hibernate: select user0_.id as id1_2_, user0_.name as name2_2_ from TB_User user0_
Hibernate: select nicknames0_.user_id as user_id1_2_0_, nicknames0_.nickname as nickname2_1_0_ from Nicknames nicknames0_ where nicknames0_.user_id=?
Hibernate: select addresses0_.user_id as user_id1_2_0_, addresses0_.fld_street as fld_street2_0_0_ from Addresses addresses0_ where addresses0_.user_id=?
User -- > [alpha, beta] --> [xyz, abc]

Hibernate: delete from Addresses where user_id=?
Hibernate: insert into Addresses (user_id, fld_street) values (?, ?)
Hibernate: delete from Addresses where user_id=?
Hibernate: insert into Addresses (user_id, fld_street) values (?, ?)

如果我尝试在user.getAddresses()中使用session获取地址列表,则Hibernate将删除&amp;在Addresses表中重新插入记录。

为什么hibernate会尝试删除并重新创建Addresses表中的记录,因为这会导致性能问题。另外,为什么它不适用于我的示例中的nickNames等基本类型,而不是为属性nickNames运行更新命令?

2 个答案:

答案 0 :(得分:1)

此行为与双向映射这一事实有关。要深入理解 - 请阅读这篇文章:

以下是使用注释的方法:

让我引用答案:

  

我找到了答案。 mappedBy注释的 @OneToMany 属性与xml文件中的inverse = true相同。

一些总结:

Hibernate可以发出我们期望的SQL语句。但是,只有我们确实有双向映射。

然后,另一端(地址)将驱动持久性。它/ 必须了解其父级 - 以及为什么可以发布一些直接的UPDATE语句。

因此,如果我们想要避免DELETE和INSERT,我们必须使用逆映射。 Hibernate会发布更多&#34;可预期的&#34; SQL。

答案 1 :(得分:0)

这个很老,但可能与sombody有关。在我的案例中帮助的另一个提示:如果您可以将读取与写入分开,请考虑使用

@Transactional(readOnly = true)

这使得基础JPA框架知道当前事务中不会进行任何修改。实际上,当你只想获取一些数据时,Hibernate不再运行delete + insert