简体   繁体   中英

retrieving polymorphic entities with hibernate and mysql

In a spring mvc application using hibernate over a MySQL database, I have an AccessLog entity that records the activities performed by various types of actors on various types of target resources. For simplicity, I am using polymorphism to store all the actor ids in one column and all the target resource ids in another column, regardless of their type. But how do I query the target entities and actor entities later?

Here is the AccessLog entity. It currently uses an actor_type string property to indicate the type of the entity whose id is stored in actorentity_id , and another string property target_type to indicate the type of entity whose id is stored in targetentity_id . But this could get messy because it relies on logic in the application's business layer to manage all the string type values. I could add a type property to BaseEntity , but that would still involve managing the type values in the business layer. How can I set things up so that this is designed most elegantly?

@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
}

Here is the 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;}
}

Here are a couple of examples of types of actor entities that extend BaseEntity, and which could be stored in the AccessLog.actor_entity property:

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

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

Here is the 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;

Based on the problem you propose, there are two possible solutions. If you want to code a custom solution for the problem you should do the following code.

Here is how the BaseEntity will look like

@MappedSuperClass
public abstract class BaseEntity {

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

    public Integer getId() {return id;}
}

An intermediate class would be needed (Let's call it 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;
}

All your actors should extend from this class, so for example your User entity will look like this:

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

At last your AccessLog will look like this:

@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
}

But I will not implement this approach, a better solution is to use Hibernate Envers . Check the link I posted here related to that project. Your solution will require much code in all aspects each time you made changes to the data.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM