Java 通过带回滚的HQL批量操作删除
我正在寻找一种有说服力的方法来删除事务中的多个实体 给定一个ID列表,如果受影响行的计数与列表计数不同,我想抛出一个异常。目前,我使用以下代码段,但其中涉及大量样板文件:Java 通过带回滚的HQL批量操作删除,java,hibernate,hql,java-6,Java,Hibernate,Hql,Java 6,我正在寻找一种有说服力的方法来删除事务中的多个实体 给定一个ID列表,如果受影响行的计数与列表计数不同,我想抛出一个异常。目前,我使用以下代码段,但其中涉及大量样板文件: private int deleteMyEntities(final List<Integer> ids) { final Session session = SomeHelper.getOpenSession(); Transaction tx = null; try {
private int deleteMyEntities(final List<Integer> ids) {
final Session session = SomeHelper.getOpenSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
final int affectedCount = session.createQuery("delete MyEntity where id in (:ids)")
.setParameterList("ids", ids)
.executeUpdate();
if (affectedCount == ids.size()) {
tx.commit();
tx = null;
return affectedCount;
} else {
throw new HibernateException("Delete count does not match target count.");
}
} finally {
if (tx != null) {
tx.rollback();
}
}
}
private int deleteMeyEntities(最终列表ID){
final Session Session=SomeHelper.getOpenSession();
事务tx=null;
试一试{
tx=session.beginTransaction();
final int affectedCount=session.createQuery(“删除(:id)中id所在的MyEntity”)
.setParameterList(“ids”,ids)
.executeUpdate();
if(affectedCount==ids.size()){
tx.commit();
tx=null;
返回影响计数;
}否则{
抛出新的HibernateeException(“删除计数与目标计数不匹配”);
}
}最后{
如果(tx!=null){
tx.回滚();
}
}
}
一些陷阱:
- 这是一个缺乏依赖注入、注释驱动事务和其他细节的遗留应用程序。类似于“使用弹簧”的答案并不特别有用
- 我们编译成Java1.6
- 我试了一下。在这种特定情况下,您不需要在
try
语句中启动事务,因为如果您无法启动它,那么您可能无法回滚它,但即使可以,也没有意义,因为您还没有对它做任何事情。如果交易无法打开,就没有什么可关闭的。换句话说,连接池中不会有孤立线程
private int deleteMyEntities(final List<Integer> ids) {
final Session session = SomeHelper.getOpenSession();
Transaction tx = session.beginTransaction();
try {
final int affectedCount = session.createQuery("delete MyEntity where id in (:ids)")
.setParameterList("ids", ids)
.executeUpdate();
if (affectedCount == ids.size()) {
tx.commit();
return affectedCount;
} else {
throw new HibernateException("Delete count does not match target count.");
}
} catch (Exception e) {
tx.rollback();
throw e;
}
}
private int deleteMeyEntities(最终列表ID){
final Session Session=SomeHelper.getOpenSession();
事务tx=会话.beginTransaction();
试一试{
final int affectedCount=session.createQuery(“删除(:id)中id所在的MyEntity”)
.setParameterList(“ids”,ids)
.executeUpdate();
if(affectedCount==ids.size()){
tx.commit();
返回影响计数;
}否则{
抛出新的HibernateeException(“删除计数与目标计数不匹配”);
}
}捕获(例外e){
tx.回滚();
投掷e;
}
}
不幸的是,如果不编写自己的自定义框架来执行类似于基于注释的事务之类的操作,就很难使它“漂亮”。如果您可以访问AOP库,您可以使用它来隐藏很多内容,尽管根据您的描述,这似乎是可疑的。我最终采用的解决方案包括Alex的建议。我还提出了很多逻辑来保持代码的干燥剂。注意:hibernate会话在筛选器中打开,并在请求期间保持(在视图中打开会话),因此会话在
recoverFromFailedTransaction
public int deleteMyEntity(final List<Integer> ids) {
return deleteEntities("MyEntity", ids);
}
private int deleteEntities(final String entityName, final List<Integer> ids) {
final Session session = SomeHelper.getOpenSession();
final Query query = session.createQuery("delete " + entityName + " where id in (:ids)")
.setParameterList("ids", ids);
return performBatchOperation(query, ids.size());
}
private int performBatchOperation(final Query query, final int expectedAffectedCount) {
final Session session = SomeHelper.getOpenSession();
final Transaction tx = session.beginTransaction();
try {
final int affectedCount = query.executeUpdate();
if (affectedCount == expectedAffectedCount) {
tx.commit();
return affectedCount;
} else {
throw new HibernateException(String.format(
"Affected count [%d] does not match expected count [%d].",
affectedCount,
expectedAffectedCount));
}
} catch (RuntimeException e) {
logger.error(e);
recoverFromFailedTransaction(tx);
throw e;
}
}
private void recoverFromFailedTransaction(final Transaction tx) {
try {
if (tx != null) {
tx.rollback();
}
} catch (HibernateException e) {
logger.error("Exception when rolling back failed transaction. ", e);
}
try {
SomeHelper.getOpenSession().close();
} catch (HibernateException e) {
logger.error("Exception when closing session . ", e);
}
SomeHelper.resetOpenSession();
logger.warn("Session discarded.");
}
public int deleteMeyEntity(最终列表ID){
返回删除实体(“MyEntity”,ID);
}
私有int deleteEntities(最终字符串entityName,最终列表ID){
final Session Session=SomeHelper.getOpenSession();
final Query Query=session.createQuery(“删除”+entityName+“其中id位于(:ids)”)
.setParameterList(“ids”,ids);
返回performBatchOperation(查询,ids.size());
}
私有整数性能希望(最终查询查询,最终整数预期有效计数){
final Session Session=SomeHelper.getOpenSession();
final Transaction tx=会话.beginTransaction();
试一试{
final int affectedCount=query.executeUpdate();
如果(影响计数==预期影响计数){
tx.commit();
返回影响计数;
}否则{
抛出新的HibernateeException(String.format(
“受影响的计数[%d]与预期计数[%d]不匹配。”,
受影响的伯爵,
预期有效计数);
}
}捕获(运行时异常e){
错误(e);
从失败的传输(tx)中恢复;
投掷e;
}
}
从失败交易中恢复的私有无效(最终交易发送){
试一试{
如果(tx!=null){
tx.回滚();
}
}捕获(休眠异常e){
logger.error(“回滚失败事务时出现异常。”,e);
}
试一试{
SomeHelper.getOpenSession().close();
}捕获(休眠异常e){
logger.error(“关闭会话时异常)”,e);
}
SomeHelper.resetOpenSession();
logger.warn(“会话已丢弃”);
}
这段代码有什么问题?有很多样板。它是有效的,它只是丑陋的,而不是MyEntity对象创建一个只有id字段的DTO使用此解决方案,我的方法符号必须是public int deleteMEntity(最终列表id)抛出异常
<代码>抛出异常至少可以说是令人不快的。您可以将“e”封装在运行时异常中,如抛出新的运行时异常(e),这样可以消除方法签名更改,但不是最优雅的解决方案。