Is it possible to create some combination of annotations, that provide following:
Have 2 tables (one-2-many relationship)
Is it possible on JPA level without programming, just create object of "one" class and if there is id set that just make it reference in "many" table and in case that id is not set, create new record in "one" table and make reference to that id in "many" table
No. You will need to create all the objects by your self.
What you can do is to use cascade to do a automatic persist if you want to.
It would be something like:
a.setB(b);
b.getAList().add(a);
entityManager.persist(a);
And in your classes you would map like:
public class A {
// other methods and ID
#ManyToOne
#JoinColumn(name = "b_id")
private B b;
}
public class B {
// other methods and ID
#OneToMany(mappedBy = "b", cascade = CascadeType.PERSIST)
private List<A> aList;
}
Related
I want to create a Lazy One-to-one Bidirectional 'Optional' Mapping using Hibernate annotations. I know that the normal usage of #MappedBy and #JoinColumn result in N+1 queries being fired every time.
Is there a way I can avoid this? Not just at runtime, but at the POJO level. I am using Hibernate 4.3, so can't think about bytecode enhancement.
Further, if there is no way out, is it possible to apply criteria on unidirectional mappings. For example, I have A <-> B, and C -> A as mappings. And I am searching on B. Is it possible to apply a restriction on C when C is clearly unidirectional with A?
The #OneToOne annotaion doesn't work in hibernate as needed. Please consider the #LazyToOne or try using #OneToMany like #OneToOne. Also you can attempt #PrimaryKeyJoinColumn.
p.s. The #LazyToOne annotation doesn't exist in JPA realization, you should use #OneToOne(fetch = FetchType.LAZY, optional = false) there
I could not find a complete but minimal examples of LAZY bidirectional #OneToOne, so here it is. It is neither hibernate-version-dependent nor does it misuse #OneToMany.
Parent
Defines the id and is responsible for managing the consistency/synchronization, but technically does not own the relationship, because it can not reference any unique index in B (or at least we do not want to add redundant data).
#Entity
public class A {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToOne(
mappedBy = "a",
cascade = CascadeType.ALL,
orphanRemoval = true,
fetch = FetchType.LAZY
)
private B b;
public void setB(B b) {
if (b == null) {
if (this.b != null) {
this.b.setA(null);
}
} else {
b.setA(this);
}
this.b = b;
}
// ... other setters/getters
}
Child
Technically owns the relationship by re-using the id of parent A.
#Entity
public class B {
#Id
// Not generated but shared with A
private Long id;
#OneToOne(fetch = FetchType.LAZY)
#MapsId
#JoinColumn(name = "id") // otherwise use "a_id" instead of "id" in the DB column
private A a;
// ... other setters/getters
}
And this is how the tables should look like (assuming postgres):
CREATE TABLE a (
id bigint NOT NULL PRIMARY KEY,
);
CREATE TABLE b (
id bigint NOT NULL PRIMARY KEY,
FOREIGN KEY (id) REFERENCES a(id);
);
I have two entities A and B. A is parent of B, ie A is having a list of B as child.
The JPA classes corresponding look like below :
The first entity :
#Entity
#Table(name = "A")
public class A {
#OneToMany(cascade = CascadeType.ALL, mappedBy = "refA", orphanRemoval = true)
private List<B> listOfB;
}
The second entity :
public class B {
#JoinColumn(name = "A_REF")
#ManyToOne
private CoreRecord refA;
private int updatedColumn;
#PrePersist
private void prePersit() {
this.updatedColumn = 1;
}
#PreUpdate
private void preUpdate() {
this.updatedColumn = 2;
}
}
My issue now is that when i first persit A whith a list of B, everything goes fine. Each object of B is cascadly persisted and the updatedColumn if filled correcty.
Then juste after persisting A, if i add a new instance of B in the listOfB and call merge on A, the new instance of B will be persisted in the database but the value of updatedColumn not filled correctly.
When i debug the code, i can notice that the prePersit method is being called but the modification done to my updatedColumn field is never persisted in the database. I have also notice that the preUpdate method is never called.
I would like the modification done in one of the method prePersit or preUpdate to the updatedColumn of the new instance of B to be persisted in the database when i call merge on A.
I've been tourning around searching what i'm doing wrong. Since then i have'nt found the problem nor the solution. I need your help please. Thanks in advance.
My JPA provider is eclipselink 2.5.2
I have two entitys, A and B. A has a one to one relationship of B.
class A {
String aValue;
B b;
}
class B {
String bValue;
}
The class B stuff are already pre populated. Now, a user on a website selects what B he wants to submit. On the server we get an Id of what B that was.
The Question:
How do I create a new A, without actually do a DB query asking for the B that has this id. I mean, the A table in database only has a id reference. One should be able to set that Id without fetching the B.
If you are using JPA and Assuming the following:
#Entity
#Table(name="A")
class A {
#Id
#Column(name="id")
private int id;
String aValue;
#OneToOne
B b;
// Getters, setters and other stuff
}
#Entity
#Table(name="B")
class B {
#Id
#Column(name="id")
private int id;
Integer Id;
String bValue;
// Getters, setters and other stuff
}
If you now the id of entity B. You can simply make persist as follows:
B b = new B();
b.setId(1000); //Assuming that you know the id.
//There is no necessary fill all the object. Just the PK is needed.
A a = new A();
a.setId(100);
a.setAValue("nothing");
a.setB(b);
em.persist(a);
I have tested this with Hibernate as Persistence Provider. If you are using other ORM please specify it.
You should take a look at JPA 2.0.
With JPA you can define a relationship between 2 entites to automatically retrieve the related entities.
I have the following classes:
class A{
#OneToOne(cascade=CascadeType.ALL)
private B b;
}
class C{
#ManyToOne
private A a;
}
class B{
#OneToOne
private A a;
#MapKey(name = "name")
#OneToMany(cascade = CascadeType.ALL, ...)
#JoinColumn(...)
private Map<String C> cs;
}
How do I have to specify the mapping on B.cs to join where B.a == C.a?
Is this possible? Or do I have to change the property C.a to C.b? (I would prefer to keep it as it is, as the entity B is just a helper class.)
I also tried to change B to #Embeddable, but Map is not supported for embeddables.
JPA requires that all relationships be by Id (the foreign key references the primary key).
So, you need to either add a #ManyToOne from C to B.
Or, ensure that B's Id is the foreign key to A (add #Id on the #OneToOne from B to A and remove A's other #Id).
If B was a subclass of A instead of having a OneToOne this would also work.
If you are using EclipseLink, you can defined more complex criteria for a relationship. You would need to define the OneToMany's foreign keys using a DescriptorCustomizer and the OneToManyMapping API.
I think you can extends B from A.
If this doesn't work for you, maybe you can add a transient property to refer A's id,
#Transient
Integer getId1() {
return a.getId();
}
and join C using id1 instead of B's primary key.
Edit: This doesn't work.
I am attempting to persist/merge a brand new object graph through jpa but it seems like the order of persistance is incorrect as it tries to save sub objects who have a constraint on their parent being present.
public class ObjectA implements Serializable {
...
#OneToMany(cascade = CascadeType.ALL, mappedBy = "objectAId")
private List<ObjectB> objectBList;
...
}
and
public class ObjectB implements Serializable {
...
#JoinColumn(name = "OBJECT_A_ID", referencedColumnName = "ID", nullable = false)
#ManyToOne(optional = false)
private ObjectA objectAId;
...
}
I will create a new entity ObjectA and along with several new ObjectB entities and add them to Object A. When i merge ObjectA I get the following:
org.hibernate.PropertyValueException: not-null property references a null or transient value: com.mycompany.data.ObjectB.objectAId
What am I missing or doing wrong?
It's your responsibility to keep both sides of bidirectional relationship in consistent state for objects in memory. In other words, when you add ObjectB to ObjectA.objectBList, you also should make ObjectB.objectAId pointing to the corresponding ObjectA.
Moreover, without optional = false you would be able to persist objects without errors, but the relationship between them wouldn't be persisted if ObjectB.objectAId is null. It happens because Hibernate looks at the state of the owning side of relationship when saving it to the database, and in the case of bidirectional one-to-many relationship owning side is the "many" side (ObjectB)