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.
Related
This is not a duplicated issue, the others don't have the same scenario
My problem is similar to this comment: nullable = false not only create database constraint, but his solution disable all check nullability from hibernate, which I don't want, I want it specifically in this #ManyToOne relationship.
I have a Class RegraValidacao.java that can ocasionally have only one of its #ManyToOne fields setted, the others null. Those #ManyToOne relationships are made with a #JoinTable. What I want is: when generating my DDL via spring.jpa.properties.hibernate.hbm2ddl.auto, I want the #JoinColumns present inside the #JoinTable annotation to generate database constraint NOT NULL on my db2 database ONLY.
So, RegraValidacao can have #ManyToOne nullable relationships with the entities ContaLimite.java and Documento.java. It shall be OK to persist a RegraValidacao that only have a ContaLimite.java, like:
RegraValidacao rgr = new RegraValidacao();
rgr.setContaLimite(new ContaLimite());
rgr.setDocumento(null);
hibernate.save(rgr) // SHOULD WORK
But when I set nullable = false on my JoinColumn, it won't let the field Documento to be null.
#Entity
public class RegraValidacao {
#ManyToOne
#JoinTable(name = "CDR_CONTA_LIMITE_REGRA",
joinColumns = {#JoinColumn(name = "RGR_ID", nullable = false)},
inverseJoinColumns = {#JoinColumn(name = "CDL_ID", nullable = false)})
private ContaLimite contaLimite;
#ManyToOne
#JoinTable(name = "DOR_DOCUMENTO_REGRA",
joinColumns = {#JoinColumn(name = "RGR_ID", nullable = false)},
inverseJoinColumns = {#JoinColumn(name = "DOM_ID", nullable = false)})
private Documento documento;
}
EDIT: For simplification, I've just put those two attributes, but actually I have 8 pairs of Attributtes/JoinTables in this class. And why? Because this relationship is mutually excluded. If I have a common table relationship #ManyToOne without JoinTables, I would have 7 null fields on database and 1 set. With JoinTables I will not have any null fields, and if there's an id set in a JoinTable, certainly there's an entry on the tables related.
In other words, the relationship with ContaLimite.java and Documento.java should be: optional = true, but their columns on the #JoinTable should be nullable = false.
What I expect:
Tables generated via hbm2ddl.auto:
CDR_CONTA_LIMITE_REGRA --> RGR_ID NOT NULL, CDL_ID NOT NULL //OK
DOR_DOCUMENTO_REGRA --> RGR_ID NOT NULL, CDL_ID NOT NULL //OK
Persisting through Hibernate:
RegraValidacao rgr = new RegraValidacao();
rgr.setContaLimite(new ContaLimite());
rgr.setDocumento(null);
hibernate.save(rgr) // SHOULD BE OK!
What I get:
Tables generated via hbm2ddl.auto:
CDR_CONTA_LIMITE_REGRA --> RGR_ID NOT NULL, CDL_ID NOT NULL //OK
DOR_DOCUMENTO_REGRA --> RGR_ID NOT NULL, CDL_ID NOT NULL //OK
Persisting through Hibernate:
RegraValidacao rgr = new RegraValidacao();
rgr.setContaLimite(new ContaLimite());
rgr.setDocumento(null);
hibernate.save(rgr) --> Error, cannot persist null
ERROR: org.springframework.dao.DataIntegrityViolationException: not-null property references a null or transient value : br.system.regravalidacao.RegraValidacao.documento; nested exception is org.hibernate.PropertyValueException: not-null property references a null or transient value : br.system.regravalidacao.RegraValidacao.documento
This way I would expect that the columns of the JoinTable would have the NOT NULL constraint, but I can still persist a RegraValidacao with a null Documento.
You're using your join table for two attributes. I wouldn't recommend this.
If I understand your question correctly, you want either the Documento to be null or both the Documento or the ContaLimite to be set. So I would simply allow null for the Documento.
Well, I've found a solution that fits but it's not exactly what I wanted. Hope it helps someone someday.
I wanted to disable check null from Hibernate Runtime to a specific attribute of a class, this I couldn't achieve. I could just disable it for my entire class RegraValidacao.java
I created a Spring #Configuration class package scoped with a .properties file (inside my /resources directory) with spring.jpa.properties.hibernate.check_nullability=false on it, and #ComponentScan to my class package:
RegraValidacaoConfiguration.java:
#Configuration
#PropertySource("classpath:regravalidacao.properties")
#EntityScan(RegraValidacaoConfiguration.PACKAGE)
#ComponentScan({RegraValidacaoConfiguration.PACKAGE})
class RegraValidacaoConfiguration {
static final String PACKAGE = "br.system.regravalidacao";
}
RegraValidacao.java:
package br.system.regravalidacao;
public class RegraValidacao {
//...
}
regravalidacao.properties:
spring.jpa.properties.hibernate.check_nullability=false
This way, my whole project would still have the spring.jpa.properties.hibernate.check_nullability=true (default) and only my package br.system.regravalidacao will be false.
The only injury is my other attributes inside RegraValidacao (some of them are #OneToOne) will be wrongly affected by check_nullability = false, that's why I wanted something Attribute scoped and not Class scoped. Although this injury isn't that bad because I will still receive an error on those attributes, not from Hibernate Runtime, but from my database. It's pretty ok for me.
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 am using classes which are JPA annotated to map xml data into a database and the other way around via JAXB. Problem is that objects created by JAXB do not include foreign key fields and therefore are null. This conerns ownerId in example below.
Is there a way to fix this without looping through the whole tree again and add foreign keys ?
#Entity
public class Element {
#Id
String id;
#OneToMany(mappedBy = "owner", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<Property> properties;
}
#Entity
public class Property {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int propertyId;
#ManyToOne(optional = false, cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn(name = "ownerId", referencedColumnName = "id")
private Element owner;
}
The relationship is owned by the Property so the Property has to know related Element object to have it saved to DB. Adding a property to the list inside the element is not enough.
for (Property p : element.getProperties()) p.setOwner(element);
will solve the problem with the key.
If you read data from XML, typically your Property will not contained the referenced Element, only Element would have the list of Included properties.
You may also want to remove CascateType.ALL from the ManyToONe in Property, as removing a Property will trigere removing its parent Element and all the related Properties, which is probably not what you wanted).
But you may want to add orphanRemoval = true, to the OneToMany in Element, so if a property is removed from the list, it is also deleted in the DB.
What about adding ownerId property into your Property class like this?
#Column("ownerId", insertable=false, updatable="false")
private ownerId;
In this case you will have both ownerId and owner in your class. ownerId is read only and owner is used to assign/update values.
If you want to make it the opposite way around move insertable=false, updatable=false to owner property annotations.
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.
#Entity
public class Report extends Model {
public Date date;
public double availability;
#ElementCollection
#Cascade(value={CascadeType.ALL})
public Map<FaultCategory, Integer> categories;
}
In one of my jobs I have the following code:
int n = MonthlyReport.delete("date = ?", date);
This always fails to delete the entity with the following error:
The DELETE statement conflicted with the REFERENCE constraint "FK966F0D9A66DB1E54". The conflict occurred in database "TFADB", table "dbo.MonthlyReport_categories", column 'MonthlyReport_id'.
How can I specify the mapping so the elements from the categories collection get deleted when the report is deleted?
Cascading delete (and cascading operations in general) is effective only when operation is done via EntityManager. Not when delete is done as bulk delete via JP QL /HQL query. You cannot specify mapping that would chain removal to the elements in ElementCollection when removal is done via query.
ElementCollection annotation does not have cascade attribute, because operations are always cascaded. When you remove your entity via EntityManager.remove(), operation is cascaded to the ElementCollection.
You have to fetch all MonthlyReport entities you want to delete and call EntityManager.remove for each of them. Looks like instead of this in Play framework you can also call delete-method in entity.
The answer provided by J.T. is correct, but was incomplete for me and for sebge2 as pointed out in his/her comment.
The combination of #ElementCollection and #OnDelete further requires #JoinColumn().
Follow-up example:
#Entity
public class Report extends Model {
#Id
#Column(name = "report_id", columnDefinition = "BINARY(16)")
public UUID id; // Added for the sake of this entity having a primary key
public Date date;
public double availability;
#ElementCollection
#CollectionTable(name = "report_category", joinColumns = #JoinColumn(name = "report_id")) // choose the name of the DB table storing the Map<>
#MapKeyColumn(name = "fault_category_key") // choose the name of the DB column used to store the Map<> key
#Column(name = "fault_category_value") // choose the name of the DB column used to store the Map<> value
#JoinColumn(name = "report_id") // name of the #Id column of this entity
#OnDelete(action = OnDeleteAction.CASCADE)
#Cascade(value={CascadeType.ALL})
public Map<FaultCategory, Integer> categories;
}
This setup will create a table called report and another table report_category with three columns: report_id, fault_category_key, fault_category_value. The foreign key constraint between report_category.report_id and report.report_id will be ON DELETE CASCADE. I tested this setup with Map<String, String>.
We found the magic ticket! Add OnDelete(action= OnDeleteAction.CASCADE) to the ElementCollection. This allows us to remove the item from SQL (outside of the entityManager).
I met the same problem, and here is my code sample.
#ElementCollection
#CollectionTable(name = "table_tag", joinColumns=#JoinColumn(name = "data_id"))
#MapKeyColumn(name = "name")
#Column(name = "content")
private Map<String, String> tags
After a lot of tries, finally, I just add foreign key constraint for the table_tag.data_id to the parent table's primary key. Notice that you should set ON DELETE CASCADE to the constraint.
You can delete the parent entity by any ways, and the child element collection would be deleted too.
As an alternative to the hibernate-specific annotation #org.hibernate.annotations.OnDelete, you can also provide the constraint via #javax.persistence.ForeignKey to customize automatic schema generation:
#CollectionTable(name = "foo_bar", foreignKey = #ForeignKey(
name = "fk_foo_bar",
foreignKeyDefinition = "foreign key (foo_id) references Foo (id) on delete cascade"))
private List<String> bar;