Hibernate savOrUpdate只插入或更新

时间:2014-09-20 19:35:06

标签: hibernate

@Autowired
private SessionFactory sessionFactory;
public String getAccountsDetails(List<Account> accountList) {
    Session session = sessionFactory.openSession();
    for (Account account : accountList) {
        int i = 0;
        AccountDetails accountDetails = new AccountDetails();
        accountDetails.setAccountsId(Long.parseLong(account.getId()));//LINE no -20
        accountDetails.setName(account.getName());
        accountDetails.setSubAccount(account.getAccountSubType());
        session.saveOrUpdate(accountDetails);
        if (++i % 20 == 0) {
            session.flush();  
            session.clear();  
        } 
    }
        session.getTransaction().commit();
        session.close();
}

输出:

即使db中没有数据,它也会一直运行更新。

   Hibernate: update name=?, subaccount=? where accounts_id=?
    ....

如果我在LINE no-20中评论帐号ID,它总是运行insert。

Hibernate:插入表测试...         ....

dto class:

@Entity
@Table(name="test")
public class AccountDetails{
    @Id
    @GeneratedValue
    @Column(name = "accounts_id")
    private Long accountsId;

    @Column(name = "name")
    private String name;

    @Column(name = "subaccount")
    private String subAccount;

}

查询: Oracle db:

 create table test (accounts_id NUMBER(10) NOT NULL PRIMARY KEY, 
    name VARCHAR(100),
    subaccount VARCHAR(100) 
    )

我的要求是如果db中没有数据,则插入否则更新。

修改

我在dto中创建了一个新字段:

@Version
@Column(name = "version")
private long version;

并在db中创建了一个版本号(100)的列。每当我运行应用程序时,它总是先运行一个updte语句,然后抛出StaleObjectStateException:

Hibernate: update test set accountsubtype=?, accounttype=?, acctnum=?, active=?, currentbalance=?, currentbalancewithsubaccounts=?, description=?, fullyqualifiedname=?, name=?, subaccount=?, version=? where accounts_id=? and version=?
ERROR 2014-09-22 11:57:25,832 [[qbprojects].connector.http.mule.default.receiver.04] org.hibernate.event.def.AbstractFlushingEventListener: Could not synchronize database state with session
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.trinet.mulesoft.quickbooks.dto.AccountDetails#63]
    at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:1932)
    at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2576)

1 个答案:

答案 0 :(得分:2)

TL; DR

在您的情况下,saveOrUpdate()将生成:

    如果INSERTaccountsId,则
  • null
  • 一个UPDATE否则。

这就是为什么当你有第20行(accountDetails.setAccountsId(...);)时,它是UPDATE

最简单的方法可能是自行检查,如果save()尚不存在则调用accountsId,否则调用update()

详细信息:

请参阅Automatic State Detection - 斜体由我撰写:

  

saveOrUpdate()执行以下操作:

     
      
  • 如果对象在此会话中已经持久化,则不执行任何操作;
  •   
  • 如果与会话关联的另一个对象具有相同的标识符,则抛出异常;
  •   
  • 如果对象没有标识符属性,save()它;   
        
    • 没有标识符属性表示没有@Id或XML等效。
    •   
  •   
  • 如果对象的标识符具有分配给新实例化对象的值,则save();   
        
    • 请注意,新实例化的对象unsaved-value属性定义。
    •   
    • 换句话说,这个子弹说:如果@Id属性值等于unsaved-value(见下文),则认为它未保存,然后save()
    •   
    • 这是您的方案。请注意,您使用的是unsaved-value=null,因为它是默认设置。
    •   
  •   
  • 如果对象由<version><timestamp>版本化,并且版本属性值是分配给新实例化对象的相同值,save()它;   
        
    • 这与之前的项目相同,但使用<version><timestamp>属性而不是@Id属性。
    •   
    • 请注意,<version><timestamp>也有unsaved-value=null属性来定义未保存的新实例化的对象
    •   
  •   
  • 否则update()对象
  •   

有关unsaved-value attribute的更多详细信息(以下引用来自unsaved-value /标识符属性的@Id

  

unsaved-value(可选 - 默认为“合理”值):a   标识符属性值,表示实例是新的   实例化(未保存),将其与分离的实例区分开来   在之前的会话中保存或加载。

对于Long个标识符,“明智的”默认值很可能为null

解决方法:

如果只是想分配标识符,我们可以使用<generator class="assigned"/>,它就可以了。但是你不是 世界:有时分配并生成其他世界。

1 - 如上所述,一个选项是检查对象是否已经存在,并相应地调用save()update()

2 - 另一个选项是implement an Interceptor覆盖isTransient() method。在该方法中,您必须再次检查实体是否已经存在。优点:您只需实施一次(Interceptor)。缺点:好吧,它是Interceptor,您必须连接它和其他所需的一切。

3 - 最后,docs hint有可能(我们必须确认)您可以使用生成器并设置属性{{{ 1}}。但是你必须像can't set the unsaved-value through annotations一样使用XML映射:

  

Hibernate和Hibernate几乎不需要unsaved-value="undefined"属性   确实在注释中没有相应的元素。

通过XML设置unsaved-value类似于:

unsaved-value