Reputation: 9191
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;
Upvotes: 0
Views: 189
Reputation: 595
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.
Upvotes: 1