@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)
答案 0 :(得分:2)
在您的情况下,saveOrUpdate()
将生成:
INSERT
为accountsId
,则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