ManyToMany with more columns in the relational table - java

I have this scenario:
Table Group
idgroup
groupname
...
Table Interval
idInterval
Description
...
Table group_interval
idInterval
idGroup
active
I had to change the relationship on JPA from manyToMany to manyToOne - OneToMany.
Everything works fine but when I need to insert I have some problems.
First, the exception was that query had idinterval occurred twice, so, couldn't set.
Later, after some changes, I got the exception ... index 8 ... out of bounds, and assume this was relate to setting extra parameters than allowed in the table.
I'm using a mappedSupperClass and extending to classes, and that's the reason I think these errors is relate to extra parameter in the query.
Here's my classes, insert method and stacktrace error.
Base entity
#MappedSuperclass
public abstract class BaseEntity {
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
hashCode, equals and toString
Interval
#Entity
#Table(name = "TUnPbxPauseStatusType")
#Inheritance(strategy = InheritanceType.JOINED)
#AttributeOverride(name = "id", column = #Column(name = "IdStatusType", insertable = false, updatable = false))
public class Interval extends BaseEntity implements Serializable
...
private List<IntervalGroup> listIntervalGroup;
...
#Override
#Id
public Long getId()
{
return super.getId();
}
#Override
public void setId(Long id)
{
super.setId(id);
}
...
#OneToMany(mappedBy = "interval")
public List<IntervalGroup> getListIntervalGroup()
{
return listIntervalGroup;
}
public void setListIntervalGroup(List<IntervalGroup> listIntervalGroup)
{
this.listIntervalGroup = listIntervalGroup;
}
Group
#Entity
#Table(name = "TUnPbxGroup")
#Inheritance(strategy = InheritanceType.JOINED)
#AttributeOverride(name = "id", column = #Column(name = "GroupId", insertable = false, updatable = false))
public class MainGroup extends BaseEntity implements Serializable
...
private List<IntervalGroup> listIntervalGroup;
...
#Override
#Id
public Long getId()
{
return super.getId();
}
#Override
public void setId(Long id)
{
super.setId(id);
}
#OneToMany(mappedBy = "mainGroup")
public List<IntervalGroup> getListIntervalGroup()
{
return listIntervalGroup;
}
public void setListIntervalGroup(List<IntervalGroup> listIntervalGroup)
{
this.listIntervalGroup = listIntervalGroup;
}
IntervalGroup
#Entity
#Table(name = "TUnpbxPauseGroup")
#AttributeOverride(name = "id", column = #Column(name = "fkidstatustype", insertable = false, updatable = false))
#IdClass(IntervalGroup.class)
public class IntervalGroup extends BaseEntity implements Serializable
...
private MainGroup mainGroup;
private Interval interval;
private long idGroup;
#Override
#Id
public Long getId()
{
return super.getId();
}
#Override
public void setId(Long id)
{
super.setId(id);
}
#Id
#Column(name = "fkidGroup", nullable = true)
public long getIdGroup()
{
return idGroup;
}
public void setIdGroup(long idGroup)
{
this.idGroup = idGroup;
}
#ManyToOne(targetEntity = MainGroup.class)
#JoinColumn(name = "fkIdGroup")
public MainGroup getMainGroup()
{
return mainGroup;
}
public void setMainGroup(MainGroup mainGroup)
{
this.mainGroup = mainGroup;
}
#ManyToOne(targetEntity = Interval.class)
#JoinColumn(name = "fkIdStatusType")
public Interval getInterval()
{
return interval;
}
public void setInterval(Interval interval)
{
this.interval = interval;
}
I tried override the id method from supperclass and used transient; Don't use #AttributeOverride in the IntervalGroup class (but i got that the name id doesn't exist);
I tried #Embeddable class, and a lot of another things. Nothing worked.
Maybe not using the BaseEntity class I would solve my problem, but I'd like to use this to use some generics methods.
Here's how I am trying to insert:
IntervalController:
for (IntervalGroup intervalGroups : getItem().getListIntervalGroup())
{
intervalService.save(intervalGroups); //Error happens here.
}
save();//This saves the interval, after save intervalGroup
GenericService
public <T extends BaseEntity> T save(T entity)
{
logger.info("Salvar: " + entity);
if (entity.getId() == null)
{
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
Remembering: Its search works perfectly, the problem happens only when I try to insert.
insert query:
(*fkidstatustype*, icActive, icBlockTime, *fkIdStatusType*, fkIdGroup, nuTime, nuTimeAfterUnlock) values (?, ?, ?, ?, ?, ?, ?)
as shown, the fkidstatustype appears twice.
09:35:46,060 WARN [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] (http--0.0.0.0-8080-5) SQL Error: 0, SQLState: S1093
09:35:46,061 ERROR [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] (http--0.0.0.0-8080-5) The index 8 is out of range.
09:35:46,062 WARN [com.arjuna.ats.arjuna] (http--0.0.0.0-8080-5) ARJUNA012125: TwoPhaseCoordinator.beforeCompletion - failed for SynchronizationImple< 0:ffffac100058:7f5df46c:548ad301:27, org.hibernate.engine.transaction.synchronization.internal.RegisteredSynchronization#1b90c55 >: javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: The index 8 is out of range.

Related

Hibernate define #Where annotation in super class

I am trying to implement soft delete in my spring - hibernate project.
My plan is to override the delete method with the #SQLDelete annotation and filter the logical deleted entities with hibernate #Where annotation in my queries.
I experience some difficulties when I try to define the #Where clause in the super class, It's seems that the entities dont inherit the #Where clause from the abstract base class.
Note: If I move the #Where annotation to the entity class everything work as expected
The base entity class:
#MappedSuperclass
#Where(clause = " IS_DELETED = false")
public abstract class BaseEntity {
#Column(name = "IS_DELETED")
private boolean isDeleted;
public BaseEntity() {
}
public boolean getIsDeleted() {
return this.isDeleted;
}
public void setIsDeleted(boolean isDeleted) {
this.isDeleted = isDeleted;
}
}
The entity class:
#Entity
#Table(name = "Events")
#SQLDelete(sql ="UPDATE events " +
"SET IS_DELETED = true " +
"WHERE id = ?")
public class Event extends BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID")
private Long id;
#Column(name = "NAME")
private String name;
public Event() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Thanks for any kind of help :)
Did you try this?
#Where(clause = "IS_DELETED = 0")
or
#Where(clause = "isDeleted = 0")
?

Could not determine type for: java.util.Set, at table

I create two classes with many to many relation between them, like as below:
#Entity
#Table(name = FoodEntity.TABLE_NAME)
public class FoodEntity extends BaseEntity<Long> {
public static final String TABLE_NAME = "T_FOOD";
#ManyToMany
#JoinTable(
name = "T_FOOD_FOODCATEGORY",
joinColumns = { #JoinColumn(name = "FOOD_ID") },
inverseJoinColumns = { #JoinColumn(name = "FOOD_CATEGORY_ID") })
private Set<FoodCategoryEntity> categories;
public Set<FoodCategoryEntity> getCategories() {
return categories;
}
public void setCategories(Set<FoodCategoryEntity> categories) {
this.categories = categories;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "FOOD_ID", nullable = false)
#Override
public Long getId() {
return id;
}
#Override
public void setId(Long id) {
this.id = id;
}
}
and :
#Entity
#Table(name = FoodCategoryEntity.TABLE_NAME)
public class FoodCategoryEntity extends BaseEntity<Long> {
public static final String TABLE_NAME = "T_FOOD_CATEGORY";
#ManyToMany(mappedBy = "categories")
private Set<FoodEntity> foods;
public Set<FoodEntity> getFoods() {
return foods;
}
public void setFoods(Set<FoodEntity> foods) {
this.foods = foods;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "FOOD_CATEGORY_ID", nullable = false)
#Override
public Long getId() {
return id;
}
#Override
public void setId(Long id) {
this.id = id;
}
}
but when I test this relation with junit,spring and hibernate, I get below exception:
Caused by: org.hibernate.MappingException: Could not determine type for: java.util.Set, at table: T_FOOD, for columns: [org.hibernate.mapping.Column(categories)]
at org.hibernate.mapping.SimpleValue.getType(SimpleValue.java:455)
at org.hibernate.mapping.SimpleValue.isValid(SimpleValue.java:422)
at org.hibernate.mapping.Property.isValid(Property.java:226)
at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:597)
at org.hibernate.mapping.RootClass.validate(RootClass.java:265)
at org.hibernate.boot.internal.MetadataImpl.validate(MetadataImpl.java:329)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:451)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:889)
... 46 more
I checked annotation package references, and all ID column names, but all of them are correct.
point : I also use the HSQLDB database whose storage location it is a file.
I found my problem.
mapping class in persistence.xml was incorrect.
You annotations infer property access type mapping as #Id is placed on getter
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "FOOD_ID", nullable = false)
#Override
public Long getId() {
return id;
}
But collection annotaions are placed on field level
#ManyToMany
#JoinTable(
name = "T_FOOD_FOODCATEGORY",
joinColumns = { #JoinColumn(name = "FOOD_ID") },
inverseJoinColumns = { #JoinColumn(name = "FOOD_CATEGORY_ID") })
private Set<FoodCategoryEntity> categories;
While effective configuration is (not) specified on getter
public Set<FoodCategoryEntity> getCategories() {
return categories;
}
You should not mix two types of configurations.

ERROR: null value in column "master_field_id" violates not-null constraint

I have classes:
#Entity
#Table(schema = "master", name = "master_mapping")
public class MasterMapping extends AuditedEntity {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "master_field_id")
private MasterFields masterField;
#JsonIgnore
public MasterFields getMasterField() {
return masterField;
}
public void setMasterField(MasterFields masterField) {
this.masterField = masterField;
}
}
and
#Entity
#Table(schema = "master", name = "master_fields")
public class MasterFields extends AuditedEntity {
private String label;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "masterField")
private List<MasterMapping> mappedFields;
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public List<MasterMapping> getMappedFields() {
return mappedFields;
}
public void setMappedFields(List<MasterMapping> mappedFields) {
this.mappedFields = mappedFields;
}
}
and common class with id
#MappedSuperclass
public class AuditedEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
I'm trying to save MasterFields object with MasterMapping, but I'm getting error:
Hibernate: insert into master.master_fields (label) values (?)
Hibernate: select currval('master.master_fields_id_seq')
Hibernate: insert into master.master_mapping (master_field_id) values (?)
SQL Error: 0, SQLState: 23502
ERROR: null value in column "master_field_id" violates not-null constraint
Details: Failing row contains (null).
javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute statement
I'm trying to save it to db with EntityManager.merge(). I see that hibernate can't get generated id from MasterFields object. How can fix it?

Save an entity and all its related entities in a single save in spring boot

I'm using Spring Boot,REST and JPA to build my application. In app, there are 2 entities with one to many relationship.
Entity 1 :
#Entity
#Table( name = "report")
#JsonIgnoreProperties(ignoreUnknown = true)
public class CustomReport {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "REPORT_SEQ")
#SequenceGenerator(sequenceName = "REPORT_SEQ", allocationSize = 1, name = "REPORT_SEQ")
private Long id;
private String name;
private Long createdBy;
private Timestamp lastModifiedTimestamp;
#OneToMany(mappedBy = "customReport", cascade = CascadeType.ALL)
private Set<CustomReportActivity> customReportActivitySet;
public Set<CustomReportActivity> getCustomReportActivitySet() {
return customReportActivitySet;
}
public void setCustomReportActivitySet(Set<CustomReportActivity> customReportActivitySet) {
this.customReportActivitySet = customReportActivitySet;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getCreatedBy() {
return createdBy;
}
public void setCreatedBy(Long createdBy) {
this.createdBy = createdBy;
}
public Timestamp getLastModifiedTimestamp() {
return lastModifiedTimestamp;
}
public void setLastModifiedTimestamp(Timestamp lastModifiedTimestamp) {
this.lastModifiedTimestamp = lastModifiedTimestamp;
}
}
Entity 2:
#Entity
#Table( name = "report_activity")
public class CustomReportActivity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "REPORT_ACTIVITY_SEQ")
#SequenceGenerator(sequenceName = "REPORT_ACTIVITY_SEQ", allocationSize = 1, name = "REPORT_ACTIVITY_SEQ")
private Long id;
String activityName;
#ManyToOne
#JoinColumn( name="report_id" )
#JsonBackReference
private CustomReport customReport;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getActivityName() {
return activityName;
}
public void setActivityName(String activityName) {
this.activityName = activityName;
}
public CustomReport getCustomReport() {
return customReport;
}
public void setCustomReport(CustomReport customReport) {
this.customReport = customReport;
}
}
And my request JSON is as follows :
{
"name": "test report",
"createdBy" : 129,
"customReportActivitySet": [
{"activityName":"a"},
{"activityName":"b"},
{"activityName":"c"},
{"activityName":"d"},
{"activityName":"e"}
]
}
I want to save both entities in one shot. I've implemented the save functionality in following way:
#RequestMapping(value="/save", method = RequestMethod.POST)
public ResponseEntity<?> addReport(#RequestBody CustomReport customReport) {
return new ResponseEntity<>(customReportService.createCustomReport(customReport), HttpStatus.CREATED);
}
CustomReportService method:
public CustomReport createCustomReport(CustomReport customReport) {
return customReportRepository.save(customReport);
}
CustomRepository:
public interface CustomReportRepository extends CrudRepository<CustomReport, Long> {
}
But I'm getting the constraint violation exception with this:
java.sql.SQLIntegrityConstraintViolationException: ORA-01400: cannot
insert NULL into ("REPORT_ACTIVITY"."REPORT_ID")
Is it possible to save both entities in one save operation?
Please help!
You would have to add a small piece of code which would populate each CustomReportActivity within the CustomReport instance. Only then the persistence provide can successfully perform the cascade save operation:
public CustomReport createCustomReport(CustomReport customReport) {
customReport.getCustomReportActivitySet.forEach((activity) -> {
activity.setCustomReport(customReport);
});
return customReportRepository.save(customReport);
}
The bottom line is that the dependencies have to be set on both sides of the relationship.
Try this sample, in my case it worked as expected, child entities are saved automatically in a single save operation with creating relations to the parent entity:
#Entity
public class Parent {
#Id
private Long id;
#JoinColumn(name = "parentId")
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Child> children;
}
#Entity
public class Child {
#Id
private Long id;
private Long parentId;
}

Hibernate Criteria: How to retrieve table data with foreign key relationship

I have two pojo classes wihch are named Document and DocumentUser. DocumentUser has an property documentId which linked to Document's id by foreign key.
So i want to create criteria query which retrieve Documents with its DocumentUser which is linked itself by forein key("document_id")
pojo classes:
Document
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#Entity
#Table(name = "DYS_BYS_DOSYA")
#Audited
public class Document implements Serializable {
private Long id;
private String name;
private List<DocumentUser> documentUserList = new ArrayList<DocumentUser>();
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID", nullable = false, precision = 15, scale = 0)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name = "AD", nullable = false, length = 500)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#OneToMany(mappedBy = "document", fetch = FetchType.EAGER)
#Fetch(FetchMode.SUBSELECT)
#Cascade(CascadeType.ALL)
public List<DocumentUser> getDocumentUserList() {
return documentUserList;
}
public void setDocumentUserList(List<DocumentUser> documentUserList) {
this.documentUserList = documentUserList;
}
#Override
public String toString() {
return "tr.com.enlil.dys.server.servis.model.Document[id=" + id + "]";
}
}
DocumentUser:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#Entity
#Table(name = "DYS_DOSYA_SAHIBI_USER")
#Audited
public class DocumentUser implements Serializable {
/**
*
*/
private static final long serialVersionUID = 6393919788296838129L;
private Long id;
private Long personelId;
private Document document;
private String personelName;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID", unique = true, nullable = false, precision = 15, scale = 0)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name = "OLUSTURUCU_PERSONEL_ID")
public Long getPersonelId() {
return personelId;
}
public void setPersonelId(Long personelId) {
this.personelId = personelId;
}
#Column(name = "KULLANICI_AD")
public String getPersonelName() {
return personelName;
}
public void setPersonelName(String personelName) {
this.personelName = personelName;
}
#ManyToOne
#JoinColumn(name = "DOSYA_ID")
public Document getDocument() {
return document;
}
public void setDocument(Document document) {
this.document = document;
}
}
In this way, how can i get Document data depends on personelId of DocumentUser table by using criteria query? I am not familiar with hibernate and i need your helps. I try to write some codes but didn't work.
public List<Document> fetchRecordsByCriteriaLimitedList(String userId) throws Exception{
Criteria criteria = getSessionFactory().getCurrentSession().createCriteria(Dosya.class);
DetachedCriteria dosyaSahibiCriteria = (DetachedCriteria) criteria.createCriteria("documentUserList");
dosyaSahibiCriteria.add(Restrictions.eq("personelId", userId));
dosyaSahibiCriteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
return criteria.list();
}
Several problems with your code. First of all, you said
2)DocumentUser is subclass of Document
This isn't true, judging from your code (it would mean that DocumentUser extends Document), but you probably meant they are in a parent -> child relation. Second, in documentUserList mapping, there is this #OneToMany(mappedBy = "dosya", fetch = FetchType.EAGER), which means there is a field named dosya in DocumentUser, and there isn't. Instead, replace it with mappedBy = "document". Assuming everything else is ok, query to get all documents based on their DocumentUser's id would be
public List<Document> fetchRecordsByCriteriaLimitedList(String userId) throws Exception{
Criteria criteria = getSessionFactory().getCurrentSession().createCriteria(Document.class);
criteria.createAlias("documentUserList", "users").add(Restrictions.eq("users.personelId", userId));
return criteria.list();
}

Categories