How to solve (Java + Spring + JPA + Hibernate Mappings) relationships - java

I have read many topics about mapping with JPA + Hibernate, but after trying several things I can not get the expected result.
I have declared all my unidirectional relationships since I do not see the need to bidirect them
My objects to map are:
Client has a Country and a list of addresses.
#Table(name = "Client")
#Entity
public class Client
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "native")
#GenericGenerator(name = "native", strategy = "native")
private Long id;
#OneToOne(cascade = { CascadeType.MERGE, CascadeType.PERSIST })
#JoinColumn(name="country_id", unique = false, /*nullable = false,*/ insertable = true, updatable = false, foreignKey = #ForeignKey(name = "country_fk0"))
private Country country;
#OneToMany(cascade = { CascadeType.PERSIST }, orphanRemoval = true)
#JoinColumn(name = "address_id",/* nullable = false,*/ foreignKey = #ForeignKey(name = "address_fk0"))
private List<Address> address;
//GETTERS / SETTERS
}
then I will evaluate the client and save it in a new ClientProcessed table, which will reference the Addresses and Country objects stored in my Client Object.
#Table(name = "ProcessedClient")
#Entity
public class ProcessedClient
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "native")
#GenericGenerator(name = "native", strategy = "native")
private Long id;
#OneToOne(cascade = { CascadeType.MERGE, CascadeType.PERSIST })
#JoinColumn(name="client_evaluation_id", unique = false, /*nullable = false,*/ insertable = true, updatable = false, foreignKey = #ForeignKey(name = "evaluation_fk0"))
private ClientEvaluation evaluation;
#OneToOne(cascade = { CascadeType.MERGE })
#JoinColumn(name="country_id", unique = false, /*nullable = false,*/, foreignKey = #ForeignKey(name = "country_fk1"))
private Country country;
#OneToMany(cascade = { CascadeType.MERGE}, orphanRemoval = true)
#JoinColumn(name = "address_id",/* nullable = false,*/ foreignKey = #ForeignKey(name = "address_fk2"))
private List<Address> addresses;
//GETTERS / SETTERS
}
So then when i do that:
Country country = new Country();
country.setId(1l); // (DB ID)
// I do the same with addresses
ProcessedClient processedClient = new ProcessedClient();
processedClient.setAddresses(addresses);
processedClient.setCountry(country);
this.getDao().save(processedClient);
Result:
org.hibernate.TransientPropertyValueException: object references an
unsaved transient instance - save the transient instance before
flushing
Thx

From documentation:
Hibernate defines and supports the following object states:
Transient - an object is transient if it has just been instantiated using the new operator, and it is not associated with a Hibernate Session . ...
Persistent - a persistent instance has a representation in the database and an identifier value.
You should load country from database by id and then set it to ProcessedClient.

Related

OneToMany relationship does not store entity field

When saving my entities, child entities that work through the #OneToMany relationship are not saved to their tables. I can’t understand what’s the matter.
Employee:
#Entity
#Table(name = "EMPLOYEE", schema = PUBLIC)
public class Employee {
private String name;
private String lastname;
#OneToMany(mappedBy = "employee", cascade = CascadeType.ALL, orphanRemoval = true)
List<EmployeePhoneNumber> employeePhoneNumbers = new ArrayList<>();
}
EmployeePhoneNumber:
#Entity
#Table(name = "EMPLOYEE_PHONES", schema = PUBLIC)
public class EmployeePhoneNumber {
#Id
#SequenceGenerator(allocationSize = 1, name = "SEQ_EMPLOYEE_PHONES", schema = PUBLIC,
sequenceName = "EMPLOYEE_PHONES_ID_SEQ")
#GeneratedValue(generator = "SEQ_EMPLOYEE_PHONES", strategy = GenerationType.SEQUENCE)
#Column(name = "ID", unique = true, nullable = false)
private Long id;
#ManyToOne
#JoinColumn(name = "employee_id", referencedColumnName = "id",
nullable = false, insertable = false, updatable = false)
private Employee employee;
#Column(name = "PHONE_NUMBER", unique = true, nullable = false)
private String phoneNumber;
#Enumerated(EnumType.STRING)
#Column(name = "NUMBER_TYPE", nullable = false)
private PhoneNumberType phoneNumberType;
}
How I set those fields and then save the entity:
EmployeePhoneNumber workPhone = new EmployeePhoneNumber();
workPhone.setPhoneNumber(workPhone);
workPhone.setPhoneNumberType(PhoneNumberType.WORK_PHONE);
EmployeePhoneNumber mobilePhone = new EmployeePhoneNumber();
mobilePhone.setPhoneNumber(mobilePhone);
mobilePhone.setPhoneNumberType(PhoneNumberType.MOBILE_PHONE);
EmployeePhoneNumber corporatePhone = new EmployeePhoneNumber();
corporatePhone.setPhoneNumber(corporatePhoneNumber);
corporatePhone.setPhoneNumberType(PhoneNumberType.CORPORATE_PHONE);
List<EmployeePhoneNumber> employeePhoneNumbers = employee.getEmployeePhoneNumbers();
employeePhoneNumbers.add(workPhone);
employeePhoneNumbers.add(mobilePhone);
employeePhoneNumbers.add(corporatePhone);
employee.setEmployeePhoneNumbers(employeePhoneNumbers);
employeeRepository.save(employee);
Upon completion of the method, I do not have a single error, everything works out correctly, only the tables are not filled - why?
You must also set the Employee reference in EmployeePhoneNumber because Hibernate will use this to save it.
workPhone.setEmployee(employee);
mobilePhone.setEmployee(employee);
corporatePhone.setEmployee(employee);
The best solution would be to create an addEmployeePhoneNumber method on the Employee like this:
public void addEmployeePhoneNumber(EmployeePhoneNumber phoneNumber) {
phoneNumber.setEmployee(this);
employeePhoneNumbers.add(phoneNumber);
}
That way you will not forget to set both sides of the relationship.

Hibernate/JPA Using parents's identity id on cascade son's id

Anyone has been through this using Hibernate / JPA:
In a mapping that does the "many-to-many" with extra fields I do the following assembly:
Father1 -> SonDocument - Since the son has a composite key which is the father's ID + the ID of a Document table.
The problem: The father's id is Identity and I persist the father with a cascade of the children, so at the time of the child's persist it would have to have a "refresh" to get the ID that was just generated in the father. It turns out that this ID is never loaded.
Mapping:
public class Pai{
#OneToMany(cascade={CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.REFRESH}, fetch = FetchType.LAZY, mappedBy = "pai", orphanRemoval = true)
private Set<FilhoDocumento> documentos;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "nome", length = 30, unique = false, nullable = false)
private String nome;
public void addDocumento(final Documento) {
getDocumentos().add(new FilhoDocumento(this, d));
}
}
Son's Mapping
#Entity
#AttributeOverrides({
#AttributeOverride(name = "paiId", column=#Column(name="...")),
#AttributeOverride(name = "documentoId", column=#Column(name="..."))
})
#IdClass(FilhoDocumentoPK.class)
public class FilhoDocumento implements Serializable {
#Column(name = "impeditivo", nullable = false)
private boolean impeditivo;
#Column(name = "obrigatorioMatriculaPeloAluno", nullable = false)
private boolean obrigatorioMatriculaPeloAluno;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "idPai", referencedColumnName = "id", insertable = false, updatable = false)
private Pai pai;
#Id
private Integer paiId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "IDDocumento", referencedColumnName = "ID", insertable = false, updatable = false)
private Documento documento;
#Id
private Integer documentoId;
....
}
And the PK's mapping class
public class PaiDocumentoMatriculaPK implements Serializable {
#Column(name = "IDPai")
private Integer paiId;
#Column(name = "IDDocumento")
private Integer documentoId;
...
}
Running the following test code or Father's id in the SonDocument table is null at the time of Persist execution on the Parent entity. For this idea, a cascade should take the Id that was just included and use it on the child. It is possible?
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
try {
Documento rg = new Documento("RG");
entityManager.persist(rg);
entityManager.flush();
Session session = entityManager.unwrap(Session.class);
rg = (Documento) session.bySimpleNaturalId(Documento.class)
.load("RG");
Pai hpjp1 = new Pai();
hpjp1.setNome("teste");
hpjp1.addDocumento(rg);
entityManager.persist(hpjp1);
...
shows the error:
Caused by: java.sql.SQLIntegrityConstraintViolationException: Column 'IDPai' cannot be null
Changing the way the parent entity was persisting, first recording and getting the ID and then passing the ID and persisting in the child entities, it worked.
Example:
Documento rg = new Documento("RG");
entityManager.persist(rg);
entityManager.flush();
Session session = entityManager.unwrap(Session.class);
rg = (Documento) session.bySimpleNaturalId(Documento.class)
.load("RG");
final Pai hpjp1 = new Pai();
// hpjp1.setId(1L);
hpjp1.setNome("teste");
hpjp1.addDocumento(rg);
HashSet<FilhoDocumento > docs = new HashSet<FilhoDocumento >(hpjp1.getDocumentos());
entityManager.persist(hpjp1);
entityManager.clear();
entityManager.flush();
for(FilhoDocumento r : docs) {
r.setPai(hpjp1);
r.setPaiId(hpjp1.getId());
r.setDocumentoId(rg.getId());
}
hpjp1.setDocumentos(docs);
entityManager.merge(hpjp1);
thank you all for the tips

Java/Postgres/JPA : PersistentObjectException: detached entity passed to persist

I have a one to many relationship between "code" and "code system" (a code system has many codes.) I implemented my service with the following to save codes for code systems:
service
code = new Code();
code.setCode(valueConcept.getCode());
code.setName(valueConcept.getDisplay_name());
code.setValueSet(valueSet);
code.setCodeSystem(codeSystem);
code.setDateCreated(new Date());
code.setDateUpdated(new Date());
codeRepository.save(code);
The last line of the code is throwing the exception. "Code System" is an object which is created above using a similar JPA repository.
To implement this one to many relationship I have the following two entity classes:
Code.java
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "codeCode_idGenerator")
#Basic(optional = false)
#Column(name = "\"CODE_ID\"", nullable = false, insertable = false, updatable = false)
#SequenceGenerator(allocationSize = 1, name = "codeCode_idGenerator", sequenceName = "crisp_pophealth.application.code_id_seq", schema = "application", catalog = "crisp_pophealth")
public Integer getId() {
return this.id;
}
#ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE }, fetch = FetchType.LAZY)
#org.hibernate.annotations.Cascade({ org.hibernate.annotations.CascadeType.SAVE_UPDATE })
#Basic(optional = true)
#JoinColumn(name = "\"CODE_SYSTEM_ID\"", nullable = true)
public CodeSystem getCodeSystem() {
return this.codeSystem;
}
CodeSystem.java
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "codeSystemRecord_idGenerator")
#Basic(optional = false)
#Column(name = "\"RECORD_ID\"", nullable = false)
#SequenceGenerator(allocationSize = 1, name = "codeSystemRecord_idGenerator", sequenceName = "crisp_pophealth.application.code_system_id_seq", schema = "application", catalog = "crisp_pophealth")
public Integer getId() {
return this.id;
}
#OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST,
CascadeType.MERGE }, mappedBy = "codeSystem")
#org.hibernate.annotations.Cascade({ org.hibernate.annotations.CascadeType.SAVE_UPDATE })
#Basic(optional = false)
#Column(name = "\"RECORD_ID\"", nullable = false)
public Set<Code> getCodes() {
return this.codes;
}
The reason the sequences are set the way they are is because the primary key is defined as follows on each postgres table (as an example):
CREATE TABLE application.code ("CODE_ID" integer NOT NULL DEFAULT nextval('application.code_id_seq'::regclass),
What's the best way annotate the classes for this relation to exist? I'm using the sequence strategy AUTO since the database is managing sequence values on creation. I've tried saving unsuccessfully using an entity manager, but I would prefer to have all persistence managed by these JPA repository classes.

Strange behaviour when requesting on a ManyToMany relation with composite PrimaryKey

I am trying to create a ManyToMany relation between DocumentModels, with an additionnal information in the relation (dosIndex)
#Entity
#Table(name = "T_DOCUMENT_MODELS_DMO")
public class TDocumentModelsDmo extends fr.axigate.nx.frontend.server.common.entity.ValidityPeriodEntity implements Serializable
{
#Id
#SequenceGenerator(name = "T_DOCUMENT_MODELS_DMO_DMOID_GENERATOR", sequenceName = "T_DMO_ID_SEQ")
#GeneratedValue(strategy = GenerationType.AUTO, generator = "T_DOCUMENT_MODELS_DMO_DMOID_GENERATOR")
#Column(name = "DMO_ID", precision = 22)
private Long dmoId;
//Other unrelated members, no reference to TjDocumentSourcesDos
//constructors, getters and setters without annotations
}
#Entity
#Table(name = "TJ_DOCUMENT_SOURCES_DOS")
public class TjDocumentSourcesDos implements Serializable
{
#Column(name = "DOS_INDEX", nullable = false, precision = 22)
private long dosIndex; //the additionnal info on the relation
#EmbeddedId
private TjDocumentSourcesDosPK id = new TjDocumentSourcesDosPK();
#ManyToOne
#MapsId("dosParentId")
#JoinColumn(name = "DOS_PARENT_ID", nullable = false, insertable = false, updatable = false)
private TDocumentModelsDmo TDocumentModelsDmoParent;
#ManyToOne
#MapsId("dosSourceId")
#JoinColumn(name = "DOS_SOURCE_ID", nullable = false, insertable = false, updatable = false)
private TDocumentModelsDmo TDocumentModelsDmoSource;
//constructors, getters and setters without annotations
}
#Embeddable
public class TjDocumentSourcesDosPK implements Serializable
{
#Column(name = "DOS_PARENT_ID", nullable = false, precision = 22)
private Long dosParentId;
#Column(name = "DOS_SOURCE_ID", nullable = false, precision = 22)
private Long dosSourceId;
//constructors, getters and setters without annotations
//hashCode and equals implemented
}
I can insert datas in both tables, but when I try to request it using an entityManager, i get something strange :
Query query = entityManager.createQuery("SELECT dos.TDocumentModelsDmoSource FROM TDocumentModelsDmo AS dmo, TjDocumentSourcesDos as dos WHERE dmo.dmoId = :modelId AND dos.TDocumentModelsDmoParent = dmo");
query.setParameter("modelId", someData);
ArrayList<TjDocumentSourcesDos> dosList = (ArrayList<TjDocumentSourcesDos>) query.getResultList();
will work, while the following will throw an exception : QuerySyntaxException: dos.TDocumentModelsDmoSource is not mapped
Query query = entityManager.createQuery("SELECT sources FROM TDocumentModelsDmo AS dmo, TjDocumentSourcesDos as dos, dos.TDocumentModelsDmoSource AS sources WHERE dmo.dmoId = :modelId AND dos.TDocumentModelsDmoParent = dmo");
query.setParameter("modelId", someData);
ArrayList<TjDocumentSourcesDos> dosList = (ArrayList<TjDocumentSourcesDos>) query.getResultList();
This prevents me from doing more complicated requests where I would use my sources models in the WHERE condition.
I tried adding a referencedColumnName = "DMO_ID" in both my JoinColumn annotations, but I still get the same error

hibernate parent - child query, dont filter by children's FK

I have a problem with hibernate query, that filters by columns from multiple entities inherited from common parent.
Here is example:
abstract parent:
#Entity
#Inheritance (strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class AItem
{
/** The id. */
#Id
#Column (name = "pk_id")
private Long id;
/** The date of creational. */
#Column (name = "c_date_created", nullable = true, columnDefinition = "timestamp with time zone")
private Calendar dateCreated;
/** The date of change. */
#Column (name = "c_date_changed", nullable = true, columnDefinition = "timestamp with time zone")
private Calendar dateChanged;
/** Name of item. */
#Column (name = "c_name", nullable = false)
private String name;
/** type of item. */
#Column (name = "c_type", nullable = false)
#Enumerated (EnumType.STRING)
EItemType type;
}
children:
#Entity
#Table (name = "t_product", schema = "am")
public class CProduct extends AItem
{
#ManyToMany (fetch = FetchType.LAZY, mappedBy = "products")
private Set<CCampaign> campaigns;
...some more attributes...
}
#Entity
#Table (name = "t_service", schema = "am")
public class CService extends AItem
{
#ManyToMany (fetch = FetchType.LAZY, mappedBy = "services")
private Set<CCampaign> campaigns;
...some more attributes...
}
ccampaign:
#Entity
#Table (name = "t_campaign", schema = "am")
public class CCampaign
{
/** The id. */
#Id
#SequenceGenerator (name = "T_CAMPAIGN_PKID_GENERATOR", sequenceName = "AM.T_CAMPAIGN_PK_ID_SEQ")
#GeneratedValue (strategy = GenerationType.AUTO, generator = "T_CAMPAIGN_PKID_GENERATOR")
#Column (name = "pk_id")
private Long id;
/**
* Products available in the campaign.
*/
#ManyToMany (fetch = FetchType.EAGER, cascade = {CascadeType.REFRESH, CascadeType.MERGE})
#JoinTable (name = "t_x_campaign_product", schema = "am", joinColumns = {#JoinColumn (name = "pk_campaign", nullable = false, updatable = false)}, inverseJoinColumns = {#JoinColumn (name = "pk_item", nullable = false, updatable = false)})
private Set<CProduct> products;
/**
* services available in the campaign.
*/
#ManyToMany (fetch = FetchType.EAGER, cascade = {CascadeType.REFRESH, CascadeType.MERGE})
#JoinTable (name = "t_x_campaign_service", schema = "am", joinColumns = {#JoinColumn (name = "pk_campaign", nullable = false, updatable = false)}, inverseJoinColumns = {#JoinColumn (name = "pk_item", nullable = false, updatable = false)})
private Set<CService> services;
}
I want all items (services+products) returned for that campaign.
I have query
select distinct item from AItem item left join item.campaigns camp where camp.id = 5
now what hibernate does is it returns all related products,but not single service.
there are assigned services to campaign.
when i run query without specifiing camp.id then query will return services .
can somone help solve this?.
Using of Hibernate filter will solver your problem, see here See the questions answer of this
annotation to filter results of a #OneToMany association

Categories

Resources