I have a problem with the auto update of foreign keys which appears as following:
I have a two tables HamKeyword and HamKeywordAlias. One entry in the hamKeyword has 0…n entries in HamKeywordAlias. This relationship is reflected with a foreign key field in the HamKeywordAlias table. Both tables have their own primary keys. I defined the two tables using reverse engineering of hibernate eclipse tools as follows:
#Entity
#Table(name = "HAM_KEYWORDS")
public class HamKeywords implements java.io.Serializable {
private long keywordid;
private String keyword;
…
#Id
#GenericGenerator(name="gen",strategy="increment")
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "KEYWORDID", unique = true)
public long getKeywordid() {
return this.keywordid;
}
…
#OneToMany(fetch = FetchType.EAGER, cascade=CascadeType.ALL, mappedBy = "hamKeywords")
public Set<HamKeywordsAlias> getHamKeywordsAliases() {
return this.hamKeywordsAliases;
}
#Entity
#Table(name = "HAM_KEYWORDS_ALIAS", schema = "dbo", catalog = "ham")
public class HamKeywordsAlias implements java.io.Serializable {
#Id
#GenericGenerator(name="gen",strategy="increment")
#GeneratedValue(generator="gen")
#Column(name = "ALIASID", unique = true, nullable = false)
public long getAliasid() {
return this.aliasid;
}
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "KEYWORDID", nullable = false, updatable = false, insertable = true)
public HamKeywords getHamKeywords() {
return this.hamKeywords;
}
Now to my problem. I try to add a new entry to HamKeyword with 1 new related HamKeywordAlias:
HamKeywords hkw = new HamKeywords();
HamKeywordsAlias hka = new HamKeywordsAlias();
hka.setAlias("new alias");
hkw.setHamKeywordsAliases(new HashSet<HamKeywordsAlias>());
Set<HamKeywordsAlias> hkaS = hkw.getHamKeywordsAliases();
hkaS.add(hka);
hkw.setHamKeywordsAliases(hkaS);
session.flush();
session.save(hkw);
session.getTransaction().commit();
This code fails with the error message:
ERROR: The value NULL can not be inserted in table 'KEYWORDID'-Spalte, 'ham.dbo.HAM_KEYWORDS'. No NULL allowed for INSERT. Exception in thread "main" org.hibernate.exception.ConstraintViolationException: could not execute statement
(Please note that I translated the error message into english, it might be a bit different languagewise)
Obviously, the foreign key in field KEYWORDID of the HamKeywordAlias table is not be updated. I double checked this by removing the NOT NULL constraint. What happens is, that the enty into the ALIAS table is inserted but with a NULL in the field keywordid.
I tested furthermore adding manually rows into the HamKeywordAlias table. Retrieving an entry of the HamKeyword table and retrieving the related Aliases with following code works great:
HamKeywords hamCurrentKeyword = (HamKeywords) session.get(HamKeywords.class, (long)1);
hamCurrentKeyword.getHamKeywordsAliases();
Thus I assume that I defined the many to one relation correctly. However, the foreign key is not updated automatically.
Can you assist me why this is not be done?
Thanks
Felix
You have a bidirectional OneToMany association. The owner of the association is the Many side: HamKeywordsAlias.hamKeywords. That's the side that Hibernate cares about. But you didn't initialize it. You added an alias to the keywords' collection of aliases, but failed to set the keywords of the alias:
hka.setHamKeywords(hkw);
Related
I'm trying to achieve to have an entity called MyEntity along with another entity called MyEntityInfo using Hibernate 5.3.13.Final with annotations under Wildfly 18.
The idea is to have MyEntity store some commonly requested fields, and MyEntityInfo store some rarely requested fields. Both share the same primary key called SID (Long), and there is a FK from Info's SID to Entity's SID. There can be entities without info.
Normally you will not require the additional info. For example, I don't want the info entity to be fetched when I query my entity like this:
MyEntityImpl entity = em.find(MyEntityImpl.class, 1L);
However, when I run this code, I find that there's a second query, fetching the Info entity along the main one, as in an EAGER behaviour.
I'm mapping the relationship using #OneToOne. I've tried several combinations of FetchType, optional and #LazyToOne, but so far without success.
Here is the code for both MyEntity and MyEntityInfo classes (additional getters and setters removed):
MyEntity (ID generator is a custom sequence generator):
#Entity
#Table(name = MyEntityImpl.TABLE_NAME)
public class MyEntityImpl {
public static final String TABLE_NAME = "TMP_MY_ENTITY";
#Id
#GeneratedValue(strategy = GenerationType.TABLE, generator = "GEN_" +
TABLE_NAME)
#GenericGenerator(name = "GEN_" +
TABLE_NAME, strategy = CoreIdGenerator.ID_GENERATOR, parameters = {
#Parameter(name = "tableName", value = TABLE_NAME) })
#Column(name = "sid", nullable = false, unique = true)
private Long sid;
#OneToOne(mappedBy = "myEntity", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = true)
#LazyToOne(LazyToOneOption.NO_PROXY)
private MyEntityInfoImpl info;
#Column
private String field;
MyEntityInfo:
#Entity
#Table(name = MyEntityInfoImpl.TABLE_NAME)
public class MyEntityInfoImpl {
public static final String TABLE_NAME = "TMP_MY_ENTITY_INFO";
#Id
#Column(name = "SID", nullable = false, unique = true)
private Long sid;
#OneToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "SID", referencedColumnName = "SID", insertable = false, updatable = false, nullable = false)
private MyEntityImpl myEntity;
#Column(name = "INFO_FIELD")
private String infoField;
I've tried this solution, but as I said, it didn't work for me:
Hibernate lazy loading for reverse one to one workaround - how does this work?
I've managed to do something somewhat similar using #OneToMany and managing data manually, but that's not what I'd like to do. However, another alternatives and information on whether this can be achieved or not using #OneToOne, or the right design pattern to do this are also welcome.
PS: Database tables creation for SQL Server, in case you want to try it:
create table TMP_MY_ENTITY (SID NUMERIC(19,0) NOT NULL, FIELD VARCHAR(100));
go
ALTER TABLE TMP_MY_ENTITY ADD CONSTRAINT PK_TMP_MY_ENTITY PRIMARY KEY CLUSTERED (SID);
go
create table TMP_MY_ENTITY_INFO (SID NUMERIC(19,0) NOT NULL, INFO_FIELD VARCHAR(100));
go
ALTER TABLE TMP_MY_ENTITY_INFO ADD CONSTRAINT PK_TMP_MY_ENTITY_INFO PRIMARY KEY CLUSTERED (SID);
go
CREATE SEQUENCE SEQ_TMP_MY_ENTITY START WITH 1 INCREMENT BY 1 MINVALUE 1 CACHE 20;
alter table TMP_MY_ENTITY_INFO add constraint FK_TMP_MY_ENT_INFO_MY_ENT FOREIGN KEY (SID) references TMP_MY_ENTITY(SID);
go
insert into TMP_MY_ENTITY(SID, FIELD) VALUES (NEXT VALUE FOR SEQ_TMP_MY_ENTITY, 'Field 1');
insert into TMP_MY_ENTITY_INFO(SID, INFO_FIELD) VALUES ((SELECT MAX(SID) FROM TMP_MY_ENTITY), 'Info 1');
insert into TMP_MY_ENTITY(SID, FIELD) VALUES (NEXT VALUE FOR SEQ_TMP_MY_ENTITY, 'Field 2');
insert into TMP_MY_ENTITY_INFO(SID, INFO_FIELD) VALUES ((SELECT MAX(SID) FROM TMP_MY_ENTITY), 'Info 2');
insert into TMP_MY_ENTITY(SID, FIELD) VALUES (NEXT VALUE FOR SEQ_TMP_MY_ENTITY, 'Field 3 no info');
-- DELETE ALL
drop table TMP_MY_ENTITY_INFO;
drop table TMP_MY_ENTITY;
drop sequence SEQ_TMP_MY_ENTITY;
After following #SternK link, and upgrading to Wildfly 19 and Hibernate 5.4.14, it finally worked by using #MapsId.
The right mapping to use is this:
MyEntity:
public class MyEntityImpl {
#OneToOne(mappedBy = "myEntity", cascade = CascadeType.REMOVE, fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "SID")
private MyEntityInfoImpl info;
MyEntityInfo:
public class MyEntityInfoImpl {
#OneToOne(fetch = FetchType.EAGER, optional = false)
#MapsId
#JoinColumn(name = "SID", referencedColumnName = "SID", insertable = false, updatable = false, nullable = false)
private MyEntityImpl myEntity;
I'm getting this error when I try to persist an A class Object:
Detail: Key (classB)=() is not present in table "b".
I need to have the possibility to insert the object with null on the referenced column.
The problem is hibernate convert the null value on an empty string, so when I try to persist the object, it fails.
If I put cascade = CascadeType.ALL on #ManyToOne works, but it creates a row on B table with ID = 0 and an empty string as refColName value. I want to avoid this because de A class is the child, and the cascade should be in B class.
#Entity
#Table
public class A {
...
#ManyToOne
#JoinColumn(name = "class_b", referencedColumnName = "refColName", nullable = true)
private B classB;
...
}
#Entity
#Table
public class B {
...
#Id
#Column (name = "id")
private long id;
#Column(name = "refColName")
private String refColName;
...
}
Any suggestion?
Thanks for your time
Edit:
It's a unidirectional relationship, where B is a master data table, so i have predefined values. refColName should be a String. I use referencedColumnName because I can´t take the id as a foreign key.
I need to have the possibility to insert the object with null on the
referenced column.
Please make sure the classA input to persist should be like below(representing in json)
{
classB:null
}
and not
{
classB:{
refColName:null
}
}
i have ORA-02291 while creating new object (entitymanager.persist(taskVisit))
#Entity(name = "CRM_TASKDEPARTURE")
#Access(AccessType.PROPERTY)
#DiscriminatorValue(value = TaskType.Consts.VISIT_ID)
public class TaskVisit extends Task {
private static final long serialVersionUID = 1L;
private List<TaskVisitAddress> addresses = new ArrayList();
public TaskVisit() { }
#OneToMany(fetch = FetchType.LAZY,cascade = CascadeType.PERSIST,mappedBy = "taskVisit")
public List<TaskVisitAddress> getAddresses() {
return addresses;
}
}
Connstraint is CRM_TaskVisitAddress(TASKID)
Code of entity:
#Entity(name = "CRM_TaskDepartureAddress")
public class TaskVisitAddress implements Serializable {
...any fields
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "TASKID")
public TaskVisit getTaskVisit() {
return taskVisit;
}
#Id
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "PERSONADDRESSID")
public PersonAddress getPersonAddress() {
return personAddress;
}
}
Exception:
Error Code: 2291
Call: INSERT INTO CRM_TASKDEPARTUREADDRESS (TASKID, PERSONADDRESSID) VALUES (?, ?)
bind => [3299, 1]
Caused by: java.sql.SQLIntegrityConstraintViolationException: ORA-02291: integrity constraint (DUMMY.FK_CRM_TASKDEPARTUREADDR_TASKI) violated - parent key not found
Where is the mistake?
P.s. in test object saved without exception...
Updated
I found wrong INSERT generated sequence
1. insert into superclass Task (right)
2. insert into CRM_TaskDepartureAddress (wrong)
3. insert into CRM_TASKDEPARTURE (wrong)
№ 2 and 3 must be swaped, becouse CRM_TaskDepartureAddress referenced to CRM_TASKDEPARTURE .
Updated
InheritanceType.JOINED
You cannot have a ManyToOne as your primary key; it is stating that there are many TaskVisitAddress instances referencing the same TaskVisit, while your #Id requires something that is unique.
You need to find something on your TaskVisitAddress that will uniquely identify it from other TaskVisitAddress instances, such as Integer id assigned through sequencing.
The #JoinColumn(name = "TASKID") is referring to "TASKID" in the Task table, as JPA only allows relationships to reference an Entity's primary key, making the constraint requiring CRM_TASKDEPARTURE to be inserted first is incorrect. If you must keep the constraint and require CRM_TASKDEPARTURE to be inserted first, you can try specifying the table name in the joinColumn:
#JoinColumn(name = "TASKID", referencedColumnName = "CRM_TASKDEPARTURE.TASKID")
as described in feature request
https://bugs.eclipse.org/bugs/show_bug.cgi?id=333100
I have 2 entities that one of them is the foreign key of the other:
#Entity
#Table(name = "XXXX")
#XmlRootElement
public class Drfacopt implements Serializable {
#Id
#NotNull
#Basic(optional = false)
#Column(name = "OPCOD")
private Short optionCode;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 50)
#Column(name = "OPCODDH")
private String optionCodeDescription;
}
#Entity
#Table(name = "YYYY")
#XmlRootElement
public class Drfac03f implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
protected Drfac03fPK drfac03fPK;
#JoinColumn(name = "F3OPT", referencedColumnName = "OPCOD" )
#ManyToOne(optional = false)
private Drfacopt relationCode;
}
when I'm trying to insert a new record using em.persist(drfac03f) I get this exception:
During synchronization a new object was found through a relationship that was not marked cascade PERSIST
How can I insert the foreign key as null? I want to have the #Null for so when the user inserts a record directly to Drfacopt it wont be null or zero, but if it's through Drfac03f then it can be inserted as null. how can this be done?
When you try em.persist(myObj) and get the error:
During synchronization a new object was found through a relationship that was not marked cascade PERSIST
It means the object (myObj) you are trying to persist has a field/property (say foreignProperty) that has a [foreign key] relationship with myObj and neither foreignProperty was persisted before myObj, nor the relationship was marked with CascadeType.PERSIST, that is, foreignProperty does not exist in the database yet.
The solution is to persist the foreignProperty before or, as said, set the relationship CascadeType.PERSIST.
How can I insert the foreign key as null?
Since you want to set the foreign field as null, you shouldn't persist it before (there's no need to "persist" a null object).
So, just simply set the field as null and proceed to persist myObj as usual.
I tried to persist, but got the error "Null values not allowed in column or variable ..."
This error message states that you are trying to persist a null value somewhere you couldn't. There are some possible reasons:
Most probably the actual column in the database is NOT NULL. Solution: Set it to NULL.
The relationship is marked as (optional = false). The solution would be to set that optional attribute as true or remove it altogether. (Note: this is possibly implementation dependent so it requires confirmation.)
I'm working on a hibernate entity mapping for a database view; when I do a criteria query against it, hibernate is generating bad SQL. Any help figuring out what the problem is with my mapping would be greatly appreciated!
I have two mapped entities which I am trying to grab from a database view; the view has no other columns, just the FK of each entity. One of these FK's can be treated as a primary key, since the view has a row for each primary entity. So my DB schema for the view looks like:
primary(primary_id, some_other_fields)
history(history_id, primary_id, some_other_fields)
view_latest_status_history(primary_id, history_id)
Note the view is used because I want to pick out only the latest history for each primary, not all mapped history records. Here is the object I am using for the view, with entity annotations:
#Entity
#org.hibernate.annotations.Entity(dynamicUpdate = true)
#Table(name = "view_latest_status_history")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class ViewLatestStatusHistoryRow implements Serializable {
private Primary primary;
private History history;
/**
* #return Returns the history.
*/
#ManyToOne(cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REMOVE }, fetch = FetchType.LAZY)
#JoinColumn(name = "history_id", nullable = true)
#AccessType("field")
public History getHistory() {
return history;
}
//equals() and hashCode() implementations are omitted
/**
* #return Returns the primary.
*/
#Id
#ManyToOne(cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REMOVE }, fetch = FetchType.LAZY)
#JoinColumn(name = "primary_id", nullable = false)
#AccessType("field")
public Primary getPrimary() {
return primary;
}
}
Both the Primary and History objects have complete, working entity annotations.
My criteria setup:
criteria.add(Restrictions.in("primary", [collection of primary objects]));
criteria.setFetchMode("primary", FetchMode.JOIN);
criteria.setFetchMode("history", FetchMode.JOIN);
And the (wrong) generated SQL:
select this_.primary as primary78_1_, this_.primary_id as prim2_78_1_, primary2_.history_id as unique1_56_0_, ...history fields
from DB_CATALOG.dbo.view_latest_status_history this_
left outer join DB_CATALOG.dbo.history primary2_ on this_.primary_id=primary2_.primary_id
where this_.specChange in (?, ?...)
I might've mucked up a few things when editing out the specifics of our project's DB schema, but the point is the first field in the 'select' clause is wrong:
this_.primary (view_latest_status_history.primary) is not a field; the field should be called primary_id. I think this may have something to do with the #Id annotation on the primary field? Any ideas how to fix this? If I remove the #Id, I get an error telling me that the entity has no primary key.
Update:
I no longer map the view as a field using a join table notation (as suggested below). The annotations have been revised, as follows. This solution works correctly in HQL, and generates the expected schema when hbm2ddl is enabled, but I have not re-tested it using the criteria query.
#Entity
#Table(name = "view_latest_status_history")
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class ViewLatestStatusHistoryRow implements Serializable {
private String id;
private Primary primary;
private History history;
/**
* #return Returns the history.
*/
#OneToOne(optional = true)
#JoinColumn(name = "history_id", nullable = true)
#AccessType("field")
public History getHistory() {
return history;
}
//equals() and hashCode() implementations are omitted
#Id
#Column(name = "primary_id", nullable = false)
#Override
#AccessType(value = "field")
public String getId() {
return id;
}
/**
* #return Returns the primary.
*/
#PrimaryKeyJoinColumn(name = "primary_id", referencedColumnName = "unique_id")
#OneToOne(optional = false)
#AccessType("field")
public Primary getPrimary() {
return primary;
}
}
It most certainly is due to #Id annotation - primary_id is NOT a primary key in this case. Nor can you realistically have #Id and #ManyToOne on the same property.
Let me ask you this - why are you mapping ViewLatestStatusHistoryRow as an entity to begin with? It's not like you ever going to persist it. Consider mapping your latest history entry directly (as read-only) on primary (as many-to-one) and using your view as join table.