I have a fairly straightforward one-to-many relationship
[SampleAliasMask] has many [SampleAliasMaskPart]
My problem is that when I persist a new instance of SampleAliasMask with collection parts I get an constraint violation that the foreign key link from the tables of SampleAliasMaskPart to SampleAliasMask is being set to NULL.
I am mapping using hibernate annotations as such:
#Entity
#Table(name="SAMPLE_ALIAS_MASK")
public class SampleAliasMask extends ClientEntity {
#OneToMany(mappedBy = "sampleAliasMask", fetch = FetchType.EAGER, cascade = javax.persistence.CascadeType.ALL, orphanRemoval = true)
#Cascade(CascadeType.ALL)
#Length(min = 1, message = "The sample alias mask must have components")
private Set<SampleAliasMaskPart> components;
With the other half of the relationship mapped as so:
#Entity
#Table(name="SAMPLE_ALIAS_MASK_PART")
public class SampleAliasMaskPart extends ClientEntity {
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "SAMPLE_ALIAS_MASK_ID", nullable = false)
private SampleAliasMask sampleAliasMask;
The relevant part of ClientEntity is
#MappedSuperclass
public abstract class ClientEntity {
#Id
#Column(name="ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
I am creating the parts like this:
HashSet<SampleAliasMaskPart> components = new HashSet<>();
for(Map<String, Object> c : this.components) {
SampleAliasMaskPart component = new SampleAliasMaskPart(Integer.parseInt(c.get("value").toString(), 10), c.get("name").toString());
result.validate(component);
components.add(component);
}
mask.setComponents(components);
The exact error I get is:
java.sql.BatchUpdateException: ORA-01400: cannot insert NULL into ("ST"."SAMPLE_ALIAS_MASK_PART"."SAMPLE_ALIAS_MASK_ID")
I suspect the issue has to do with the fact that I never explicitly set SampleAliasMaskPart.sampleAliasMask but why do I need to? That relationship is never exposed nor navigated. That field is only there for mapping purposes which makes me think that I'm mapping this wrong.
Your assumption is correct. Hibernate uses the owning side of an association to know is the association exists or not. And the owning side ai the side where there is no mappedBy attribute.
The general rule is that when you have a bidirectional association, it's your responsibility to make the object graph coherent by initializing/modifying both sides of the association. Hibernate doesn't care much about it, but if you don't initialize the owning side, it won't persist the association.
Note that you're not forced to make this association bidirectional. If you don't, then adding the part to the mask will be sufficient, because this side (which is the unique side) is the owning side.
JB Nizet suggested correctly. There are two ways you can solve it:
Removing the bi-directional relationship:Remove the annotation #ManyToOne(optional = false, fetch = FetchType.LAZY) from the simpleAliasMask in SampleAliasMaskPart
Add the mask to each component by doing something like component.setSimpleAliasMask(mask). This will do the bidirectional relationship.
Related
I have an entity, leaning on which hibernate able to generate join table for OneToMany-relations.
#Entity
public class RequestType extends EntityObject {
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name = "MType_MType", joinColumns = #JoinColumn(name = "mtype_id"),
inverseJoinColumns = #JoinColumn(name = "inner_request_types_id"))
private List<LogicalInnerType> innerRequestTypes;
}
#Entity
public class EntityObject {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "EntityID")
private Integer id;
}
But when I run application, I receive the following exception:
PSQLException: EROOR: null value in column "inner_request_types_id" violate constraint NOT NULL
As I know, any JoinColumn has a default true of nullable-parameter.
How could I otherwise drop notnull-constraint?
Yes, the default value of JoinColumn.nullable() is true:
/** (Optional) Whether the foreign key column is nullable. */
boolean nullable() default true;
But this is not relevant here, because nullable() is not inspected for #JoinColumn in a #OneToMany. Hibernate will always add a not null constraint to both columns, because it expects that there is always an owning entity and that the list never contains null values.
If you wanted to add null values to your list in the first place (and this is not a mistake), you should think about an alternative. For example you could add a boolean attribute to RequestType that indicates the state that you wanted to achieve with the null value.
By the way there is another thing, that is apparently not correct in your model. You are using a List, but you add no #OrderColumn. As a result Hibernate has no possibility to ensure any order. If you really want to store a specific order, you should add an #OrderColumn annotation. Or you use a Set instead.
I am trying to join to Hibernate Entities in a OneToOne Mapping. I am able to fetch the data for a given primary key from the Main Entity, the joining entity, however, returns null. I am new to hibernate and any help will be appreciated.
I have two Tables,
PT_CORE
Primary Key: ptId - Integer;
Foreign Key: stId(ST_AUX) - Integer;
Columns: ptId, ptName
ST_AUX
Primary Key: stId;
Columns: stId, stName
The two tables get populated by other applications and mine is a read-only operation.
Below is my first Entity class(PtCore.java)
#Entity
#Table(name="PT_CORE")
public class PtCore implements Serializable{
#Id
#Column(name="ptId", nullable = false)
private int id;
#Column(nullable=false)
private int stId; //The Foreign key column
#OneToOne
#JoinTable( name = "core_aux", joinColumns = {#JoinColumn(Name="ptId")},
inverseJoinColumns = {#JoinColumn(Name="stId")}
)
private StAux staux;
//Getters, setters and toString() for above
}
StAux is another Entity, defined as below,
#Entity
#Table(name="ST_AUX")
public class StAux implements Serializable {
#Id
#Column(nullable=false)
private Integer stId;
#OneToOne
private PtCore ptcore;
#Column
private String stName;
//Getters, Setters and toString follow.
}
I do below in the Service method:
PtCore obj = (PtCore) session.get(PtCore.class,1);
System.out.println(obj);
In the Results, I get the value of ptName, but the stAux class variables are null, Indicating that the join does not work as expected.
First of all you have the mapping information existing in your PT_CORE. And I assume it is something like FOREIGN KEY (stid) REFERENCES (stid). If you want to use existing schema and existing data I guess there is no mapping table core_aux really existing. At least you did not mention it. However it is visible as #JoinTable annotation but still there is this above mentioned foreign key which seems to be the real mapping (so again not the join table).
I suggest the following
remove this
#Column(nullable=false)
private int stId; //The Foreign key column
from your PtCore. I think it is not needed. Also in PtCore, remove the #JoinTable (because what I told above) and add mapping informaiion to #OneToOne annotation, like:
#OneToOne
#JoinColumn(name = "stid")
private StAux staux;
from your PT_CORE.
Then in StAux alter also a bit:
#Id
#Column(name = "stid") // this might not be needed but if there is like "st_id"...
private Integer stId; // so just for sure
#OneToOne(mappedBy = "staux")
private PtCore ptcore;
Because you have existing tables and constraints there might raise errors if hibernate tries to auto-generate those again by JPA instructions.
Check this for example for more information.
UPDATE: just realized also that in your title is #OneToMany but in your code is #OneToOne.
So you might want to elaborate your question and/or title a bit.
In your relation, the owning side is PtCore, the inverse side is StAux.
In bidirectional OneToOne relations, the inverse side has to have the mappedBy attribute. Actually, the mappedBy attribute contains the name of the association-field on the owning side.
So, you must change your inverse side code (StAux Entity). You have to add mappedBy attribute to #OneToOne in StAux class:
#OneToOne(mappedBy="staux")
private PtCore ptcore;
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);
);
Imagine having following simplified code using Hibernate and JPA:
#Entity
class C {
#Id #GeneratedValue public long id;
}
#MappedSuperclass
abstract class A {
#Id #GeneratedValue public long id;
#OneToMany(cascade = CascadeType.ALL)
public List<C> list1;
#OneToMany(cascade = CascadeType.ALL)
public List<C> list2;
}
#Entity
class B extends A { }
Using a PostgreSQL database, this leads to a table b_c with columns
b_id | list1_id | list2_id
Now, trying to persist any B leads to following exception:
org.postgresql.util.PSQLException: FEHLER: NULL-Wert in Spalte „list2_id“ verletzt Not-Null-Constraint
Detail: Fehlgeschlagene Zeile enthält (779, 827, null).
Which roughly translates to
NULL-Value in column "list2_id" violates Non-Null-Constraint. Contents: (770, 827, null)
Why does this happen and how can I avoid it?
Merging the lists into one is not an option. Using Sets instead does not change anything.
This is happening because of the unidirectional #OneToMany association. You need to first determine if your association is unidirectional or bidirectional.
If your association is unidirectional then you need to use #JoinColumn to fix the extra join table problem. If your association is bidirectional, then you will rely on the #ManyToOne side to propagate all entity state changes, with the use of mappedBy on the #OneToMany side. You can find good explanation about different ways of #OneToMany association here.
I am using JPA (Hibernate) with the following entity class with one one-to-many relationship.
When I add elements to the list, and then persist the Organization entity, it adds the new elements to the proyects table, but when I remove elements from the list, nothing happens when persist (or merge), and I would like these elements to be removed from the database.
I tried also orphanRemoval=true in the OneToMany annotation, but it doesn't work.
#Entity
public class Organization {
#Id
#GeneratedValue
public long internalId;
#Basic
#Column(nullable = false, length = 100)
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "organization")
private List<Proyect> proyects;
// Getters and Setters
}
You need to set Proyect.organization to null and update that entity, since this property is responsible for the database entry (Proyect is the owning side in this case ).