In a bidirectional mapping between entities (e.g. #ManyToOne ↔ #OneToMany), the counterpart needs to be synchronized on every change, especially when using a 2nd level cache. This is usually done with helper methods. Those helper methods do not perform well if the Many part contains a lot of entries, because the whole set is fetched each time. A simple example would be an entity Store which has n Products, whereas n is very large. Adding a new Product to the Store would require the Store to fetch the whole list of Products to finally add it to the set (see code example below).
One could argue, that when modelling such a relation it would be better represented with an unidirectional association from the Product to the Store. We are using many JPQL queries in our application though. In JPQL, it comes very handy to join entities from both sides.
Do you see any problems when mapping the #OneToMany relation in the Store entity, when Many actually means many and not just a few, and just make the field private, without getters and setters, provided that the whole relation is lazily fetched? As I understand, Hibernate just needs the field to map the relation. And if the set is private, no performance issues should occur?
The Product entity:
#Entity
#Table(name = "product")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Product {
#ManyToOne(fetch = FetchType.LAZY)
private Store store;
// setter, getter, helper methods
}
The Store entity:
#Entity
#Table(name = "store")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Store {
#OneToMany(mappedBy = "products", fetch = FetchType.LAZY)
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<Product> products;
// setter, getter, helper methods
}
No, there is nothing wrong with it, it's a technique I've used often as well. Of course just make sure that the field is not accessed reflectively in some context (like automatic toString builders and similar utilities you may be using).
Also, you don't need the #Cache annotation on it since you will never access the collection anyway, thus it will never be cached.
Related
Is it possible to annotate a class as #Embeddable or a property as #Embedded?
Sample code:
#Embeddable
class A{
...
}
class B{
...
}
#Entity
class Foo {
A a;
#Embedded B b;
}
When to prefer #Embedded and #Embeddable?
There are two primary uses for #Embedded/#Embeddable as far as I know:
First, and most important: Splitting up large entity classes. In the database world, a large table (one with many columns) is fine. Breaking up such a table might even make things worse, and be in collision with database design principles. In Java (or object oriented languages in general), on the other hand, a large class is a code smell. Here, we would like to split the classes, including entity classes, into smaller units. #Embedded/#Embeddable allows us to easily do this without having to split the database table.
Second, it allows for reuse of common mappings between entities. Say each table has a simple revision tracking, with two columns containing the username of the person who changed the row, and the time it happened. Then, one can make an #Embeddable entity covering these rows, and then reuse this across all entities by embedding it (rather than repeating the variables corresponding to these columns in each entity.)
If we have Person and Address that are two POJOs, You would not want to create another table for Address but you would want to embed the address within the person table. So Address is adding up value to the Person object but doesn't make any sense individually. In this case we may go with:
#Embeddable
public class Address{
}
#Entity
public class Person
{
#Embedded
private Address address;
}
You would use #Embeddable and #Embedded together. You mark your class as #Embeddable, which indicates that this class will not exist in the DB as a separate table. Now when you use #Embedded on the field itself.
The word embeddable and embedded gives you a big clue actually.
Embeddable = This class can be embedded in a class
Embedded = This class will now be embedded in your class as a field.
I guess if you annotate class as #Embeddable you don't need to annotate field as #Embedded. Also, if you annotate class as #Embeddable and you want to use it as primary key, you can use #Id only, but if it is not annotated as #Embeddable, you have to use #EmbeddedId on field to work as primary key.
I have what I would think is a fairly typical setup in EclipseLink, although admittedly I am a bit new to EclipseLink. I've worked with Persistence and Hibernate a bit, but usually through some convenience wrappers and such and not as directly as I am here. Persisted class A contains a list of persisted class B. It's annotated as follows:
public class A {
#OneToMany(fetch = FetchType.EAGER, targetEntity = B.class, cascade = CascadeType.ALL, mappedBy = "fieldA")
#PrivateOwned
#CascadeOnDelete
private List<B> bItems;
}
public class B {
#ManyToOne(targetEntity = A.class, fetch = FetchType.EAGER)
#JoinColumn(name = "JOIN_COL")
private A aItem;
}
The creation and editing of these objects is working just fine. When an object A is deleted, it cascades as it should and any B objects tied to it are cleared out as well. The issue I'm hitting is trying to clear B objects from A directly. Normally this is done via a UI with some intermediary DTO objects between. That's working fine. However, I have some backend code that needs to clear out all of Object B directly from the domain object A in certain circumstances. It seems that using:
itemA.getBItems().clear();
is not working. I would think that this should be picked up by the EntityManager (which is done with a simple entityManager.persist(objectA) in a service class), but it's not clearing the associated B objects in the database. Is that simply the nature of the way I'm doing it or even the way the ArrayList class implements clear() (I'd try some other methods, but the nature of the beast in this case makes it difficult and time-consuming to properly test), or do I need to get a bit more fine grained on how I do this? Perhaps my annotations are a bit off? Or perhaps I'm doing things correctly but need to add a convenience method to the service class that clears out the B objects via the EntityManager and evicts any relevant caches?
I am going through the Hibernate documentation for bidirectional relationship, in the doc it says that:
Example 7.21. Bidirectional one to many with many to one side as association owner
#Entity
public class Troop {
#OneToMany(mappedBy="troop")
public Set<Soldier> getSoldiers() {
...
}
#Entity
public class Soldier {
#ManyToOne
#JoinColumn(name="troop_fk")
public Troop getTroop() {
...
}
Troop has a bidirectional one to many relationship with Soldier
through the troop property. You don't have to (must not) define any
physical mapping in the mappedBy side.
To map a bidirectional one to many, with the one-to-many side as the
owning side, you have to remove the mappedBy element and set the many
to one #JoinColumn as insertable and updatable to false. This solution
is not optimized and will produce additional UPDATE statements.
Example 7.22. Bidirectional association with one to many side as owner
#Entity
public class Troop {
#OneToMany
#JoinColumn(name="troop_fk") //we need to duplicate the physical information
public Set<Soldier> getSoldiers() {
...
}
#Entity
public class Soldier {
#ManyToOne
#JoinColumn(name="troop_fk", insertable=false, updatable=false)
public Troop getTroop() {
...
}
I am finding difficulty in understanding this as I am new to Hibernate.
1) What it means when the doc says:
You don't have to (must not) define any physical mapping in the mappedBy side.
2) #JoinColumn in 7.22 has same value (troop_fk) for name attribute. Can we specify different values? What is the advantage & disadvantages of setting insertable=false, updatable=false here?
Can someone please explain?
It's a bidirectional association. So, if a soldier balongs to a troop, the troop contains the soldier. These are just the two ways of saying the same thing.
In Soldier, you tell how the association is represented in the database: using a join column named troop_fk:
#JoinColumn(name="troop_fk")
public Troop getTroop() {
So, repeating that same information on the other side of this bidirectional association is redundant. You must not do it. By saying
#OneToMany(mappedBy="troop")
public Set<Soldier> getSoldiers() {
you're telling Hibernate that getSoldiers() is the inverse side of a bidirectional association, and that the way this association is mapped can be found on the Soldier.troop property.
Regarding your second question. Once again, the goal is to define a single, but bidirectional association. You don't need two different foreign keys to map a single association. So specifying a different name for the join column would make no sense: it would create a different, unidirectional association.
This way of doing is an ugly hack that, AFAIK, is not supported by the JPA spec. The JPA spec mandates that the owner side of a bidirectional OneToMany association is the many side. In fact it creates two unidirectional associations mapped the same way, and tells Hibernate (using insertable = false and updatable = false) to ignore one of them when saving the entity. It will populate soldier.troop when reading a soldier from the database, but whatever you put into soldier.troop will be ignored when saving the soldier. You should avoid this way of doing, IMHO.
What is the difference between #Embedded annotation technique and #OneToOne annotation technique because in Embedded the java class contain "Has a" relationship in class and with the help of #Embedded annotation we persist the has a object in database. and in OneToOne relationship we also persist the has a object in database.
#OneToOne is for mapping two DB tables that are related with a one to one relationship. For example a Customer might always have one record in a Name table.
Alternatively if those name fields are on the Customer table (not in a separate table) then you might want an #embedded. On the face of it you could just add the name fields as standard attributes to the Customer entity but it can be useful if those same columns appear on multiple tables (for example you might have the name columns on a Supplier table).
Its the difference between composition and aggregation. #Embedded objects are always managed within the lifecycle of their parents. If the parent is updated or deleted, they are updated or deleted as well. #OneToOne objects may mimic composition via the cascadeType option of their #Join annotation, but by default they are aggregated, aka their lifecycle is separate from that of their parent objects.
#Embedded is used with Value Objects (Objects which have a meaning only when attached to an Object) whereas one to one mapping is between two objects having their own existence and meaning.
For e.g.
Value Object and #Embedded: If we have a User class and this class has an address Object in it, it can be considered as a value object as the address alone does not have any significance until unless associated with a user. Here address object can be annotated with #Embedded.
One to One mapping and #OneToOne: If we have a User class and this class has a 'Father' Object or a 'Mother' object, we would want to annotate the 'Father' or 'Mother' instance as #OneToOne as 'Father' or 'Mother' have their own meaning and existence and are not Value objects to User class.
A closely related difference is between #OneToMany and #ElementCollection. Both are used to save instance variables of Collection type in Java class. The difference being, #ElementCollection is to be used when the elements of Collection being saved are Value Objects whereas #OneToMany is used when the elments and object have well defined meaning and existence.
Use #OneToOne, only if fields can be reused. Otherwise, go for #Embeddable.
A quote from Beginning Hibernte, 3rd Edition:
There is nothing intrinsically wrong with mapping a one-to-one association between two entities where one is not
a component of (i.e., embedded into) the other. The relationship is often somewhat suspect, however. You should
give some thought to using the embedded technique described previously before using the #OneToOne annotation.
#Embeddable:
If the fields in an entity (X) are contained within the same table as another entity (Y), then entity X is called "component" in hibernate terms or "embedded" in JPA terms. In any case, JPA or hibernate do not allow to use 2nd table to store such embedded entities.
Generally, we think of normalizing a table when data is being reused by more than one table. Example: A Customer (id, name, street, city, pin, landmark) can be normalized into Customer(id, name) and CustomerAddress(cust_id, street, city, pin, landmark). In this case, we can reuse CustomerAddress by linking the same using cust_id with other tables. But if this reuse is not required in your application, then we can just keep all columns in one table.
So, a thumb rule is,
If reuse -> #OneToOne,
If no reuse -> #Embeddable
#Embedded is typically to represent a composite primary key as an embeddable class:
#Entity
public class Project {
#EmbeddedId ProjectId id;
:
}
#Embeddable
Class ProjectId {
int departmentId;
long projectId;
}
The primary key fields are defined in an embeddable class. The entity contains a single primary key field that is annotated with #EmbeddedId and contains an instance of that embeddable class. When using this form a separate ID class is not defined because the embeddable class itself can represent complete primary key values.
#OneToOne is for mapping two DB tables that are related with a one to one relationship. #Id will be the primary key.
Let's say I have the following class structure:
/** Boring bits snipped */
#Entity
#Table(name = "Foo")
public class Foo {
#JoinColumn(name = "id")
private Bar bar;
/** Other flat data goes here */
}
#Entity
#Table(name = "Bar")
public class Bar {
/** Some data goes here */
}
For reasons I'm not going to go into, I have copies of these tables which I want to also map too, which should appear in Java to also be Foo and Bar objects. Most importantly, the relationships between tables should be between the copied tables when dealing with copied objects.
What is the most correct way of doing this?
I'm guessing I can probably do something like this:
#Entity
#Table(name = "OtherFoo")
public class OtherFoo extends Foo {
#JoinColumn(name = "id")
private OtherBar bar;
}
#Entity
#Table(name = "OtherBar")
public class OtherBar extends Bar {
}
But is that the right way to do it?
You're close, but you can't just inherit from another entity and change the table like that. Entity inheritance has to follow one of the provided inheritance models. It may be for your use case as simple as adding #Inheritance(strategy=InheritanceType.TABLE_PER_CLASS) to the superclass. There are some limitations to this if you have some more complicated mappings with other classes. Since it won't be able to tell which table a superclass based mapping is actually in, it can't join through it. And mappings to the superclass will require checking both tables every time. You also of course need unique ID generation across all the tables in the hierarchy. You may want to consider using an abstract superclass and having both concrete entities be leaf classes. Then at least you can always work with just a single table when you know which one it is.
Alternately you can declare your column mappings in an #MappedSuperclass and each subclass can then be an entity with a table mapping. That might work better if it's legacy data and you don't have unique IDs across the 'regular' and 'copy' tables.