用hibernate和mysql检索多态实体

时间:2014-08-13 16:12:01

标签: java mysql hibernate

在使用hibernate通过MySQL数据库的spring mvc应用程序中,我有一个AccessLog实体,它记录各种类型的actor在各种类型的目标资源上执行的活动。为简单起见,我使用多态来将所有actor ids存储在一列中,将所有target resource ids存储在另一列中,而不管其类型如何。 但是我如何在以后查询目标实体和参与者实体?

这是AccessLog实体。它当前使用actor_type字符串属性来指示type存储在id中的实体的actorentity_id,以及另一个字符串属性target_type来指示类型id存储在targetentity_id中的实体。但这可能会变得混乱,因为它依赖于应用程序业务层中的逻辑来管理所有字符串类型值。我可以将类型属性添加到BaseEntity,但这仍然涉及管理业务层中的类型值。 如何进行设置,以便最优雅地设计?

@Entity
@Table(name = "accesslogs")
public class AccessLog extends BaseEntity{

    @ManyToOne
    @JoinColumn(name = "actorentity_id")
    private BaseEntity actor_entity;

    @ManyToOne
    @JoinColumn(name = "targetentity_id")
    private BaseEntity target_entity;

    @Column(name="actorentity_type")//this could get messy
    private String actor_type;

    @Column(name="targetentity_type")//this could get messy
    private String target_type;

    @Column(name="action_code")
    private String action;

    @Column(name="access_date")
    @Type(type="org.jadira.usertype.dateandtime.joda.PersistentDateTime")
    private DateTime accessdate;

    //getters and setters
}

这是BaseEntity

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    protected Integer id;

    public void setId(Integer id) {this.id = id;}
    public Integer getId() {return id;}
}

以下是一些扩展BaseEntity的actor实体类型的示例,它们可以存储在AccessLog.actor_entity属性中:

@Entity
@Table(name="users")
public class User extends BaseEntity{
    //other stuff
}

@Entity
@Table(name="externalsystems")
public class ExternalSystem extends BaseEntity{
    //other stuff
}

这是DDL:

CREATE TABLE IF NOT EXISTS accesslogs(
  id int(11) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  actorentity_id int(11) UNSIGNED NOT NULL,
  targetentity_id int(11) UNSIGNED NOT NULL,
  actorentity_type varchar(100), #This could get messy
  targetentity_type varchar(100), #This could get messy
  action_code varchar(100),
  access_date DATETIME
)engine=InnoDB;SHOW WARNINGS;

CREATE TABLE IF NOT EXISTS users(
  id int(11) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  #other stuff
)engine=InnoDB;SHOW WARNINGS;

CREATE TABLE IF NOT EXISTS externalsystems(
  id int(11) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  #other stuff
)engine=InnoDB;SHOW WARNINGS;

1 个答案:

答案 0 :(得分:1)

根据您提出的问题,有两种可能的解决方案。如果要为问题编写自定义解决方案,则应执行以下代码。

以下是BaseEntity的外观

@MappedSuperClass
public abstract class BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    private Integer id;

    public Integer getId() {return id;}
}

需要一个中间类(我们称之为AuditableEntity)

@Entity
@Inheritance(strategy=InheritanceType.Joined)    
public abstract class AuditableEntity extends BaseEntity {

    @OneToMany(mappedBy="actor")
    private List<AccessLog> actorLogs;

    @OneToMany(mappedBy="target")
    private List<AccessLog> targetLogs;
}

您的所有演员都应该从此课程扩展,例如您的User实体将如下所示:

@Entity
@PrimaryKeyJoinColumn
public class User extends AuditableEntity{
    //other stuff
}

最后,您的AccessLog将如下所示:

@Entity
@Table(name = "accesslogs")
public class AccessLog extends BaseEntity{

    @ManyToOne
    @JoinColumn
    private AuditableEntity actor;

    @ManyToOne
    @JoinColumn
    private AuditableEntity target;

    @Column(name="action_code")
    private String action;

    @Column(name="access_date")
    @Type(type="org.jadira.usertype.dateandtime.joda.PersistentDateTime")
    private DateTime accessdate;

    //getters and setters
}

但我不会实现这种方法,更好的解决方案是使用Hibernate Envers。检查我在此处发布的与该项目相关的链接。每次更改数据时,您的解决方案都需要很多代码。