Is this correct (better) object relational mapping? - java

I have really got stuck with using Hibernate in my project. Some issues I have described here: Hibernate: getting too many rows I have started wondering if my code is correct. I am working on a huge project and I have had to define mapping classes with annotations on my own. But when the problems have began to occur I have decided to recreate part of database separate to the project and try to generate entities in IDE.
I have two tables: My and Option. My has primary key: column qwerty and property. Propertyis the foreign key from Option table. And of course Option has property as a primary key.
In my solution I have created #Embeddable MyPK class with two properties: String qwerty and String property. In my My entity I have of course #EmbeddedId MyPK and also property (the same column name as in the MyPK) but is this Option object, not String as in the MyPK.
#ManyToOne
#JoinColumn(name = "property", nullable = false, insertable = false, updatable = false)
protected Option option;
This is entity generated by Hibernate Tools in Intellij Idea. There isn't EmbeddedId, but there is #IdClass. I have thought that #IdClass is only for basic types. But I have a object as a part of my primary key. However there is also OptionEntity object here. Is this correct to keep basic type and object type for one column?
#javax.persistence.IdClass(test.go.MyEntityPK.class)
#javax.persistence.Table(name = "MY", schema = "PUBLIC", catalog = "PUBLIC")
#Entity
public class MyEntity {
private String qwerty;
#javax.persistence.Column(name = "QWERTY")
#Id
public String getQwerty() {
return qwerty;
}
public void setQwerty(String qwerty) {
this.qwerty = qwerty;
}
private String text;
#javax.persistence.Column(name = "TEXT")
#Basic
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
private String lang;
#javax.persistence.Column(name = "PROPERTY")
#Id
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property= property;
}
#Override
public boolean equals(Object o) {
//equals
}
#Override
public int hashCode() {
//hashCode
}
private OptionEntity optionByProperty;
#ManyToOne
#javax.persistence.JoinColumn(name = "PROPERTY", referencedColumnName = "PROPERTY", nullable = false)
public OptionEntity getOptionByProperty() {
return optionByProperty;
}
public void setOptionByProperty(OptionEntity optionByProperty) {
this.optionByProperty = optionByProperty;
}
}
This is MyEntityPK generated class:
public class MyEntityPK implements Serializable {
private String qwerty;
#Id
#Column(name = "qwerty")
public String getQwerty() {
return qwerty;
}
public void setQwerty(String qwerty) {
this.qwerty = qwerty;
}
private String property;
#Id
#Column(name = "PROPERTY")
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
#Override
public boolean equals(Object o) {
//equals
}
#Override
public int hashCode() {
//hashCode
}
}
OptionEntity below. No special points aren't in this entity. I would like only #Version annotation on version property and also List<MyEntity> instead of Collection<MyEntity>.
#javax.persistence.Table(name = "OPTION", schema = "PUBLIC", catalog = "PUBLIC")
#Entity
public class OptionEntity {
private Long version;
#javax.persistence.Column(name = "VERSION")
#Basic
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
private String property;
#javax.persistence.Column(name = "PROPERTY")
#Id
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
#Override
public boolean equals(Object o) {
//equals
}
#Override
public int hashCode() {
//hashcode
}
private Collection<MyEntity> myByProperty;
#OneToMany(mappedBy = "optionByProperty")
public Collection<MyEntity> getMyByProperty() {
return myByProperty;
}
public void setMyByProperty(Collection<MyEntity> myByProperty) {
this.myByProperty = myByProperty;
}
}
What option is the most proper and less problematic? The one that I have described or the one that pasted?

get rid of property private String lang;/private String property; it is already defined by the manytoone. you also don't need a class for the primary key of MyEntity. MyEntity can be its own id class with the two properties qwerty and option as its key-property, key-manytoone.

Check out JPA 2.0 examples using derived Ids:
http://wiki.eclipse.org/EclipseLink/Examples/JPA/2.0/DerivedIdentifiers
which seem to be what you are after.
In JPA, you do not need an embeddedId for your ids, but if using a composite PK, you do need a class to hold the multiple values that make up the pk. Instances of this class are passed to the em.find method, and it can be either an EmbeddedId or a PKClass. I prefer using PKClass myself, but its up to you - using an embedded just places the fields within the embeddedable class, so you use the embedded object to set the mappings and access the values. If using a pkClass, you do not need the fields/properties annotated within it since they are accessed and mapped within the entity directly.

Related

Why user defined #javax.persistence.Converter isn't recognized by hibernate when mapping the entity field to database column

I have the following Entity containing a field of Enum type:
#Entity
#Table(name = "INPUT_DATA")
public class InputDataEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name = "INPUT_DATA_SEQ", allocationSize = 1, sequenceName = "INPUT_DATA_SEQ")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "INPUT_DATA_SEQ")
private Long id;
#Column(name = "FIELD1", nullable = false)
private String field1;
#Column(name = "FIELD2", nullable = false)
#Convert(converter = Type.Converter.class)
private Type field2;
// getters and setters
}
The Enum type looks like:
public enum Type {
ENUM_ITEM_1("item1"),
// more items
ENUM_ITEM_N("itemN");
private String code;
private Type(String code) {
this.code = code;
}
public static Type fromString(String name) {
switch (name) {
case "item1":
return ENUM_ITEM_1;
// more cases
case "itemN":
return ENUM_ITEM_N;
default:
throw new IllegalArgumentException("Wrong value for Type");
}
}
#Override
public String toString() {
return code;
}
#javax.persistence.Converter
public static class Converter implements AttributeConverter<Type, String> {
#Override
public String convertToDatabaseColumn(Type attribute) {
return attribute.toString();
}
#Override
public Type convertToEntityAttribute(String s) {
return Type.fromString(s);
}
}
}
The problem is that hibernate doesn't recognize my Converter when I want to fetch data from the database.
I've also tried:
#Embedded and #Embeddable but with no luck.
#Enumerated(EnumType.STRING) but again with no luck.
My question is:
how to make hibernate to recognize my converter when converting the appropriate field?
Many thanks in advance.
I eventually ended up by implementing a StringValuedEnum interface and its relevant reflector and type class by implementing EnhancedUserType, ParameterizedType as it was described here.
This helped me to properly store into and retrieve from DB data corresponding to user defined enum types, although the questions with converters remains still open. If someday a proper answer will be given, that will be very appreciated.

Joincolumn returns null value

I am trying to join a column using the #JoinColumn annotation but my column is always returning a null and I am not sure why.
#Entity
public class Blender implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "blender_id")
private int id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "blender", fetch = FetchType.EAGER)
private List<Ingredients> ingredients;
private Status status;
private String type;
public Blender() {
}
public Blender(List<Ingredients> ingredients) {
this.ingredients = ingredients;
}
public List<Ingredients> getIngredients() {
return ingredients;
}
public void setIngredients(List<Ingredients> ingredients) {
this.ingredients = ingredients;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
public int getId() {
return id;
}
#Override
public String toString() {
String result = String.format(
"Blender[id=%d, type=%s, status=%s]%n",id,type,status);
if(ingredients!=null){
for (Ingredients ingredient: ingredients) {
result += String.format(
"ingredients[id=%d,fruit=%s,juice=%s,ice=%s]%n",
ingredient.getId(),
ingredient.getFruit(),
ingredient.getJuice(),
ingredient.getIce());
}
}
return result;
}
}
and Ingredients
#Entity
public class Ingredients implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private int fruit;
private int juice;
private int ice;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(columnDefinition="integer", name = "blender_id")
private Blender blender;
public Ingredients() {
}
public Long getId() {
return id;
}
public int getFruit() {
return fruit;
}
public void setFruit(int fruit) {
this.fruit = fruit;
}
public int getJuice() {
return juice;
}
public void setJuice(int juice) {
this.juice = juice;
}
public int getIce() {
return ice;
}
public void setIce(int ice) {
this.ice = ice;
}
public Blender getBlender() {
return blender;
}
public void setBlender(Blender blender) {
this.blender = blender;
}
#Override
public String toString() {
return "Ingredients{" +
"id=" + id +
", fruit='" + fruit + '\'' +
", juice='" + juice + '\'' +
", ice='" + ice + '\'' +
'}';
}
}
#JoinColumn(columnDefinition="integer", name = "blender_id") is returning null not sure why.
try with just
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "blender_id")
private Blender blender;
#OneToMany(mappedBy = "association", cascade = { CascadeType.ALL })
private List<Company> company;
#ManyToOne
#JoinColumn(name = "association_id")
private Association association;
You can try this pattern.
Read for you.
How to Enable Lazy Loading in Hibernate
Before moving further, it is important to recap the default behavior of lazy loading in case of using hibernate mappings vs annotations.
The default behavior is to load ‘property values eagerly’ and to load ‘collections lazily’. Contrary to what you might remember if you have used plain Hibernate 2 (mapping files) before, where all references (including collections) are loaded eagerly by default. Also note that #OneToMany and #ManyToMany associations are defaulted to LAZY loading; and #OneToOne and #ManyToOne are defaulted to EAGER loading. This is important to remember to avoid any pitfall in future.
To enable lazy loading explicitly you must use "fetch = FetchType.LAZY" on a association which you want to lazy load when you are using hibernate annotations.
An example usage will look like this:
#OneToMany( mappedBy = "category", fetch = FetchType.LAZY ) private Set products; Another attribute parallel to "FetchType.LAZY" is "FetchType.EAGER" which is just opposite to LAZY i.e. it will load association entity as well when owner entity is fetched first time.
How Lazy Loading Works in Hibernate
The simplest way that Hibernate can apply lazy load behavior upon your entities and associations is by providing a proxy implementation of them. Hibernate intercepts calls to the entity by substituting a proxy for it derived from the entity’s class. Where the requested information is missing, it will be loaded from the database before control is ceded to the parent entity’s implementation.
Please note that when the association is represented as a collection class, then a wrapper (essentially a proxy for the collection, rather than for the entities that it contains) is created and substituted for the original collection. When you access this collection proxy then what you get inside returned proxy collection are not proxy entities; rather they are actual entities. You need not to put much pressure on understanding this concept because on runtime it hardly matters.

Hibernate creates two rows instead of one

pals.
I have an issue with Hibernate's JPA implementation. I use spring-boot-starter-data-jpa and PostgreSql v9.
I have two entities with bidirectional connection via OneToMany & ManyToOne:
#Entity
public class ShoppingCart {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#OneToMany(mappedBy = "shoppingCart", cascade = {CascadeType.ALL})
private List<Good> goods = new ArrayList<>();
public void addGood(Good good) {
good.setShoppingCart(this);
goods.add(good);
}
public Good removeGood(Good good) {
goods.remove(good);
good.setShoppingCart(null);
return good;
}
public ShoppingCart() {
}
public List<Good> getGoods() {
return goods;
}
public ShoppingCart(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
}
And second entity is
#Entity
public class Good {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "cart_id")
#JsonIgnore
private ShoppingCart shoppingCart;
public ShoppingCart getShoppingCart() {
return shoppingCart;
}
public void setShoppingCart(ShoppingCart shoppingCart) {
this.shoppingCart = shoppingCart;
}
public Good(String name) {
this.name = name;
}
public Good() {
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
}
Also I use CrudRepository to access ShoppingCart
public interface ShoppingCartRepository extends CrudRepository<ShoppingCart, Long> {}
And when I'm trying to fill existing cart I have two goods in my database. This is a code to add some goods into existing cart:
ShoppingCart cart = shoppingCartRepository.findOne(id);
cart.addGood(new Good("Butter"));
return shoppingCartRepository.save(cart);
In table "good" I have now two elements with different PKey and same data
5;"Butter";100
6;"Butter";100
Why it happens?
Also, when I'm trying to insert breakpoint at repository.save line, I see only one good in goods list in cart.
So, the problem is solved.
First way to solve is to make method with save code #Transactional.
Secon way is to use getGoods() instead of goods. We should change this code
public void addGood(Good good) {
good.setShoppingCart(this);
goods.add(good);
}
public Good removeGood(Good good) {
goods.remove(good);
good.setShoppingCart(null);
return good;
}
to this
public void addGood(Good good) {
good.setShoppingCart(this);
this.getGoods().add(good);
}
public Good removeGood(Good good) {
this.getGoods().remove(good);
good.setShoppingCart(null);
return good;
}
getGoods() here forces hibernate to update state of object and everything works fine.
As for me, I use both ways together
It happens because you create a new Good object without id. So Hibernate will generate a new id and persist the new object. If you don't want to create a new object, but only assign an already existing one, you either have to fetch the existing one from the database and assign it to the ShoppingCart oder add the ID if you create the new Good object.

What's the right way to map a polymorphic relationship with JPA?

As the title may be a bit vague, here is an explanation of my problem (which is not easy to summarize into a concise title).
My problem is quite simple (what I'm trying to achieve seems really intuitive to me), though solving it might not be. I've two abstract classes representing tied concepts. As an example I choose Project and Task. A given project can have many tasks. These classes are abstract because there are different kind of projects and different kind of tasks, but projects and tasks kinds are the same. For example there are development project and development tasks (may seems a bit odd but this is a simplified example). Of course there are other kinds of projects and tasks but let's stick to these ones.
What I want to achieve is to map, with JPA, my relationship in the two abstract classes and benefit from it in the concrete classes. What complicates things is that I must also map inheritance: single table with discriminator, which is also part of classes ids. I add generic types to abstract classes to ensure that I can force concrete classes to refer to the right associated classes (DevelopmentProject tied to DevelopmentTask).
Problem is, whatever I try, I always get an error of some kind. What I seek here is somebody that've achieved something like this. I must tell you that I'm working with a legacy database (I'm well aware that current design suffer from many problems, but there is unfortunately nothing I can do for it).
To help you, I join the example code I made, that way you will see what I meant in the above paragraphs.
#Entity
#Table(name = "PROJECT")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "TYPE", discriminatorType = DiscriminatorType.INTEGER)
public abstract class Project<T extends Task> {
/* Fields */
private ProjectId id;
private List<T> tasks;
/* Constructors */
protected Project() {}
public Project(ProjectId id) {
this.id = id;
}
/* Getters */
#EmbeddedId
public ProjectId getId() {
return id;
}
#OneToMany(fetch = FetchType.EAGER, mappedBy = "project", targetEntity = Task.class)
public List<T> getTasks() {
return tasks;
}
/* Setters */
public void setTasks(List<T> tasks) {
this.tasks = tasks;
}
}
#Embeddable
public class ProjectId {
/* Fields */
private Integer type;
private Integer number;
/* Constructors */
#SuppressWarnings("unused")
private ProjectId() {} /* Empty constructor for hibernate */
public ProjectId(Integer type, Integer number) {
this.type = type;
this.number = number;
}
/* Getters */
#Column(name = "TYPE", nullable = false)
private Integer getType() { /* Only for Hibernate */
return type;
}
#Column(name = "NUMBER", nullable = false)
public Integer getNumber() {
return number;
}
/* Setters */
#SuppressWarnings("unused")
private void setType(Integer type) { /* Only for Hibernate */
this.type = type;
}
public void setNumber(Integer number) {
this.number = number;
}
}
#Entity
#Table(name = "TASK")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "TYPE", discriminatorType = DiscriminatorType.INTEGER)
public abstract class Task<P extends Project> {
/* Fields */
private TaskId id;
private P project;
/* Constructors */
protected Task() {}
public Task(TaskId id) {
this.id = id;
}
/* Getters */
#EmbeddedId
public TaskId getId() {
return id;
}
#ManyToOne(fetch = FetchType.EAGER, optional = false, targetEntity = Project.class)
#JoinColumns({
#JoinColumn(name = "TYPE", referencedColumnName = "TYPE", insertable = false, updatable = false),
#JoinColumn(name = "PROJECT_NUMBER", referencedColumnName = "NUMBER", insertable = false, updatable = false)
})
public P getProject() {
return project;
}
/* Setters */
public void setId(TaskId id) {
this.id = id;
}
public void setProject(P project) {
this.project = project;
}
}
#Embeddable
public class TaskId {
/* Fields */
private Integer type;
private Integer projectNumber;
private Integer number;
/* Constructors */
#SuppressWarnings("unused")
private TaskId() {} /* Empty constructor for Hibernate */
public TaskId(Integer type, Integer projectNumber, Integer number) {
this.type = type;
this.projectNumber = projectNumber;
this.number = number;
}
/* Getters */
#Column(name = "TYPE", nullable = false)
private Integer getType() { /* Only for Hibernate */
return type;
}
#Column(name = "PROJECT_NUMBER", nullable = false)
public Integer getProjectNumber() {
return projectNumber;
}
#Column(name = "NUMBER", nullable = false)
public Integer getNumber() {
return number;
}
/* Setters */
#SuppressWarnings("unused")
private void setType(Integer type) { /* Only for Hibernate */
this.type = type;
}
public void setProjectNumber(Integer projectNumber) {
this.projectNumber = projectNumber;
}
public void setNumber(Integer number) {
this.number = number;
}
}
#Entity
#DiscriminatorValue(value = "1")
public class DevelopmentProject extends Project<DevelopmentTask> {
/* Constructors */
#SuppressWarnings("unused")
private DevelopmentProject() { /* Empty constructor for Hibernate */
super();
}
public DevelopmentProject(DevelopmentProjectId id) {
super(id);
}
}
public class DevelopmentProjectId extends ProjectId {
/* Constructors */
public DevelopmentProjectId(Integer number) {
super(1, number); /* Development type is forced */
}
}
#Entity
#DiscriminatorValue(value = "1")
public class DevelopmentTask extends Task<DevelopmentProject> {
/* Constructors */
#SuppressWarnings("unused")
private DevelopmentTask() { /* Empty constructor for Hibernate */
super();
}
public DevelopmentTask(DevelopmentTaskId id) {
super(id);
}
}
public class DevelopmentTaskId extends TaskId {
/* Constructors */
public DevelopmentTaskId(Integer number) {
super(1, number); /* Development type is forced */
}
}
EDIT :
I corrected my example a bit (adding a projectNumber to Task, without it the relationship seems meanless, and even erroneous). I also added targetEntity on my relationships (I forgot it and you will see why below).
Now I will try to explain you my path until here.
First of all I used MappedSuperclass annotation on my superclasses and no targetEntity on my relationships. That lead me to an error because of the generics: Hibernate didn't know what to map and suggest me to either remove generics, either use targetEntity, either use #Type (but that last one being Hibernate specific I prefer not to use it). So I set up targetEntity with Project.class and Task.class (at the superclasses level there were nothing else to do it seems).
Then, I ran into another problem because of MappedSuperclass annotation. Hibernate tells me there is a reference to an unknown entity Project (or Task) [I'm adapting from there errors I got in my real project but I'm pretty sure I'll have the same once I'll manage to set those two tables up and tests them]. I googled that error and finally found that MappedSuperclass was the problem and that I could use Entity on abstract classes. So did I.
After that, and that's the last error I got : repeated column mapping for column TYPE in DevelopmentProject. But If I try to set insertable = false and updatable = false everywhere this column is defined (id, relationship)... that doesn't change anything. I fear to have fallen in the same case of my precedent question : How to map to attributes from embedded class to a single database column with JPA?. But in that case, there was an easy and not-too-ugly workaround. In this case I can't see it.
And I really wonder if someone have ever tried to achieve something like this and succeed, even if the solution is different, as it's as handy and elegant as possible.

Foreign key usage in Java with Hibernate

I am trying to develop a web application and I was wondering if there is a way to utilize a foreign key without writing a lot of code.
My Trainees.java
#Entity
public class Trainees {
#Id
#GeneratedValue
private int traineesID;
private int groupsID;
#ManyToOne
#JoinColumn(name = "status_trainee")
private String status_TraineeID;
private int customersID;
private String name;
private String surname;
private String phoneDetails;
private String email;
public Trainees(){
}
public Trainees(String name, String surname, String phoneDetails, String email, int id, int groupsID, String status_TraineeID, int customersID) {
super();
this.name = name;
this.surname = surname;
this.email = email;
this.phoneDetails = phoneDetails;
this.groupsID = groupsID;
this.status_TraineeID = status_TraineeID;
this.customersID = customersID;
}
//getters and setters
#Override
public boolean equals(Object object) {
if (object instanceof Trainees){
Trainees contact = (Trainees) object;
return contact.traineesID == traineesID;
}
return false;
}
#Override
public int hashCode() {
return traineesID;
}
}
Status_Trainee.java
#Entity
public class Status_Trainee {
#Id
#GeneratedValue
private int status_traineeID;
private String value;
public Status_Trainee(){
}
public Status_Trainee(String value, int id) {
super();
this.value = value;
}
//getters and setters
#Override
public boolean equals(Object object) {
if (object instanceof Status_Trainee){
Status_Trainee value = (Status_Trainee) object;
return value.status_traineeID == status_traineeID;
}
return false;
}
#Override
public int hashCode() {
return status_traineeID;
}
}
Error: Caused by: org.hibernate.AnnotationException: #OneToOne or #ManyToOne on uaiContacts.model.Trainees.status_TraineeID references an unknown entity: String
So my aim is that using the Trainees table and class, I could retrieve the value of Status_Trainee table using the foreign key. For example: if foreign keys ID is 2, then it would retrieve a string from status_trainee table where primary key would match the foreign key ID.
I am using models, controlers, hibernate and angularjs to display to the view, I don't really want to pass the table through all this, I thought using something like ManyToOne or JoinColumns would retrieve the value?
Thanks for all the help!
You should add a reference to StatusTrainee in Trainee and annotate that with OneToMany, ManyToOne or OneToOne. Depending on which kind of relationship you will need a list of StatusTrainee or just a StatusTrainee.
Tip: dont use underscores in class names.
First of all, it is not recommended to use "_" in a class name when using hibernate. Hibernate uses underscores when accessing foreignKeys. So Lets Say you rename your class to: TraineeStatus and the id change it to traineeStatusId..
Secondly, You can use the Hibernate annotations for what you need. but first you need to know how the relation is:
#OneToMany : One Trainee can have lots of statuses
#ManytoOne : Many trainees can have the same status
#OneToOne : one Trainee Can only have one status and the other way around.
Try this:
#Entity
public class Trainees {
#Id
#GeneratedValue
private int traineesID;
private int groupsID;
#OneToOne
private TraineeStatus status;
private int customersID;
private String name;
private String surname;
private String phoneDetails;
private String email;
...
You can change the #OneToOne for the one you need..
Remember that hibernate will try to map this in your Trainees mysql table as status_traineeStatusId, so if you have this column (as an integer) at your trainess table you are done :)..
That is it..
Hope it helps

Categories