I want to model a couple of object relations and I'm currently not sure about a smart way to do this. Let's assume a Record has a OneToMany relationship to different RecordSources. The common attributes of the RecordSources are long id and boolean preferred. Other attributes are individual and the number of record sources may increase in the future.
One possibility is for Record
#OneToMany(cascade = CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="record")
private List<GenericSimpleRecordSource> recordSources;
where GenericSimpleRecordSource would look like this:
#Entity
public class GenericSimpleRecordSource implements Serializable
{
public static enum Type {a,b}
#ManyToOne private Record record;
#NotNull
private Type sourceType;
#OneToOne(cascade = CascadeType.ALL,mappedBy="source")
private SimpleRecordSourceA sourceA;
#OneToOne(cascade = CascadeType.ALL,mappedBy="source")
private SimpleRecordSourceB sourceB;
}
SimpleRecordSourceA and SimpleRecordSourceA are individual #Entity classes. I don't feel comfortable with this approach, using inheritance might be better:
#Entity
#Inheritance(strategy= InheritanceType.TABLE_PER_CLASS)
#DiscriminatorColumn(name="type")
#DiscriminatorValue("generic")
public class GenericRecordSource
{
#Id #GeneratedValue(strategy=GenerationType.AUTO) private long id;
#ManyToOne private Record record;
private boolean preferred;
}
#Entity
#DiscriminatorValue("A")
public class RecordSourceA extends GenericRecordSource
{
...
}
#Entity
#DiscriminatorValue("B")
public class RecordSourceB extends GenericRecordSource
{
...
}
This seems to be smarter, but are there any shortcomings using Inheritance? I appreciate any comments on both approaches or even another alternative.
Is it possible to model the #OneToMany relationship from Record like this?
#OneToMany(cascade = CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="record")
private List<GenericRecordSource> recordSources;
Related
Interestingly, I can't find any solution for a seemingly common scenario! So I'm asking here to learn from experienced professionals in Spring Data JPA. I'll consider using Lombok to make the sample codes more concise.
Consider a simple IMDB example web application. I've defined two simple entities as below:
#Data
#Entity
public class Movie {
#Id
#GeneratedValue
private long id;
private String title;
private int year;
private int rating;
}
#Data
#Entity
public class Actor {
#Id
#GeneratedValue
private long id;
private String firstName;
private String lastName;
private Date birthday;
private String gender;
}
Now we need a join-table to link these two entities; but this is not just a simple join-table. Other than the actor and movie columns, this table has some additional attributes. We didn't want to waste storage by adding an ID column here, instead we used a composite-key consisting of actor and movie:
#Data
#Embeddable
public class MovieActorId implements Serializable {
private Actor actor;
private Movie movie;
}
#Data
#Entity
public class MovieActor {
#EmbeddedId
private MovieActorId id;
private int salary;
private String characterName;
}
There are two Many-to-One relations here: MovieActor >-- Actor and MovieActor >-- Movie.
Now my main question is: "Assuming the above design, how should I define the #ManyToOne relationships in this design?"
NOTE: I believe if we add an additional ID column to the MovieActor join-table instead of the composite/embedded MovieActorId, the JPA code will become fairly straight-forward. But suppose we have some sort of limitation, and we need to stick to this design as much as possible.
You need to use #MapsId which provides the mapping for an EmbeddedId primary key in #ManyToOne relation
#Data
#Embeddable
public class MovieActorId implements Serializable {
private long actorId;
private long movieId;
// constructor, setter, etc
}
#Data
#Entity
public class MovieActor {
#EmbeddedId
private MovieActorId id;
#ManyToOne(cascade = CascadeType.ALL)
#MapsId("actorId")
private Actor actor;
#ManyToOne(cascade = CascadeType.ALL)
#MapsId("movieId")
private Movie movie;
...
}
I designed a database model like this. In this, I'd like to store user info from Facebook when they use Facebook or Google to login.
I use JPA with Hibernate to work with Database, so I need to map the database model to ORM.
What I tried:
I tried MappedSuperclass for SnsProfile abstract class but It doesn't work when I need to map SnsProfile with User entity.
I tried #Inheritance(strategy = InheritanceType.SINGLE_TABLE) but if I have some more Social Sites and additional properties, the table is more and more complicated.
I would like to have some JPA Entity like this:
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String password;
private String email;
private UserStatus status;
#OneToMany(fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
mappedBy = "user")
private List<SnsProfile> profile;
}
public abstract class SnsProfile implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String snsId;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "user_id", nullable = false)
private User user;
}
public abstract class FacebokProfile extends SnsProfile {
// Properties here
}
public abstract class GoogleProfile extends SnsProfile {
// Properties here
}
Could you please give me some advice on Entity design to the database model above work well on JPA? Or any changes in Database model are welcome.
Please help. Thank you so much!
Lets assume we have a complex JPA relation, a fraction of which looks like this:
#MappedSuperclass
public class DiffEntity {
private String diffId;
public DiffEntity() {
this.diffId = UUID.randomUUID().toString();
}
//...
}
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class ParentEntity extends DiffEntity {
#Id
#GeneratedValue
private long id;
#Column
private String name;
//...
}
#Entity
public class Construct extends ParentEntity {
#Column
private String variable;
#OneToMany(mappedBy = "construct", cascade = CascadeType.ALL)
private List<Partconstruct> partconstructs;
//...
}
#Entity
public class Partconstruct extends ParentEntity {
#OneToMany(mappedBy = "partconstruct", cascade = CascadeType.ALL)
private List<Field> fields;
#OneToMany(mappedBy = "partconstruct", cascade = CascadeType.ALL)
private List<Hardparameter> hardparameters;
#ManyToOne
#JoinColumn(name = "construct_id")
private Construct construct;
//...
}
#Entity
public class Field extends ParentEntity {
#Column
private int fieldSize;
#ManyToOne
#JoinColumn(name = "partconstruct_id")
private Partconstruct partconstruct;
//...
}
#Entity
public class Hardparameter extends ParentEntity {
#Column
private String value;
#ManyToOne
#JoinColumn(name = "partConstruct_Id")
private Partconstruct partConstruct;
//...
}
We are concerned with Construct type objects. Construct is deeply cloned and persisted, having all its nested objects on the object graph being cloned too and getting a new Id (primary key). On every clone the diffId (from DiffEntity entity) stays the same (it serves the purpose of correlating objects for a diffing feature).
How would it be possible to search and get a reference for a specific DiffEntity given we have the below:
a reference to the Construnct instance
type of the nested object
diffId we are after.
I have tried different versions of object graph traversers with reflection, which will work for a small in size Construct object, but once it becomes too big performance is very slow.
Is there any magic on the entity manager itself to achieve that ?
I have an abstract and generic class:
#MappedSuperclass
public abstract class Field<T> {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "field_id")
private int id;
#ManyToMany
#JoinTable(name = "FIELD_SECTION")
private List<Section> sections;
}
with a couple of classes extending it, such as:
#Entity
#Inheritance
public class StringField extends Field<String> {
}
or
#Entity
#Inheritance
public class DateField extends Field<Date> {
}
I also have a Section class, which contains an abstract collection of Field
#Entity
public class Section {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "section_id")
private int id;
#ManyToMany(mappedBy = "sections")
private List<Field<?>> fields;
}
However, the JPA cannot properly map the List<Field<?>> fields collection which results in the following error:
Caused by: org.hibernate.AnnotationException: Use of #OneToMany or #ManyToMany targeting an unmapped class: com.model.Section.fields[com.model.field.Field]
Basicly, it should be doable, since the FIELD_SECTION table would hold a pair of (section_id, field_id). Each type of field would have a separate table, but all of the field_id's would be unique in all of the field tables, allowing the JPA to determine the type of each field.
Is there a way to achieve the above?
Good day!
I am triing to save my Entity model by always get an error
Error inserting bean [class models.CategoryEntity] with unidirectional relationship. For inserts you must use cascade save on the master bean [class models.CategoryEntity].]
Here my class
#Entity
public class CategoryEntity extends Model {
#Id
private String categoryId;
private String Name;
private Integer level;
#OneToMany(targetEntity = CategoryEntity.class, cascade = CascadeType.ALL)
private List<CategoryEntity> categories;
//GETERS SETRES
}
I tried to save the header category, but error is the same
If I understand the question correctly and what you want is that each CategoryEntity contains a list of other CategoryEntities, 2 possible approaches come to mind (although none of them use #OneToMany):
Approach 1:
You could create a #ManyToMany relationship and define a #JoinTable whilst naming its keys:
#Entity
public class CategoryEntity extends Model {
#Id
private String categoryId;
private String name;
private Integer level;
#ManyToMany(cascade=CascadeType.ALL)
#JoinTable( name = "category_category",
joinColumns = #JoinColumn(name = "source_category_id"),
inverseJoinColumns = #JoinColumn(name = "target_category_id"))
public List<CategoryEntity> category_entity_lists = new ArrayList<CategoryEntity>();
}
Approach 2:
Or you could create a new entity for the list of category entities and create a #ManyToMany relationship, e.g.:
#Entity
public class CategoryList extends Model
{
#Id
public Long id;
#ManyToMany
#JoinTable(name="categorylist_category")
public List<CategoryEntity> category_list = new ArrayList<CategoryEntity>();
}
And then in your model:
#Entity
public class CategoryEntity extends Model {
#Id
private String categoryId;
private String name;
private Integer level;
#OneToOne
public CategoryList this_category_list;
#ManyToMany(mappedBy="category_list")
public List<CategoryList> in_other_category_lists = new ArrayList<CategoryList>();
}
Didn't test the code, but what this should do is that each CategoryEntity can be part of several CategoryLists. Each CategoryList contains a list of CategoryEntities.
You would have to initialize this_category_list and add CategoryEntities to its category_list field.
Your model doesnt make sense.
why do you have as class attribute the class itself?
the List should not be in the same class