【问题标题】:Hibernate @SQLDelete sql not adding schemaHibernate @SQLDelete sql 不添加模式
【发布时间】:2016-12-11 23:55:17
【问题描述】:

我正在尝试使用 Hibernate 的 @SQLDelete 注释进行软删除。当数据库模式是静态的时,它工作得很好,即:在 SQL 中传递它。
不幸的是,SQL 似乎按原样传递给EntityPersisters(参见EntityClass 的方法CustomSQL createCustomSQL(AnnotationInstance customSqlAnnotation),所以我找不到像使用{h-schema} 有没有人找到解决这个问题的好方法(我使用的是 Hibernate 4.3.5)?

编辑:除非有真正的解决方案,否则我最终通过在方法doLateInit 中设置自定义SQL 查询时替换架构占位符来修改org.hibernate.persister.entity.AbstractEntityPersister 的代码源。

Edit2:我在 Hibernate JIRA 中为这种行为创建了 an issue。我将在今天晚些时候创建一个拉取请求,我希望 Hibernate 团队能够接受它

【问题讨论】:

  • JIRA 似乎已针对 5.2 进行了修复,但在 5.3.7 中仍然看到此问题,在较新版本的发行说明中没有看到它......但可能会丢失它。
  • 您是否在 SQL 查询中添加了 {h-schema} 前缀?上次我检查时,修复在那里但老实说我不确定版本
  • 检查源代码后,我添加了 {h-schema},确实解决了问题。

标签: java sql hibernate schema abstract


【解决方案1】:

Soft deletes using Hibernate annotations.

如下链接作者所述:

我目前正在开发一个需要在数据库中进行软删除的 Seam 应用程序。在右侧,您可以看到我的数据库图表的 sn-p,其中包含 CUSTOMERAPP_USER 表。这只是一个简单的一对多关系,但需要注意的重要一点是每个表中的“已删除”字段。这是将用于跟踪软删除的字段。如果该字段包含“1”,则记录已被删除;如果该字段包含“0”,则该记录未被删除。

在像 Hibernate 这样的 ORM 之前,我必须自己使用 SQL 跟踪和设置这个标志。这不会很难做到,但谁愿意编写一堆样板代码来跟踪记录是否已被删除。这就是 Hibernate 和注解发挥作用的地方。

以下是 Hibernate 使用 seamgen 生成的 2 个实体类。为了清楚起见,我省略了部分代码。

客户.java

//Package name...

//Imports...

@Entity
@Table(name = "CUSTOMER")
//Override the default Hibernation delete and set the deleted flag rather than deleting the record from the db.
@SQLDelete(sql="UPDATE customer SET deleted = '1' WHERE id = ?")
//Filter added to retrieve only records that have not been soft deleted.
@Where(clause="deleted <> '1'")
public class Customer implements java.io.Serializable {
    private long id;
    private Billing billing;
    private String name;
    private String address;
    private String zipCode;
    private String city;
    private String state;
    private String notes;
    private char enabled;
    private char deleted;
    private Set appUsers = new HashSet(0);

    // Constructors...

    // Getters and Setters...

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "customer")
    // Filter added to retrieve only records that have not been soft deleted.
    @Where(clause = "deleted <> '1'")
    public Set getAppUsers() {
        return this.appUsers;
    }

    public void setAppUsers(Set appUsers) {
        this.appUsers = appUsers;
    }
}

AppUser.java

//Package name...

//Imports...

@Entity
@Table(name = "APP_USER")
//Override the default Hibernation delete and set the deleted flag rather than deleting the record from the db.
@SQLDelete(sql="UPDATE app_user SET deleted = '1' WHERE id = ?")
//Filter added to retrieve only records that have not been soft deleted.
@Where(clause="deleted <> '1'")
public class AppUser implements java.io.Serializable {
    private long id;
    private Customer customer;
    private AppRole appRole;
    private char enabled;
    private String username;
    private String appPassword;
    private Date expirationDate;
    private String firstName;
    private String lastName;
    private String email;
    private String phone;
    private String fax;
    private char deleted;
    private Set persons = new HashSet(0);

    // Constructors...

    // Getters and Setters...
}

以下两个步骤是我实现软删除所要做的全部。

  1. 添加了覆盖默认值的@SQLDelete 注释 该实体的休眠删除。
  2. 添加@Where注解过滤查询并只返回 没有被软删除的记录。另请注意,在 CUSTOMER 类我在 appUsers 集合中添加了一个@Where。这是 只需要为该客户获取尚未 被软删除。

维奥拉!现在,无论何时您删除这些实体,它都会将 “DELETED” 字段设置为“1”,当您查询这些实体时,它只会返回 “DELETED”中包含“0”的记录 字段。

难以置信,但这就是使用 Hibernate 注释实现软删除的全部内容。

注意:

还请注意,您可以使用休眠过滤器 (http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#entity-hibspec-filters) 来全局过滤掉所有“已删除”的实体,而不是使用 @Where(clause="deleted ‘1’") 语句。我发现定义 2 个实体管理器(“正常”一个过滤已删除项目,一个不过滤,在极少数情况下……)通常非常方便。

使用 EntityPersister

您可以创建DeleteEventListener,例如:

public class SoftDeleteEventListener extends DefaultDeleteEventListener {

/**
 * 
 */
private static final long serialVersionUID = 1L;

@Override
public void onDelete(DeleteEvent event, Set arg1) throws HibernateException {
    Object o = event.getObject();
    if (o instanceof SoftDeletable) {
        ((SoftDeletable)o).setStatusId(1);
        EntityPersister persister = event.getSession().getEntityPersister( event.getEntityName(), o);
        EntityEntry entityEntry = event.getSession().getPersistenceContext().getEntry(o);
        cascadeBeforeDelete(event.getSession(), persister, o, entityEntry, arg1);

        cascadeAfterDelete(event.getSession(), persister, o, arg1);

    } else {
        super.onDelete(event, arg1);
    }
}
}

像这样把它挂到你的 persistence.xml

<property name = "hibernate.ejb.event.delete" value = "org.something.SoftDeleteEventListener"/> 

另外,不要忘记更新注释中的级联。

资源链接:

  1. Hibernate: Overwrite sql-delete with inheritace
  2. Custom SQL for CRUD operations
  3. Custom SQL for create, update and delete

【讨论】:

  • 谢谢,但很抱歉,我的问题不是关于如何使用@SQLDelete,而是如何在 SQL 查询中动态指定模式
  • 我想你需要让hibernate知道表所在的模式只是为实体定义一个持久单元。在这种情况下,执行 hql 时不需要模式限定符
【解决方案2】:

这样使用

@SQLDelete(sql = "UPDATE {h-schema}LEAVE  SET STATUS = 'DELETED' WHERE id = ?", check = ResultCheckStyle.COUNT)

【讨论】:

  • 请注意报告问题的 Hibernate 版本。此外,您可以检查我当时创建的问题。它在以后的版本中得到了修复
【解决方案3】:

我认为有两种方式 首先是添加:

app.datasource.schema=<schema_name> 

到您的 application.properties。

第二个是在表模型的注释中使用模式

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-08
    • 2012-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多