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
}
}
Related
I have generated master tables using liquibase. I have created the corresponding models in spring boot now I want to maintain a relation ship between those models.
I have one table called Vehicle_Type, it is already pre-populated using liquibase.
#Data
#Entity
#Table(name="VEHCILE_TYPE")
public class VehicleType {
#Id
private int id;
#Column(name="DISPLAY_NAME")
private String displayName;
#Column(name="TYPE")
private String type;
#Column(name="CREATED_DATE")
private LocalDateTime createdDate;
#Column(name="UPDATED_DATE")
private LocalDateTime updateDate;
}
now what I want to achieve is, I have one child entity, I have refer the VehicleType instance inside that entity as depicted below
#Data
#Entity
#EqualsAndHashCode(callSuper = true)
#Table(name = "NON_MSIL_VEHICLE_LAYOUT")
public class NonMsilVehicleLayout extends BaseImagesAndLayout {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "NMV_SEQ")
#SequenceGenerator(sequenceName = "NON_MSIL_VEH_SEQUENCE", allocationSize = 1, name = "NMV_SEQ")
private int id;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "VEH_TYPE", referencedColumnName = "id")
private VehicleType vehicleType;
public interface VehType {
String getVehType();
}
}
The problem is when I tries to save entity NonMsilVehicleLayout, then it tries to first insert the data in VEHICLE_TYPE table also. which should not going to be happen.
I don't want that, I want JPA will pick the correct ID from VEHICLE_TYPE table and place it inside the corresponding table for NonMsilVehicleLayout, because the id of VEHICLE_TYPE table is act as foreign key in Non_Msil_Vehicle_Layout table.
log.info("Inside saveLayout::Start preparing entity to persist");
String resourceUri = null;
NonMsilVehicleLayout vehicleLayout = new NonMsilVehicleLayout();
VehicleType vehicleType=new VehicleType();
vehicleType.setType(modelCode);
vehicleLayout.setVehicleType(modelCode);
vehicleLayout.setFileName(FilenameUtils.removeExtension(FilenameUtils.getName(object.key())));
vehicleLayout.setS3BucketKey(object.key());
I know I missed something, but unable to figure it out.
You are creating a new VehicleType instance setting only the type field and set the vehicleType field of NonMsilVehicleLayout to that new instance. Since you specified CascadeType.ALL on NonMsilVehicleLayout#vehicleType, this means to Hibernate, that it has to persist the given VehicleType, because the instance has no primary key set.
I guess what you rather want is this code:
vehicleLayout.setVehicleType(
entitManager.createQuery("from VehicleType vt where vt.type = :type", VehicleType.class)
.setParameter("type", typeCode)
.getSingleResult()
);
This will load the VehicleType object by type and set that object on NonMsilVehicleLayout#vehicleType, which will then cause the foreign key column to be properly set to the primary key value.
Finally, after some workaround, I got the mistake, the column name attribute was incorrect, so I made it correct and remove the referencedColumn and Cascading.
Incorrect:
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "VEH_TYPE", referencedColumnName = "id")
private VehicleType vehicleType;
Correct:
#OneToOne
#JoinColumn(name = "VEHICLE_TYPE")
private VehicleType vehicleTypes;
also I have added the annotation #Column in the referende entity VehicleImage
public class VehicleType {
#Id
#Column(name = "ID") // added this one
private int id;
}
That bit workaround solved my problem, now I have achieved what I exactly looking for.
I have A.class:
#Entity
#Data
public class A {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
Integer id;
String someStringInAclass;
#OneToMany(cascade = CascadeType.ALL)
List<SomeObject> someObjectsList;
}
I have SomeObject.class:
#Entity
#Data
public class SomeObject {
#GeneratedValue(strategy = GenerationType.AUTO)
#Id
Integer id;
String someString;
Integer someInteger;
}
I save A object in database:
SomeRepository.save(A);
And tables look like this:
Then I save A object again with different values in B object:
SomeRepository.save(A);
Then tables look like this:
Table B has extended correctly, but table A_B just overrode the B_id instead putting the new one 4 rows.
Then when I retrieve the object A from database I obtain only last 4 values from B object.
How to make it work with saving new values (adding new rows to A_B) without overriding?
EDIT:
I added A_id as a foreign key to B table. Again - first save is stored properly:
Then after second save with different values for B, it modifies values for A_id column for firstable inserted 4 values (to null). So the table looks like this:
Hibernate handle unidirectional relationships very inefficiently please try to use bidirectional relationship.
Regarding above issue, you don't need third table here, you can avoid creation of third table by adding #JoinColumn as below :
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "fk_a_id")
List<SomeObject> someObjectsList;
this will add column "fk_a_id" in SOME_OBJECT table.
Since the object is already saved and has an assigned id, JPA assumes you mean the same entry in the database. You need to clone the object:
#Entity
#Data
public class A {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
Integer id;
String someStringInAclass;
#OneToMany(cascade = CascadeType.ALL)
List<SomeObject> someObjectsList;
public A(A source) {
this.someStringInAclass = source.someStringInAClass;
this.someObjectsList = new ArrayList<SomeObject>();
for (SomeObject o : source.someObjectsList) {
this.someObjectsList.add(new SomeObject(o));
}
}
}
Use it as follows:
SomeRepository.save(a);
A aClone = new A(a);
SomeRepository.save(aClone);
I added: (nullable = false) parameter for #JoinColumn the problem is solved.
So above the list there is now:
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(nullable = false)
List<SomeObject> someObjectsList;
I have some code that deletes a row from a table using the following code:
EntityManager em = getEntityManager();
try
{
final EntityTransaction transaction = em.getTransaction();
transaction.begin();
em.remove(data);
transaction.commit();
} catch (final PersistenceException e) {
{
throw new CPDPersistenceException(e);
}
The error message is: Cannot delete or update a parent row: a foreign key constraint fails.....etc.
The problem is that a foreign key for other tables "reference_id" exists in the table that I am trying to delete. However, wherever this primary key exists in the persistent Java object where it is defined, it has a reference that should cause cascading deletion. For example:
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="reference_id")
private Reference reference;
My understanding, from reading other entries on this subject, is that the "cascade" phrase attached to reference would fix the problem of deleting one entry that is related to other entries in other tables. Does anybody have any ideas?
What I understand from your code, you shouldn't set cascade property of #ManyToOne annotation for the owning entity's field of the referenced entity. On the contrary, you must set the cascade property of #OneToMany annotation in the parent entity for the child entity field. For example:
class ParentEntity {
#OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private ChildEntity child;
...
}
class ChildEntity {
#ManyToOne
private ParentEntity parent;
...
}
You are using unidirectional hibernate relationship. And because you are using cascade property for the reference field of both Uuid and Attachment entities, just manipulations on these two entities affect Reference entity, not vice versa.
I recommend using bidirectional relationship and set the cascade property of the #OneToMany annotation for your both uuid and attachment fields in your Reference entity to get the desired result, as follows:
public class Reference implements Serializable {
#Id
#Column(name = "reference_id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int referenceID;
#OneToMany(mappedBy = "reference", cascade=CascadeType.ALL)
private Attachment attachment;
#OneToMany(mappedBy = "reference", cascade=CascadeType.ALL)
private Uuid uuid;
MORE STUFF
}
public class Attachment implements Serializable {
#ManyToOne
#JoinColumn(name = "reference_id")
private Reference reference;
MORE STUFF
}
public class Uuid extends Serializable {
#ManyToOne
#JoinColumn(name = "reference_id")
private Reference reference;
MORE STUFF
}
I'm attempting to implement a limited type of object level ACL and its lead me to a place where I'm attempting to create a #OneToOne relationship using a composite key with a constant and dynamic value.
I have an Entity with a database id and a constant value defined in the class.
public class Entity{
private static final int objectType = 1;
#Id
Integer id;
}
I have an access_levels table with a composite key of objectId and objectType.
public class AccessLevel {
#EmbeddedId
private AccessLevelKey accessLevelKey;
#Embeddable
class AccessLevelKey implements Serializable{
private Integer objectType;
private Integer objectId;
....
}
}
Schema of access_levels
CREATE TABLE access_levels(
object_type INTEGER NOT NULL,
object_id INTEGER NOT NULL,
....
CONSTRAINT access_levels_type_id PRIMARY KEY (object_type, object_id)
);
I'm attempting to come up with a one to one relationship that Entity can use to fetch and update its associated AccessLevel
After taking a look a the docs on Non-Standard Joins it seems like I need something like this,
Inside of Entity:
#OneToOne
#JoinColumns({
#JoinColumn(name = "id", referencedColumnName = "object_id"),
#JoinColumn(name = "access_levels.object_type", referencedColumnName = "1"),
})
private AccessLevel accessLevel;
However this throws a hibernate MappingException at app launch
Caused by: org.hibernate.MappingException: Unable to find column with logical name: 1 in access_levels
Thanks!
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.)