detached entity passed to persist for batch insert in JPA - java

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);
}

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.

Drools rule for Object type member

I am new to drools, please bear with me if this is a silly question. I have a class member type of Object which I am using to store JSON value (passed from frontend), due to unstructured data I am using Object type for a variable. Here's the POJO class.
public class Submission {
#Id
private String id;
private String form;
private String formId;
private Object data;
private Date createdAt = new Date();
private Date modifiedAt = new Date();
private String state;
private Boolean isDeleted = false;
private Boolean valid = false;
public Boolean getValid() {
return valid;
}
public void setValid(Boolean valid) {
this.valid = valid;
}
public String getForm() {
return form;
}
public void setForm(String form) {
this.form = form;
}
public String getFormId() {
return formId;
}
public void setFormId(String formId) {
this.formId = formId;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getModifiedAt() {
return modifiedAt;
}
public void setModifiedAt(Date modifiedAt) {
this.modifiedAt = modifiedAt;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Boolean getDeleted() {
return isDeleted;
}
public void setDeleted(Boolean deleted) {
isDeleted = deleted;
}
}
and this is my .drl
package rules;
import com.ics.lcnc.Submission.Submission;
rule "check name is correct"
when
Submission(data.name == "john")
then
submission.setValid(true)
end
But for above file I get following error when I try to load this file into KieBuilder
Message [id=3, kieBase=defaultKieBase, level=ERROR, path=rules/123.drl, line=5, column=0
text=Unable to Analyse Expression data.name == "hashim":
[Error: unable to resolve method using strict-mode: java.lang.Object.name()]
[Near : {... data.name == "hashim" ....}]
Seems like rule engine is unable to find nested property of Object data. How do I target nested property which will be known to a program at runtime only?
you may try with having the data field be converted (eg by Jackson) as a Map following a prototype based approach and then
...
when
Submission(data["name"] == "john") ...
of instead of Map have a JSONNode (and adapt the constraint in the rule), again for the data field in your Submission object model.

How to map composite key having attributes such as (Subject,name) with Hibernate?

Problem:
1)Given an entity Teacher where a Teacher can teach many Subjects.
2)Teacher(name,Subject) is a Composite Key.
3)A Subject can have 1 or more ParentSubject.
Solution:
1)I used the following threads Thread1 Thread2 Thread3,Thread4
to find the solution to the problem and implemented the above solution but could not write proper implementation.
2)For a Composite key I used subjectId and teacherId because I was unable to map Subject with composite key
Q)Can anyone guide me how to find solution to the above problem.
Below is the following Code:
public class OurLogic {
public static void main(String args[])
{
Configuration cfg = new Configuration();
cfg.configure("hibernate.cfg.xml");
SessionFactory factory = cfg.buildSessionFactory();
Session session = factory.openSession();
//parent object
Teacher t =new Teacher();
t.setTeacherId(101);
t.setTeacherName("Jalaj");
//creating 3 child objects
Subject s1=new Subject();
s1.setSubjectId(504);
s1.setSubjectName("JSE");
Subject s2=new Subject();
s2.setSubjectId(505);
s2.setSubjectName("JEE");
Subject s3=new Subject();
s3.setSubjectId(506);
s3.setSubjectName("Spring");
// adding child objects to set, as we taken 3rd property set in parent
Set s=new HashSet();
s.add(s1);
s.add(s2);
s.add(s3);
t.setSubjects(s);
Transaction tx = session.beginTransaction();
session.save(t);
tx.commit();
session.close();
System.out.println("One To Many is Done..!!");
factory.close();
}
}
public class Subject implements Serializable{
private int subjectId;
private String subjectName;
private int forevenId;
public int getSubjectId() {
return subjectId;
}
public void setSubjectId(int subjectId) {
this.subjectId = subjectId;
}
public String getSubjectName() {
return subjectName;
}
public void setSubjectName(String subjectName) {
this.subjectName = subjectName;
}
public int getForevenId() {
return forevenId;
}
public void setForevenId(int forevenId) {
this.forevenId = forevenId;
}
}
public class Teacher implements Serializable{
private int teacherId;
private String teacherName;
private Set subjects;
public int getTeacherId() {
return teacherId;
}
public void setTeacherId(int teacherId) {
this.teacherId = teacherId;
}
public String getTeacherName() {
return teacherName;
}
public void setTeacherName(String teacherName) {
this.teacherName = teacherName;
}
public Set getSubjects() {
return subjects;
}
public void setSubjects(Set subjects) {
this.subjects = subjects;
}
}
public class TeachSubject implements Serializable{
private Long subjectId;
private Long teacherId;
// an easy initializing constructor
public TeachSubject(Long testId, Long customerId){
this.subjectId = subjectId;
this.teacherId = teacherId;
}
public Long getSubjectId() {
return subjectId;
}
public void setSubjectId(Long subjectId) {
this.subjectId = subjectId;
}
public Long getTeacherId() {
return teacherId;
}
public void setTeacherId(Long teacherId) {
this.teacherId = teacherId;
}
#Override
public boolean equals(Object arg0) {
if(arg0 == null) return false;
if(!(arg0 instanceof TeachSubject)) return false;
TeachSubject arg1 = (TeachSubject) arg0;
return (this.subjectId.longValue() == arg1.getSubjectId().longValue()) && (this.teacherId.longValue() == arg1.getTeacherId().longValue());
}
#Override
public int hashCode() {
int hsCode;
hsCode = subjectId.hashCode();
hsCode = 19 * hsCode+ teacherId.hashCode();
return hsCode;
}
}
ERROR:
Exception in thread "main" org.hibernate.boot.InvalidMappingException: Could not parse mapping document: TeachSubject.hbm.xml (RESOURCE)
at org.hibernate.boot.jaxb.internal.InputStreamXmlSource.doBind(InputStreamXml Source.java:46)
at org.hibernate.boot.jaxb.internal.UrlXmlSource.doBind(UrlXmlSource.java:36)
at org.hibernate.boot.spi.XmlMappingBinderAccess.bind(XmlMappingBinderAccess.java:59)
at org.hibernate.boot.MetadataSources.addResource(MetadataSources.java:274)
at org.hibernate.boot.cfgxml.spi.MappingReference.apply(MappingReference.java:70)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:413)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:87)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:691)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:723)
at str.OurLogic.main(OurLogic.java:19)
Caused by: org.hibernate.boot.MappingException: Error accessing stax stream : origin(TeachSubject.hbm.xml)
at org.hibernate.boot.jaxb.internal.AbstractBinder.seekRootElementStartEvent(AbstractBinder.java:141)
at org.hibernate.boot.jaxb.internal.AbstractBinder.doBind(AbstractBinder.java:101)
at org.hibernate.boot.jaxb.internal.AbstractBinder.bind(AbstractBinder.java:57)
at org.hibernate.boot.jaxb.internal.InputStreamXmlSource.doBind(InputStreamXmlSource.java:43)
... 9 more
Caused by: javax.xml.stream.XMLStreamException: ParseError at [row,col]:[5,2]
Message: The markup in the document preceding the root element must be well-formed.
at com.sun.org.apache.xerces.internal.impl.XMLStreamReaderImpl.next(XMLStreamReaderImpl.java:601)
at com.sun.xml.internal.stream.XMLEventReaderImpl.peek(XMLEventReaderImpl.java:276)
at javax.xml.stream.util.EventReaderDelegate.peek(EventReaderDelegate.java:104)
at org.hibernate.boot.jaxb.internal.stax.BufferedXMLEventReader.peek(BufferedXMLEventReader.java:96)
at org.hibernate.boot.jaxb.internal.AbstractBinder.seekRootElementStartEvent(AbstractBinder.java:137)
... 12 more
It looks like your mixing up primary, foreign and composite keys a bit.
In your teacher mapping you declare the columns id and name as a composite key for the teacher entity. I'd call this odd, as you do have a numerical value "id" that might itself be unique. Is there any reason you don't use teacherID as primary key on teacher?
In the same mapping you declare a set of subjects that use the integer "forevenid" as a foreign key to store which teacher teaches that subject.
The exception tells you now, that the field "forevenid" can not refer to a composite key - you can't store in the int s1.forevenid that "101, Jalaj" teaches it.
Changing the composite key on teacher to a simple primary key should solve your problem (alternatively you'd have to make forevenid a composite foreign key...). If this doesn't help, just try to stick to a basic example from the hibernate documentation for set mappings.

Delete entity with Composite Key by Primary Key

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);

Criteria query on part of a composite key

We recently ran across a bug in our software due to a missing #Id annotation:
#Entity
#Table (name ="PATRONQRSPLANS")
//#IdClass(PatronPlan.class) <-- this was missing
public class Balance {
#Transient
private String kind;
#Transient
private String planName;
#Transient
private PlanCategory planCategory;
#Id
#Column(name="PATRONID")
private int patronId;
//#Id <--- and this was missing
#Column(name="PLANID")
private int planId;
#Column(name="BALANCE")
private int balance;
#Column(name="ENDDATE")
private Date expirationDate;
public Balance() {
this.kind = "balance";
}
public Balance(int balance, int planId, Date expirationDate) {
this.balance = balance;
this.planId = planId;
this.expirationDate = expirationDate;
this.kind = "balance";
}
public int getPatronId() {
return patronId;
}
public void setPatronId(int patronId) {
this.patronId = patronId;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
public int getPlanId() {
return planId;
}
public void setPlanId(int planId) {
this.planId = planId;
}
public String getPlanName() {
return planName;
}
public void setPlanName(String planName) {
this.planName = planName;
}
public String getKind() {
return kind;
}
public void setKind(String kind) {
this.kind = kind;
}
public Date getExpirationDate() {
return expirationDate;
}
public void setExpirationDate(Date expirationDate) {
this.expirationDate = expirationDate;
}
public PlanCategory getPlanCategory() {
return planCategory;
}
public void setPlanCategory(PlanCategory planCategory) {
this.planCategory = planCategory;
}
}
The problem is that the table has a primary key constraint on both planId and patronId, so I need a composite key. The query below (without the commented out annotations above), for a patron that has 2 different plans, will return 2 copies of the same plan instead of 2 different ones.
public List<Balance> getBalancesByPatronId(int patronId) {
CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
CriteriaQuery<Balance> query = builder.createQuery(Balance.class);
Root<Balance> s = query.from(Balance.class);
query.select(s);
query.where(builder.equal(s.get("patronId"), patronId));
return entityManager.createQuery(query).getResultList();
}
To remedy this, I added an #Id and #IdClass annotation as commented out above, as well as creating this class:
public class PatronPlan implements Serializable {
private static final long serialVersionUID = -3518083815234439123L;
#Id
#Column(name="PATRONID")
private int patronId;
#Id
#Column(name="PLANID")
private int planId;
public int getPatronId() {
return patronId;
}
public void setPatronId(int patronId) {
this.patronId = patronId;
}
public int getPlanId() {
return planId;
}
public void setPlanId(int planId) {
this.planId = planId;
}
#Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (!this.getClass().isAssignableFrom(obj.getClass())) return false;
PatronPlan other = (PatronPlan) obj;
return Objects.equals(patronId, other.getPatronId()) && Objects.equals(planId, other.getPlanId());
}
#Override
public int hashCode() {
return Objects.hash(patronId, planId);
}
}
But now I get a NullPointerException in my critera query on the statement
s.get("patronId"), because patronId is not showing up as a declaredAttribute, though it does seem to be showing up in the id information.
Is my composite key setup correct and how to I query for part of a composite key using the criteria api?
If it wasn't clear above, the goal is to be able to get all the Balance objects with a given patronId, even though patronId is only part of the composite key.
I am not certain if this is correct, but it seems to be working. Is this correct? My knowledge of hibernate is limited.
public List<Balance> getBalancesByPatronId(int patronId) {
CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
CriteriaQuery<Balance> query = builder.createQuery(Balance.class);
Metamodel metaModel = getEntityManager().getMetamodel();
SingularAttribute<Balance, Integer> patronIdAttr =
(SingularAttribute<Balance, Integer>) metaModel.entity(Balance.class)
.getIdClassAttributes().toArray()[0];
Root<Balance> s = query.from(Balance.class);
query.select(s);
query.where(builder.equal(s.get(patronIdAttr), patronId));
return entityManager.createQuery(query).getResultList();
}

Categories