Hibernate savOrUpdate 仅插入或更新

Tec*_*ind 4 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();
}
Run Code Online (Sandbox Code Playgroud)

输出:

即使数据库中没有数据,它也会始终运行更新。

   Hibernate: update name=?, subaccount=? where accounts_id=?
    ....
Run Code Online (Sandbox Code Playgroud)

如果我在第 20 行中评论帐户 ID,它总是运行插入。

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

dto类:

@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;

}
Run Code Online (Sandbox Code Playgroud)

查询:Oracle数据库:

 create table test (accounts_id NUMBER(10) NOT NULL PRIMARY KEY, 
    name VARCHAR(100),
    subaccount VARCHAR(100) 
    )
Run Code Online (Sandbox Code Playgroud)

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

编辑

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

@Version
@Column(name = "version")
private long version;
Run Code Online (Sandbox Code Playgroud)

并在数据库中创建一列作为版本号(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)
Run Code Online (Sandbox Code Playgroud)

acd*_*ior 8

长话短说

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

  • 一个INSERT如果accountsIdnull
  • 否则UPDATE

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

最简单的方法可能是自己检查它,save()如果accountsId尚不存在或update()其他情况则调用。

细节:

请参阅自动状态检测-斜体是我写的:

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有关该属性的更多详细信息(下面的引用来自unsaved-value/ @Ididentifier 属性):

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

对于Long标识符,“合理”的默认值最有可能是null

解决方法:

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

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

2 - 另一种选择是使用重写的方法来实现Interceptor。在该方法中,您必须再次检查该实体是否已存在。优点:您只需实现一次(在 上)。缺点:嗯,它是一个,你必须连接它以及它需要的其他一切。isTransient()InterceptorInterceptor

3 - 最后,文档暗示有可能(我们必须确认)您可以通过使用生成器和设置属性来执行您想要的操作unsaved-value="undefined"。但是您必须求助于 XML 映射,因为您无法设置unsaved-valuethrough 注释

unsaved-valueHibernate 中几乎从不需要该属性,并且注释中确实没有相应的元素。

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

<id name="accountsId" column="accounts_id" type="long" unsaved-value="undefined">
    <generator class="auto" />
</id> 
Run Code Online (Sandbox Code Playgroud)