MySQL支持一种" INSERT ... ON DUPLICATE KEY UPDATE ..."语法,允许您"盲目地"插入数据库,并回退到更新现有记录(如果存在).
当您需要快速事务隔离并且要更新的值依赖于数据库中已有的值时,这非常有用.
作为一个人为的例子,假设您想要计算在博客上查看故事的次数.使用此语法执行此操作的一种方法可能是:
INSERT INTO story_count (id, view_count) VALUES (12345, 1)
ON DUPLICATE KEY UPDATE set view_count = view_count + 1
Run Code Online (Sandbox Code Playgroud)
这比开始交易更有效,更有效,并且处理新故事登上头版时发生的不可避免的异常.
我们怎么能用Hibernate做同样的事情,或者实现同样的目标呢?
首先,Hibernate的HQL解析器将抛出异常,因为它不了解特定于数据库的关键字.事实上,HQL不喜欢任何显式插入,除非它是" INSERT ... SELECT ....".
其次,Hibernate将SQL限制为仅选择.如果您尝试呼叫,Hibernate将抛出异常session.createSQLQuery("sql").executeUpdate().
第三,saveOrUpdate在这种情况下,Hibernate 不符合要求.您的测试将通过,但如果您每秒有多个访问者,则会导致生产失败.
我真的要颠覆Hibernate吗?
我在Glassfish v3应用服务器上使用EJB和JPA.我有一个实体类,我强迫其中一个字段与@Column注释是唯一的.
@Entity
public class MyEntity implements Serializable {
private String uniqueName;
public MyEntity() {
}
@Column(unique = true, nullable = false)
public String getUniqueName() {
return uniqueName;
}
public void setUniqueName(String uniqueName) {
this.uniqueName = uniqueName;
}
}
Run Code Online (Sandbox Code Playgroud)
当我尝试将此字段设置为非唯一值的对象持久化时,当EJB容器管理的事务提交时,我得到一个异常(如预期的那样).
我有两个问题需要解决:
1)我得到的例外是无用的"javax.ejb.EJBException:Transaction aborted".如果我递归调用getCause()足够多次,我最终会得到更有用的"java.sql.SQLIntegrityConstraintViolationException",但是这个异常是EclipseLink实现的一部分,我真的不习惯依赖它的存在.
有没有更好的方法来获取JPA的详细错误信息?
2)EJB容器坚持记录此错误,即使我抓住它并处理它.
有没有更好的方法来处理这个错误,这将阻止Glassfish使用无用的异常信息混乱我的日志?
谢谢.
我有一些执行UPSERT的代码,也称为Merge。我想清理这段代码,具体地说,我想摆脱异常处理,并为这种简单的操作降低代码的整体冗长性和纯粹的复杂性。要求是插入每个项目,除非它已经存在:
public void batchInsert(IncomingItem[] items) {
try(Session session = sessionFactory.openSession()) {
batchInsert(session, items);
}
catch(PersistenceException e) {
if(e.getCause() instanceof ConstraintViolationException) {
logger.warn("attempting to recover from constraint violation");
DateTimeFormatter dbFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
items = Arrays.stream(items).filter(item -> {
int n = db.queryForObject("select count(*) from rets where source = ? and systemid = ? and updtdate = ?::timestamp",
Integer.class,
item.getSource().name(), item.getSystemID(),
dbFormat.format(item.getUpdtDateObj()));
if(n != 0) {
logger.warn("REMOVED DUPLICATE: " +
item.getSource() + " " + item.getSystemID() + " " + …Run Code Online (Sandbox Code Playgroud)