I have a class Health Check - as part of the class I record how many parasites are seen (enum NONE, SOME, MANY) and also the location of the parasites (enum HEAD, FEET, BODY).
Two ways this could be done:
METHOD 1
Health Check
#Entity
public class HealthCheck {
#Id
#GeneratedValue(strategy = AUTO)
private Long id;
private Parasite parasite;
Parasite
public class Parasite {
private BodyLocation bodyLocation;
private Coverage coverage;
}
Or I could have:
METHOD 2
#Entity
public class HealthCheck {
#Id
#GeneratedValue(strategy = AUTO)
private Long id;
private ParasiteNumber parasiteNumber;
private ParasiteLocation parasiteLocation;
Would method 1 require #Entity on parasite class and a entry in the table for each Health Check and a #OneToOne annotation?
Note Parasite class is not used anywhere else. Its only a part of a Health Check.
Which way is correct?
Generally speaking, yes. In ORM (aka JPA or Hibernate), you are building a graph of objects that represent things in your database. Anything that one #Entity touches is also an #Entity because it's a graph.
Whether it's a #OneToOne or a #ManyToOne, etc, depends on the nature of your data model. But, keep in mind, those connections can also be #Lazy, so they are not loaded until they are needed.
Because of #Lazy, method 2 might be preferred, idk. I assume ParasiteLocation and ParasiteNumber is some sort of join-table. If that's the case, you could load a HealthCheck with its PL and PN, but those objects could be Lazy to Parasite.
I don't think there is a one-size-fits-all answer to your question. It very much depends. But good news, ORM is flexible to cover any/all scenario you might have.
If Parasite is only used in HealthCheck class,which can be seen as an association.
Association means that the existence of child class is dependent on the existence of the parent so it has no independent lifecycle ,thus you can either declare the attributes directly in HealthCheck as you did in your second example ,or you can declare them in Parasite class and then make it Embeddable inside the HealthCheck class,e.g:
/*To embed a class inside in Entity you must declare it Embeddable via the JPA
annotation #Embeddable */
#Embeddable
public class Parasite {
#Column(name="body_location")
private BodyLocation bodyLocation;
#Column(name="coverage")
private Coverage coverage;
}
#Entity
public class HealthCheck {
#Id
#GeneratedValue(strategy = AUTO)
private Long id;
#Embedded
private Parasite parasite;
}
Here your HealthCheck db table will have the attributes specified in the Parasite class,and note that Parasite table won't be created since it is Embedded and not an Entity (#Entity).
Hope this helps!
Related
I am trying to persist my Game class, structured as follows:
An class Game, which holds a list of Boards
The Board Entity class a 2D array of Fields, representing a playing board.
The Field class is abstract and has concrete subclasses such as Floor, Goal and Wall.
What I would like to have is a One-to-many relationship beween Game and Board Entities, and the Fields in the Board class as an embedded property. To embed this I'd like to write my own mapping code to turn the 2D array into a String of symbols representing the fields, but if that's not possible then I'd save the hierarchy using the single-table strategy.
I'm still learning JPA/Hibernate so there's a bit too much going on at once for me to fully grasp how to tackle this.
This is what I've tried:
Implementing an AttributeConverter for Board
I learned that this is not possible because AttributeConverters cannot be used for relationship attributes (JPA spec).
Implementing an AttributeConverter for Field[][], and making it an Embedded property
#Entity
#NoArgsConstructor
public class Game {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Getter
private Long id;
#OneToMany(mappedBy = "game", cascade = CascadeType.ALL)
#Getter
private List<Board> boards = new ArrayList<>();
...
}
#Entity
public class Board {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
#ManyToOne()
#JoinColumn(name="game_id")
private Game game;
#Embedded
#Convert(converter = FieldsConverter.class, attributeName = "field")
private Field[][] fields;
with FieldsConverter being
#Converter
public class FieldsConverter implements AttributeConverter<Field[][], String> {
IntelliJ already gives a "Embedded atribute type should not be Field[][]" error, running results in a weird ArrayIndexOutOfBoundsException (see https://pastebin.com/ZVbCGaGA for full stacktrace).
Writing the JDBC mapping myself
I also started writing a BoardRepository myself, in which I manually did the mapping of the Fields attribute, but then I remembered that then I'd probably have no managed relationship between Game and Board, or the lazy loading that JPA would provide, so I stopped before I was fully ready.
One thing I read that comes close to a solution is this: https://vladmihalcea.com/multidimensional-array-jpa-hibernate/, but I'm not sure how to adapt this to an Embedded attribute and the object actually being part of an object hierarchy.
Could anyone please give me some pointers on how to proceed?
I have an entity with two Embedded classes of the same type and which one has an ElementCollection of the same type two. The business logic is apparently correct, but I am experiencing some problems with lack of knowledge in JPA, I guess.
Let's check my classes:
#Entity
public class Etapa extends EntidadeBase {
#Embedded
private CronogramaDeDesembolso cronogramaDeReceita;
#Embedded
private CronogramaDeDesembolso cronogramaDeDespesa;
}
#Embeddable
public class CronogramaDeDesembolso {
#ElementCollection
private List<Parcela> parcelas;
}
I am receiving the following error log.
Caused by: org.hibernate.HibernateException: Found shared references
to a collection:
nexxus.convenioestadual.dominio.planodetrabalho.etapa.Etapa.cronogramaDeReceita.parcelas
Do you guys have any clue of what is wrong and how can I fix it?
EDIT:
Due comments I did this edit and it do not worked too
#Entity
public class Etapa extends EntidadeBase {
#Embedded
#AttributeOverride(name = "parcelas", column = #Column(name = "parcelasReceita"))
private CronogramaDeDesembolso cronogramaDeReceita;
#Embedded
#AttributeOverride(name = "parcelas", column = #Column(name = "parcelasDespesa"))
private CronogramaDeDesembolso cronogramaDeDespesa;
}
Is there any reason why you have decided to use this structure ? Typically when converting an object to an RDBMS you would need to model the relationships. When you use an embeddable it will add the column (or columns) associated with it to the table. So when you do this normally (not collections) it is fine.
When you do a collection it runs into issues. Mainly there is no way to represent a collection in a single row (since this is an entity you could have many of them so effectively for each object you only have one row) & one column. So when you represent a collection you actually have to have a second table with a column referencing it back to the first. It's really the opposite thinking of a normal object. The collection entries need to know what collection they were associated with instead of the collection being knowledgeable of its entries.
So in some POJO you could have and these....
MyListObject {
//Some implementation of things you want to collect
}
MyClass {
List<MyListObject> myListObject;
}
But to model this in JPA you would need to have these represented by two tables.
Your object that will be in the list.
#Entity
MyListObject {
#ManyToOne
#JoinColumn(name = "MY_CLASS_KEY")
private MyClass myClass;
}
Your object/entity that will have the list.
#Entity
MyClass {
#Id
#Column(name = "MY_CLASS_KEY")
private Long myClassKey;
#OneToMany(mappedBy = "myClass")
private List<MyListObject> myString;
}
I hope this helps.
A quick search on Google turned up this in StackOverflow:
JPA Multiple Embedded fields
It would seem as though you have to do some explicit annotation overriding over the fields within the embeddable class. There are some code examples in the linked answer as well that should give you a good idea of where to go.
Cheers,
In my use-case, I would like to #Embedded a class C in an entity.
Another entity refers to C with #OneToMany association and therefore C is annotated with #Entity.
I am aware that this seems like bad design, yet I believe that it makes perfect sense in my case.
Is it possible to force Hibernate to embed an Entity? If I try it, Hibernate complains about a missing setter for the id property of C.
I think the problem comes from this:
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
Why not just create the entity that you want, and in that entity, embed C as well. That way you have C in both classes, one as embedded and another as embedded of the new entity.
#Embeddable
public class Contact {
private String firstname;
private String lastname;
// getters and setters removed.
}
and here is your embedding class:
#Entity
public class Student {
#Embedded
private Contact contact;
}
and here is the new entity that embeds contact also
#Entity
public class FirmContact {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int contactId;
#Embedded
private Contact contact;
}
And finally the class that insists the contact must be an entity:
#Entity
public class Business {
#OneToOne(cascade=CascadeType.ALL)
private FirmContact contacts;
}
It'll just be a couple of extra steps in java to populate the object, but it should do the mapping you want. I hope this helps.
Hibernate doesn't allow you to treat an Embeddable as an Entity or to embed an Entity. According to Hibernate types:
an Embeddable, doesn't have an identifier, since it's state is part of an owning Entity.
an Entity cannot be embedded, because each Entity has a distinct life-cycle.
Since another class already has a #OneToMany association to class C, it's obvious you cannot turn it into an Embeddable.
More, a bidirectional #OneToMany association will perform better than an embeddable collection.
What you can do, is to use it as a #OneToOne association in the entity where you wanted to embed the C entity. You can make that target entity be the owning side of the association so that the C association is bound to the target entity life-cycle.
I have an entity named User. I decided that every user has its `Report (also entity) list. Then I created something like this:
#Entity
#Table(name="user")
public class User implements Serializable{
#Id
#GeneratedValue
private long id;
...
#ElementCollection
private List<Report> reportList;
Now the Report object looks like this:
#Entity
#Table(name = "report")
public class Report implements Serializable{
#Id
#GeneratedValue
private long id;
...
#ManyToOne
private User reporter;
Is it right approach? Every user can have many reports, but every report can belong to only one user.
I thought about changing #ElementCollection to #ManyToOne, but I think I prefer operating on a list.
The main problem is, how to make relation(connection) between user and their reports?
No, it's not the right approach. ElementCollection is for a collection of simple types (String dates, etc.) or embeddable types. For a collection of entities, you need a OneToMany association (which is quite normal, given that you have a ManyToOne in the other direction):
one user has many reports
many reports are reported by one user
.
#OneToMany(mappedBy = "reporter")
private List<Report> reportList;
The documentation covers this in detail.
I am using Java, Hibernate, Spring Data and fairly new to this technology. I need to figure out how to Skip rows that are marked as 'archived.' We have a strict guidance from our DB architect that no rows shall be deleted from the database.
#MappedSuperclass
public class AbstractEntity implements Identifiable<String> {
#Id
#GeneratedValue(generator="system-uuid")
#GenericGenerator(name="system-uuid", strategy="uuid")
private String id;
private boolean archived; //<----?
}
#Entity
public class Employee extends AbstractEntity {
private String title;
private String fullName;
#ManyToOne
private Department dept;
}
#Entity
public class Department extends AbstractEntity {
private String name;
}
In the above example, any class extending AbstractEntity should never return rows that have archived == true. All my domain classes will be extending AbstractEntity so I'd like a solution that's either implemented in AbstractEntity.java or at some global configuration so that all generated SQL calls are 'where [table].archived <> true'
Take a look at Hibernate Filters.
#FilterDef(name="activeOnly")
#Filter(name="activeOnly", condition= "archived <> 1")
// assumes that all tables have a numeric column "archived"
// as you can notice, this is a filter at the SQL Level
// (not at the Entity level)
#MappedSuperclass
public class AbstractEntity // ....
I've never used Spring Data, but the Adding custom behavior to all repositories section of the official documentation lead me to belieave that it is quite easy to obtain an injected EntityManager and customize its behaviour. Just unwrap it and enable the filter.
Session session = entityManager.unwrap(Session.class);
session.enableFilter("activeOnly");
If you want the filter to be applied for all subclasses of the #MappedSuperclass use a recent version of Hibernate. Only version 3.5 and greater (see HHH-4332) supports this behaviour.
Also, there is one gotcha, you may need to repeat the filter on associations (See Hibernate Filters on related table with MappedSuperClass).
If you want to customize the delete operations as well, use #SQLDelete to mark archived = 1 (see Soft deletes using Hibernate annotations). But to the best of my knowledge this only works on mapped entities (nothing can be done at the #MappedSuperclass level)