Delete entity with Composite Key by Primary Key - java

I have 4 tables.
Country(id, name), CountryType(id, name), Client (id, name) and Country_CountryType_Client relation Table (country_id, countryType_id, client_id).
Here is my Country class:
#GeneratePojoBuilder(
intoPackage = "*.builder")
#Entity
#Table(name = "MD_COUNTRY")
#SequenceGenerator(
name = "SEQ_MD_COUNTRY",
sequenceName = "SEQ_MD_COUNTRY",
allocationSize = 1)
public class Country implements Serializable {
private static final long serialVersionUID = -3313476149373055743L;
private Long md_country_id;
private String nameKey;
private List<CountryCountryTypeClient> cCTypeClients;
#Id
#GeneratedValue(
generator = "SEQ_MD_COUNTRY")
#Column(
name = "MD_COUNTRY_ID")
public Long getMd_country_id() {
return md_country_id;
}
public void setMd_country_id(Long md_country_id) {
this.md_country_id = md_country_id;
}
#Column(name = "MD_COUNTRY_NAME_KEY")
public String getNameKey() {
return this.nameKey;
}
public void setNameKey(String name) {
this.nameKey = name;
}
#OneToMany(fetch=FetchType.EAGER,mappedBy="pk.country",cascade=CascadeType.ALL)
public List<CountryCountryTypeClient> getCountryCountryTypeClient() {
return cCTypeClients;
}
public void setCountryCountryTypes(List<CountryCountryTypeClient> countryCountryTypeClient) {
this.cCTypeClient = countryCountryTypeClient;
}
/* ... hashCode and equals methods..*/
The CountryType and Client classes look the same.
Here is my CountryCountryTypeClient class :
#GeneratePojoBuilder(
intoPackage = "*.builder")
#Entity
#Table(
name = "COUNTRY_COUNTRY_TYPE_CLIENT")
#AssociationOverrides({
#AssociationOverride(name= "pk.country",
joinColumns=#JoinColumn(name = "COUNTRY_ID")),
#AssociationOverride(name="pk.countryType",
joinColumns=#JoinColumn(name = "COUNTRY_TYPE_ID")),
#AssociationOverride(name="pk.client",
joinColumns=#JoinColumn(name = "CLIENT_ID"))
})
public class CountryCountryTypeClient implements Serializable{
private static final long serialVersionUID = -879391903880384781L;
private CountryCountryTypeClientPK pk = new CountryCountryTypeClientPK();
public CountryCountryTypeClient() {}
#EmbeddedId
public CountryCountryTypeClientPK getPk() {
return pk;
}
public void setPk(CountryCountryTypeClientPK pk) {
this.pk = pk;
}
#Transient
public Country getCountry(){
return getPk().getCountry();
}
public void setCountry(Country country) {
getPk().setCountry(country);
}
#Transient
public CountryType getCountryType(){
return getPk().getCountryType();
}
public void setCountryType(CountryType countryType) {
getPk().setCountryType(countryType);
}
#Transient
public Client getClient() {
return getPk().getClient();
}
public void setClient(Client client) {
getPk().setClient(client);
}
/* ... hashCode and equals ... */
Here is my CountryCountryTypeClientPK class :
#Embeddable
public class CountryCountryTypeClientPK implements Serializable {
private static final long serialVersionUID = -3934592006396010170L;
private Country country;
private CountryType countryType;
private Client client;
public CountryCountryTypeClientPK() {}
#ManyToOne
public Country getCountry() {
return country;
}
public void setCountry(Country country) {
this.country = country;
}
#ManyToOne
public CountryType getCountryType() {
return countryType;
}
public void setCountryType(CountryType countryType) {
this.countryType = countryType;
}
#ManyToOne
public Client getClient() {
return client;
}
public void setClient(Client client) {
this.client = client;
}
/*... hashCode and equals methods ..*/
My CountryCountryTypeClientRepository class :
public interface CountryCountryTypeRepository extends JpaRepository<CountryCountryTypeClient, CountryCountryTypeClientPK> {}
For my Country class I have CountryService class with saveCountry method :
public Country saveCountry(final Country dtoCountry) {
//save NEW Country
if(dtoCountry.getId()==null){
de.bonprix.global.masterdata.model.Country modelWithoutID = convertToModel(dtoCountry);
for (CountryCountryTypeClient cCTypeClient : modelWithoutID.getCountryCountryTypeClients()) {
cCTypeClient.setCountry(modelWithoutID);
}
return convertToDTO(this.countryRepository.saveAndFlush(modelWithoutID));
}
//save EDITED Country
else if (!(dtoCountry.getId()==null)){
de.bonprix.global.masterdata.model.Country modelWithID = convertToModel(dtoCountry);
for (CountryCountryTypeClient cCTypeClient : modelWithID.getCountryCountryTypeClients()) {
cCTypeClient.setCountry(modelWithID);
ccTypeClientRepository.delete(cCTypeClient);
}
return convertToDTO(this.countryRepository.saveAndFlush(modelWithID));
}
return null;
}
The question is: How can I delete all rows from my Country_CountryType_Client Table by Country_ID. Not by PK, but by Country_ID.
When I am saving the country in my Country Table, the Country_CountryType_Client is automatically updated with corresponding values.
Small example, just to clear the current problem.
In my Country_CountryType_Client Table now I have this.
And now I want to save the NewCountry that has all the same relation except the last row (298-2-9). My NewCountry dont know nothing about (298-2-9 relation). Before saving I have to delete all rows that have 298 id.
Hope the problem is clear.

I don't really understand the issue. Seems like all you really want to do is remove a single CountryCountryTypeClient from the Country with identifier 298.
Therefore if you were to update your mapping in as outlined in the following:
https://docs.oracle.com/cd/E19798-01/821-1841/giqxy/
#OneToMany(fetch=FetchType.EAGER,mappedBy="pk.country",cascade=CascadeType.ALL, orphanRemoval = true)
public List<CountryCountryTypeClient> getCountryCountryTypeClient() {
return cCTypeClients;
}
You can then simply do as follows:
Country country = // the country with id 298
CountryCountryTypeClient client = // the client with id 298/2/9
country.getCountryCountryTypeClient().remove(client);
countryRepository.save(country);

Related

could not serialize; nested exception is org.hibernate.type.SerializationException: could not serialize

I have spend way too much find finding the root cause of the below error:
org.springframework.orm.jpa.JpaSystemException: could not serialize; nested exception is org.hibernate.type.SerializationException: could not serialize
I am trying to save some value to db:
public void logFailure(Long objectID,Integer usLK){
StatusFailureDO failureDO = new StatusFailureDO(4,objectID, usLK);
failuresRepository.save(failureDO.getFailure());
}
#Repository
public interface FailuresRepository extends JpaRepository<GeneralFailure, Integer> {
GeneralFailure save(GeneralFailure aGeneralFailure);
void delete(GeneralFailure aGeneralFailure);
GeneralFailure findByObjectID(Long objectID);
}
There were many mapping errors and as such that I got pass now. I am trying to understand where in the process error occurs and what shall I look out for.
public class StatusFailureDO extends GeneralFailureDO implements Serializable
{
public StatusFailureDO(Integer failureTypeLK,Long objectID,
Integer usLK)
{
super(new StatusFailure(failureTypeLK,
"An exception occurred while trying to update an UploadStatus entry.",
objectID, usLK));
}
//more constructors and setters
}
public abstract class GeneralFailureDO implements ICISConstant, Serializable
{
private GeneralFailure mGeneralFailure;
//constructors and setters
}
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#Table(name = "GEN_FLR")
public class GeneralFailure implements Serializable,ICISConstant
{
#Column(name = "CRTN_TM")
private Date mCreationTime;
#Column(name = "TYP_LKP_ID")
private Integer failureTypeLK;
#Column(name = "STUS_LKP_ID")
private Integer mFailureStatusLK;
#Column(name="OBJ_ID")
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "native")
#GenericGenerator(name = "native", strategy = "native")
private Long objectID;
#Column(name = "DSCR")
private String mDescription;
public Date getCreationTime()
{
return mCreationTime;
}
public void setCreationTime(Date aCreationTime)
{
mCreationTime = aCreationTime;
}
public String getDescription()
{
return mDescription;
}
public void setDescription(String aDescription)
{
if (aDescription != null && aDescription.length() > MAX_DESCRIPTION_LENGTH)
{
mDescription = aDescription.substring(0, MAX_DESCRIPTION_LENGTH);
}
else
{
mDescription = aDescription;
}
}
public Long getObjectID()
{
return objectID;
}
public void setObjectID(Long aObjectID)
{
objectID = aObjectID;
}
public Integer getFailureTypeLK()
{
return failureTypeLK;
}
public void setFailureTypeLK(Integer aFailureTypeLK)
{
failureTypeLK = aFailureTypeLK;
}
public Integer getFailureStatusLK()
{
return mFailureStatusLK;
}
public void setFailureStatusLK(Integer aFailureStatusLK)
{
mFailureStatusLK = aFailureStatusLK;
}
}
#Entity
#Table(name="STUS_FLR")
public class StatusFailure extends GeneralFailure implements Serializable
{
#Column(name = "STUS_OBJ_ID")
private Long mStatusObjectID;
#Column(name = "STUS_LKP_ID")
private Integer mStatusLK;
#Column(name = "RQST_TYP_LKP_ID")
private Integer mRequestTypeLK;
#Column(name = "CODE")
private String mCode;
#Column(name = "PST_TM")
private Timestamp mPostTime;
#Column(name = "MSG_SZ")
private Integer mMessageSize;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Collection<StatusFailureError> StatusFailureErrorList;
#Column(name = "SMPL_FLG")
private boolean mSimple;
public Integer getStatusLK()
{
return mStatusLK;
}
public void setStatusLK(Integer statusLK)
{
mStatusLK = statusLK;
}
public Long getStatusObjectID()
{
return mStatusObjectID;
}
public void setStatusObjectID(Long statusObjectID)
{
mStatusObjectID = statusObjectID;
}
public String getCode()
{
return mCode;
}
public void setCode(String aCode)
{
mCode = aCode;
}
public Collection<StatusFailureError> getStatusFailureErrorList()
{
return mStatusFailureErrorList;
}
public void setStatusFailureErrorList(
Collection<StatusFailureError> aStatusFailureErrorList)
{
mStatusFailureErrorList = aStatusFailureErrorList;
}
public void setErrorList(Collection<String> aErrorList)
{
if (aErrorList != null && !aErrorList.isEmpty())
{
mStatusFailureErrorList = new ArrayList<StatusFailureError>();
for (Iterator<String> iter = aErrorList.iterator(); iter.hasNext();)
{
String error = (String) iter.next();
StatusFailureError failureError = new StatusFailureError(this, error, getPostTime());
mStatusFailureErrorList.add(failureError);
}
}
else
{
mStatusFailureErrorList = null;
}
}
public Integer getMessageSize()
{
return mMessageSize;
}
public void setMessageSize(Integer aMessageSize)
{
mMessageSize = aMessageSize;
}
public Timestamp getPostTime()
{
return mPostTime;
}
public void setPostTime(Timestamp aPostTime)
{
mPostTime = aPostTime;
}
public Integer getRequestTypeLK()
{
return mRequestTypeLK;
}
public void setRequestTypeLK(Integer aRequestTypeLK)
{
mRequestTypeLK = aRequestTypeLK;
}
public boolean isSimple()
{
return mSimple;
}
public void setSimple(boolean aSimple)
{
mSimple = aSimple;
}
}
Any help is really appreciated.
It is not obvious what the failureDO.getFailure() returns exactly because you did not provide a method definition for the StatusFailureDO.getFailure() method and so I will assume that that method returns an instance of a GeneralFailure class (or StatusFailure that extends it).
For hibernate to successfully save objects into the database, the #Entity classes that you are trying to save need to consist of "base" types. I see that you have an object of class CLRISCache defined in your GeneralFailure data class, that is most definitely not of a base type and not another #Entity. You can prevent a field from being persisted by marking it with the #Transient annotation, but really you should keep your data class pure.
You can find a list of "base" types here: https://docs.jboss.org/hibernate/orm/5.0/mappingGuide/en-US/html/ch03.html
Actually I found the reason. The General failure has dateCreation variable of Date type and in mu status failure I had it as a timestamp. I need to make it to Date and it worked.

Can not store list of objects as single column JSON

I'm trying to store a whole array of object into one field on my oracle database, I'm referring to the solution on this question, but it kept giving me Can not set java.lang.String field xxx.demo.Models.Sensors.amplitudos to xxx.demo.Models.Sensors error, I have checked the JSON body and the entity class, but I cannot find the mistake.
Here is my code.
entity
#Entity
#Table(name = "SENSOR")
public class Sensor implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "TIMERECEIVED")
private Timestamp timereceived;
#Column(name = "SENSORS")
private Sensors[] sensors;
#Column(name = "LOC")
private String location;
public Sensor() {
}
public Sensor(Timestamp timereceived, Sensors[] sensors, String location) {
this.timereceived = timereceived;
this.sensors = sensors;
this.location = location;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public Timestamp getTimereceived() {
return timereceived;
}
public void setTimereceived(Timestamp timereceived) {
this.timereceived = timereceived;
}
public Sensors[] getSensors() {
return sensors;
}
public void setSensors(Sensors[] sensors) {
this.sensors = sensors;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
}
Sensors class
#Embeddable
public class Sensors {
private String amplitudos;
private Double displacement;
private String frequencies;
private Integer sensorId;
public Sensors() {
}
public Sensors(String amplitudos, Double displacement, String frequencies, Integer sensorId) {
this.amplitudos = amplitudos;
this.displacement = displacement;
this.frequencies = frequencies;
this.sensorId = sensorId;
}
public String getAmplitudos() {
return amplitudos;
}
public void setAmplitudos(String amplitudos) {
this.amplitudos = amplitudos;
}
public Double getDisplacement() {
return displacement;
}
public void setDisplacement(Double displacement) {
this.displacement = displacement;
}
public String getFrequencies() {
return frequencies;
}
public void setFrequencies(String frequencies) {
this.frequencies = frequencies;
}
public Integer getSensorId() {
return sensorId;
}
public void setSensorId(Integer sensorId) {
this.sensorId = sensorId;
}
}
my JSON body
{
"timereceived": "2022-11-29T12:04:42.166",
"sensors": [
{
"amplitudos": "a1#a2#a3#a4",
"displacement": 0.002,
"frequencies": "f1#f2#f3#f4",
"sensorid": 1
},
{
"amplitudos": "a1#a2#a3#a4",
"displacement": 0.002,
"frequencies": "f1#f2#f3#f4",
"sensorid": 2
},
{
"amplitudos": "a1#a2#a3#a4",
"displacement": 0.002,
"frequencies": "f1#f2#f3#f4",
"sensorid": 3
},
{
"amplitudos": "a1#a2#a3#a4",
"displacement": 0.002,
"frequencies": "f1#f2#f3#f4",
"sensorid": 4
}
],
"location": "lokasi"
}
my controller
#PostMapping("/sendData")
public ResponseEntity sendData(#RequestBody Sensor sensor) {
Sensor newSensor = sensorRepository.save(sensor);
System.out.println(newSensor);
return ResponseEntity.ok("Sensor received");
}
I have tried checking every possible solution and the problem is not fixed, my expectation is the data stored into 1 column for the sensors field in the JSON body.
The problem is with the JPA mapping, not with the Controller, I think.
You're using #Embeddable, which normally result in a set of columns in your main table. If it's a collection of #Embeddable objects, you could map it to a separate table with foreign keys, using #ElementCollection.
However, you want to store the collection of sensors as a single JSON string in a single column in your main table. For that, you do not need the #Embeddable annotation. You need to write a custom convertor to convert the collection of sensors to JSON.
public class SensorsConverter implements AttributeConverter<List<Sensors>, String> {
private final ObjectMapper objectMapper = new ObjectMapper();
#Override
public String convertToDatabaseColumn(List<Sensors> sensors) {
return objectMapper.writeValueAsString(sensors);
}
#Override
public List<Sensors> convertToEntityAttribute(String sensorsJSON) {
return objectMapper.readValue(sensorsJSON, new TypeReference<List<Sensors>>() {});
}
}
Then you can use it in your entity class:
#Column(name = "SENSORS")
#Convert(converter = SensorsConverter.class)
private List<Sensors> sensors;

Spring Data JPA - Get the values of a non-entity column of a custom native query

I am using Spring Boot/MVC.
I have a custom query using JpaRepository:
public interface WorkOrderRepository extends JpaRepository<WorkOrder, Integer> {
#Query(value = "SELECT * FROM (SELECT * FROM workorder) Sub1 INNER JOIN (SELECT wo_number, GROUP_CONCAT(service_type SEPARATOR ', ') AS 'service_types' FROM service_type GROUP BY wo_number) Sub2 ON Sub1.wo_number=Sub2.wo_number WHERE fleet_company_id=?1 AND (order_status='On-Bidding' OR order_status='Draft')", nativeQuery = true)
Collection<WorkOrder> findWorkOrdersByFleet(Long fleetCompanyID);
}
It returns the following table:
http://imgur.com/Ylkc6U0
As you can see it has service_types columns which is a result of Concat, it's not part of the entity class. My problem is how can I get the value of that column. Some said I can use a separate DTO to map the service_types column? Or I can use 'new' keyword? Maybe you have other worked on me. I also tried to make a transient column service_types but it didn't work.
This is my entity class:
#Entity
#Table(name="workorder")
public class WorkOrder {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="wo_number")
private Long woNumber;
#ManyToOne(optional=false, cascade=CascadeType.ALL)
#JoinColumn(name = "vehicle_id")
private Vehicle vehicle;
#ManyToOne(optional=false, cascade=CascadeType.ALL)
#JoinColumn(name = "fleet_company_id")
private FleetCompany fleetCompany;
#Column(name="order_title")
private String orderTitle;
#Column(name="order_date")
private String orderDate;
#Column(name="order_time")
private String orderTime;
#Column(name="order_status")
private String orderStatus;
#Column(name="ref_number")
private String refNumber;
#Column(name="proposals")
private int proposals;
//#Column(name="serviceTypes")
#Transient
private int serviceTypes;
public WorkOrder() {
super();
}
public Long getWoNumber() {
return woNumber;
}
public void setWoNumber(Long woNumber) {
this.woNumber = woNumber;
}
public String getOrderTitle() {
return orderTitle;
}
public void setOrderTitle(String orderTitle) {
this.orderTitle = orderTitle;
}
public String getOrderDate() {
return orderDate;
}
public void setOrderDate(String orderDate) {
this.orderDate = orderDate;
}
public String getOrderTime() {
return orderTime;
}
public void setOrderTime(String orderTime) {
this.orderTime = orderTime;
}
public String getOrderStatus() {
return orderStatus;
}
public void setOrderStatus(String orderStatus) {
this.orderStatus = orderStatus;
}
public String getRefNumber() {
return refNumber;
}
public void setRefNumber(String refNumber) {
this.refNumber = refNumber;
}
public int getProposals() {
return proposals;
}
public void setProposals(int proposals) {
this.proposals = proposals;
}
public Vehicle getVehicle() {
return vehicle;
}
public void setVehicle(Vehicle vehicle) {
this.vehicle = vehicle;
}
public FleetCompany getFleetCompany() {
return fleetCompany;
}
public void setFleetCompany(FleetCompany fleetCompany) {
this.fleetCompany = fleetCompany;
}
public int getServiceTypes() {
return serviceTypes;
}
public void setServiceTypes(int serviceTypes) {
this.serviceTypes = serviceTypes;
}
}
Some people told me to make a DTO:
public class WorkOrderDTO extends WorkOrder {
private String service_types;
public WorkOrderDTO() {
super();
}
public WorkOrderDTO(String service_types) {
this.service_types = service_types;
}
public String getService_types() {
return service_types;
}
public void setService_types(String service_types) {
this.service_types = service_types;
}
}
and add make the repository replaced from WorkOrder to WorkOrderDTO.
public interface WorkOrderRepository extends JpaRepository<WorkOrderDTO, Integer>
but when I do that I have autowiring problems.
I solved my own problem, finally!!!
I used #SqlResultMapping
SqlResultSetMapping(
name="workorder",
classes={
#ConstructorResult(
targetClass=WorkOrderDTO.class,
columns={
#ColumnResult(name="wo_number", type = Long.class),
#ColumnResult(name="service_types", type = String.class),
#ColumnResult(name="order_title", type = String.class)
}
)
}
)
And I created a new POJO that is not an entity named WorkOrderDTO.
#PersistenceContext
private EntityManager em;
#Override
public Collection<WorkOrderDTO> getWork() {
Query query = em.createNativeQuery(
"SELECT Sub1.wo_number, Sub2.service_types, Sub1.order_title FROM (SELECT * FROM workorder) Sub1 INNER JOIN (SELECT wo_number, GROUP_CONCAT(service_type SEPARATOR ', ') AS 'service_types' FROM service_type GROUP BY wo_number) Sub2 ON Sub1.wo_number=Sub2.wo_number WHERE fleet_company_id=4 AND (order_status='On-Bidding' OR order_status='Draft')", "workorder");
#SuppressWarnings("unchecked")
Collection<WorkOrderDTO> dto = query.getResultList();
Iterable<WorkOrderDTO> itr = dto;
return (Collection<WorkOrderDTO>)itr;
}
At last, the users who hated me for posting the same problem won't be annoyed anymore.

Select from with boolean functions

I am trying to develop an application with Java EE. Here is a part of my class diagram which I want to to implement:
Here is the implementation of classes with hibernate:
#Entity
#Table(name = "t_enseignant")
public class Enseignant extends User implements Serializable {
private static final long serialVersionUID = 1L;
private String specialite;
private List<Module> modules;
public Enseignant() {
}
public Enseignant(String nom, String prenom, String email, String login, String password, String specialite) {
super(nom, prenom, email, login, password);
this.setSpecialite(specialite);
}
public String getSpecialite() {
return specialite;
}
public void setSpecialite(String specialite) {
this.specialite = specialite;
}
#OneToMany(mappedBy = "enseignant")
public List<Module> getModules() {
return modules;
}
public void setModules(List<Module> modules) {
this.modules = modules;
}
}
#Entity
#Table(name = "t_classe")
public class Classe implements Serializable {
private static final long serialVersionUID = 1L;
private int id;
private String niveau;
private int nbreEtudiant;
private List<Module> modules;
public Classe() {
}
public Classe(String niveau, int nbreEtudiant) {
this.niveau = niveau;
this.nbreEtudiant = nbreEtudiant;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int getId() {
return id;
}
...
I also implemented the Module Class and ModulePK.
I want to check that a teacher should be assigned to a class once.
I tried with the following JPQL query
boolean exists = false;
String jpql = "select case when (count(m) > 0) then true else false end from Module m where m.idEnseignant=idEnseignant and m.idClasse=idClasse";
TypedQuery<Boolean> query = entityManager.createQuery(jpql, Boolean.class);
query.setParameter("idEnseignant", enseignant.getId());
query.setParameter("idClasse", classeToValidate.getId());
exists = query.getSingleResult();
but when I execute the method I have this exception
could not resolve property: idEnseignant of: domain.Module [select case when (count(m) > 0) then true else false end from domain.Module m where m.idEnseignant=idEnseignant and m.idClasse=idClasse]
You forgot to the : to indicate your named parameters, so you had idEnseignant=idEnseignant instead of idEnseignant=:idEnseignant
Here's the corrected code:
boolean exists = false;
String jpql = "select case when (count(m) > 0) then true else false end from Module m where m.idEnseignant=:idEnseignant and m.idClasse=:idClasse";
TypedQuery<Boolean> query = entityManager.createQuery(jpql, Boolean.class);
query.setParameter("idEnseignant", enseignant.getId());
query.setParameter("idClasse", classeToValidate.getId());
exists = query.getSingleResult();
return exists
In your Module entity you define modulePk to be the primary key. There are two possiblities to use this id:
Create an instance of ModulePk class and pass it to the find() method of the entity manager (which is not the case in your query), or
To use it in JPQL query you have to traverse the embedded id class. In your case use it as follows:
String jpql = "select case when (count(m) > 0) then true else false end from Module m where m.modulePk.idEnseignant = :idEnseignant and m.modulePk.idClasse = :idClasse";
#jens
#Entity
#Table(name = "t_module")
public class Module implements Serializable {
private static final long serialVersionUID = 1L;
private ModulePk modulePk;
private String nomModule;
private int nbreHeure;
private Enseignant enseignant;
private Classe classe;
public Module() {
}
public Module(String nomModule, int nbreHeure, Enseignant enseignant, Classe classe) {
this.nomModule = nomModule;
this.nbreHeure = nbreHeure;
this.enseignant = enseignant;
this.classe = classe;
this.modulePk = new ModulePk(enseignant.getId(), classe.getId());
}
#EmbeddedId
public ModulePk getModulePk() {
return modulePk;
}
public void setModulePk(ModulePk modulePk) {
this.modulePk = modulePk;
}
public String getNomModule() {
return nomModule;
}
public void setNomModule(String nomModule) {
this.nomModule = nomModule;
}
public int getNbreHeure() {
return nbreHeure;
}
public void setNbreHeure(int nbreHeure) {
this.nbreHeure = nbreHeure;
}
#ManyToOne
#JoinColumn(name = "idEnseignant", referencedColumnName = "id", insertable = false, updatable = false)
public Enseignant getEnseignant() {
return enseignant;
}
public void setEnseignant(Enseignant enseignant) {
this.enseignant = enseignant;
}
#ManyToOne
#JoinColumn(name = "idClasse", referencedColumnName = "id", insertable = false, updatable = false)
public Classe getClasse() {
return classe;
}
public void setClasse(Classe classe) {
this.classe = classe;
}
}
and this is ModulePK class
#Embeddable
public class ModulePk implements Serializable {
private static final long serialVersionUID = 1L;
private int idEnseignant;
private int idClasse;
public ModulePk() {
}
public ModulePk(int idEnseignant, int idClasse) {
this.setIdEnseignant(idEnseignant);
this.setIdClasse(idClasse);
}
...

detached entity passed to persist for batch insert in JPA

For the following batch insert method, i get this exception "detached entity passed to persist". Could you take a look at this method and give me some hints?
Thank you so much.
if needed, I will provided the entities here, for the moment I provide Keyword entity :
public class Keyword implements Serializable {
private static final long serialVersionUID = -1429681347817644570L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="key_id")
private long keyId;
#Column(name="key_name")
private String keyName;
#ManyToOne
#JoinColumn(name="tweet_id")
private Tweet tweet;
public long getKeyId() {
return keyId;
}
public void setKeyId(long keyId) {
this.keyId = keyId;
}
public String getKeyName() {
return keyName;
}
public void setKeyName(String keyName) {
this.keyName = keyName;
}
public Tweet getTweet() {
return tweet;
}
public void setTweet(Tweet tweet) {
this.tweet = tweet;
}
}
Here Tweet Entity :
#Entity
#Table(name="tweets")
public class Tweet implements Serializable{
#Id
#Column(name="tweet_id")
private long tweetId;
#Column(name="tweet_text")
private String tweetText;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "created_at")
private Date createdAt;
#Column(name="lang_code")
private String languageCode;
#ManyToOne
#JoinColumn(name = "user_id")
private User user;
#OneToMany(mappedBy="tweet")
//#JoinColumn(name="hashtag_id")
private List<Hashtag> hashtags;
#OneToMany(mappedBy="tweet")
//#JoinColumn(name="url_id")
private List<Url> urls;
public List<Keyword> getKeywords() {
return keywords;
}
public void setKeywords(List<Keyword> keywords) {
this.keywords = keywords;
}
#OneToMany(mappedBy="tweet")
//#JoinColumn(name="url_id")
private List<Keyword> keywords;
public long getTweetId() {
return tweetId;
}
public void setTweetId(long tweetId) {
this.tweetId = tweetId;
}
public String getTweetText() {
return tweetText;
}
public void setTweetText(String tweetText) {
this.tweetText = tweetText;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public String getLanguageCode() {
return languageCode;
}
public void setLanguageCode(String languageCode) {
this.languageCode = languageCode;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public List<Hashtag> getHashtags() {
return hashtags;
}
public void setHashtags(List<Hashtag> hashtags) {
this.hashtags = hashtags;
}
public List<Url> getUrls() {
return urls;
}
public void setUrls(List<Url> urls) {
this.urls = urls;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (tweetId ^ (tweetId >>> 32));
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Tweet other = (Tweet) obj;
if (tweetId != other.tweetId)
return false;
return true;
}
And here Url entity :
#Entity
#Table(name="tweet_url")
public class Url implements Serializable{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="url_id")
private int urlId;
#Column(name="url")
private String url;
#ManyToOne
#JoinColumn(name="tweet_id",referencedColumnName="tweet_id")
private Tweet tweet;
public int getUrlId() {
return urlId;
}
public void setUrlId(int urlId) {
this.urlId = urlId;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Tweet getTweet() {
return tweet;
}
public void setTweet(Tweet tweet) {
this.tweet = tweet;
}
And here is hashtag entity :
#Entity
#Table(name="tweet_hashtag")
public class Hashtag implements Serializable{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="hashtag_id")
private int hashtagId;
#Column(name="hashtag")
private String hashtag;
#ManyToOne
#JoinColumn(name="tweet_id",referencedColumnName="tweet_id")
private Tweet tweet;
public int getHashtagId() {
return hashtagId;
}
public void setHashtagId(int hashtagId) {
this.hashtagId = hashtagId;
}
public String getHashtag() {
return hashtag;
}
public void setHashtag(String hashtag) {
this.hashtag = hashtag;
}
public Tweet getTweet() {
return tweet;
}
public void setTweet(Tweet tweet) {
this.tweet = tweet;
}
And the method :
public void batchInsert(List<Keyword> results) throws HibernateException {
// chekeywordck if key exists
// try {
em=RunQuery.emf.createEntityManager();
em.getTransaction().begin();
for(Keyword result:results)
{
try{
em.persist(result.getTweet().getUser());
}
catch(ConstraintViolationException ce)
{
System.out.print("duplicated insert catched");
}
try{
em.persist(result.getTweet());
}
catch(ConstraintViolationException ce)
{
System.out.print("duplicated insert catched");
}
if(result.getTweet().getHashtags()!=null)
for(Hashtag hashtag:result.getTweet().getHashtags())
em.persist(hashtag);
if(result.getTweet().getUrls()!=null)
for(Url url:result.getTweet().getUrls())
em.persist(url);
em.persist(result);
em.flush();
em.clear();
//when I put these two line out of this loop, it still is the same.
}
em.getTransaction().commit();
// }
}
And here is the exception :
Exception in thread "Thread-3" javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: model.twitter.entities.Url
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1763)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1677)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1683)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1187)
at model.service.QueryResultService.batchInsert(QueryResultService.java:74)
at controller.ResultsController.save(ResultsController.java:125)
at controller.ResultsController.parse(ResultsController.java:89)
at main.TwitterStreamConsumer.run(TwitterStreamConsumer.java:41)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: model.twitter.entities.Url
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:139)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:75)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:811)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:784)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:789)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1181)
... 5 more
To answer your question: your model defines a one-to-many relationship between Tweet and URL without any cascading. When you are passing a Tweet instance for persisting, the URL objects have not yet been saved and your model does not mandate Tweet to cascade the persist operation to the URL instances. Therefore it can not create the relationship with them.
Cascading tells the hibernate, how to execute DB operations on related entities.
You can instruct it to pass/cascade the persist operation to the related entity, to cascade all operations or an array of operations.
That being said, your problem(1 of them) could be fixed if you modify the relationship with cascading info:
#OneToMany(mappedBy="tweet", cascade={CascadeType.PERSIST})
private List<Url> urls;
But your sample indicates other possible issues and I would encourage you to spent some more time reading Hibernate ORM documentation and practicing on sample model with less relationships.
One of the obvious issues seems to be the lack of understanding of relationship owner concept.
For example, in your Tweet-to-Url relationship, URL is the relationship owner(responsible for managing the relationship, e.g. managing the link via foreign key)
Please consult hibernate docs or one of hundreds of similar questions here on SO for more info.
Depending on how you fill the data, it is possible that you will run into constraint issues, or your entities will not be linked together, because you are not saving the owning side.
Also using try/catch for constraint violations is a very bad way of detecting duplicated entries. ConstraintViolationException can be have many causes and the reason you are getting them is related to the above mentioned relationship mapping issues.
ORM is complex subject and it is really beneficial to start with smaller examples, trying to understand the framework mechanics before moving to the more challenging models. Good Luck
For all the persist calls try using this instead:
if(result.getTweet().getUser().getId() == null) {
em.persist(result.getTweet().getUser());
} else {
result.getTweet().setUser(em.merge(result.getTweet().getUser()));
}
if(result.getTweet().getId() == null) {
em.persist(result.getTweet());
} else {
result.setTweet(em.merge(result.getTweet()));
}
if(result.getId() == null) {
em.persist(result);
} else {
result = em.merge(result);
}

Categories