Hibernate define #Where annotation in super class - java

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")
?

Related

Unable to locate Attribute with the given name - Spring Data JPA Projections, Hibernate and BaseEntity

I am mapping Entities in Hibernate with JPA and Spring Data and when I run application I get
Caused by: java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [id] on this ManagedType [p.s.t..entity.BaseEntity]
at org.hibernate.metamodel.internal.AbstractManagedType.checkNotNull(AbstractManagedType.java:128) ~[hibernate-core-5.3.12.Final.jar:5.3.12.Final]
at org.hibernate.metamodel.internal.AbstractManagedType.getAttribute(AbstractManagedType.java:113) ~[hibernate-core-5.3.12.Final.jar:5.3.12.Final]
at org.hibernate.metamodel.internal.AbstractManagedType.getAttribute(AbstractManagedType.java:111) ~[hibernate-core-5.3.12.Final.jar:5.3.12.Final]
at org.springframework.data.jpa.repository.query.QueryUtils.toExpressionRecursively(QueryUtils.java:633) ~[spring-data-jpa-2.1.11.RELEASE.jar:2.1.11.RELEASE]
at org.springframework.data.jpa.repository.query.JpaQueryCreator.complete(JpaQueryCreator.java:175) ~[spring-data-jpa-2.1.11.RELEASE.jar:2.1.11.RELEASE]
I have a superclass BaseEntity:
#MappedSuperclass
#Getter
#Setter
public abstract class BaseEntity implements Serializable {
#Id
#GeneratedValue
private Long Id;
private String uuid = UUID.randomUUID().toString();
#Override
public boolean equals(Object that) {
return this == that ||
that instanceof BaseEntity && Objects.equals(uuid, ((BaseEntity) that).uuid);
}
#Override
public int hashCode() {
return Objects.hash(uuid);
}
}
Regular class Task, which extends the BaseClass
#Getter
#Setter
#Table(name = "task")
#Entity
#NoArgsConstructor
#NamedEntityGraph(
name = "Task.detail",
attributeNodes = {
#NamedAttributeNode("attachments"),
#NamedAttributeNode("tags")
}
)
public class Task extends BaseEntity {
private String title;
private String description;
private LocalDateTime createdAt;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "task_id")
private Set<Attachment> attachments = new HashSet<>();
#ManyToMany
#JoinTable(
name = "tags_tasks",
joinColumns = #JoinColumn(name = "task_id"),
inverseJoinColumns = #JoinColumn(name = "tag_id")
)
private Set<Tag> tags = new HashSet<>();
public Task(String title, String description, LocalDateTime createdAt) {
this.title = title;
this.description = description;
this.createdAt = createdAt;
}
public void addAttachment(String filename, String comment) {
attachments.add(new Attachment(filename, comment));
}
public Set<Attachment> getAttachments() {
return attachments;
}
public void addTag(Tag tag) {
tags.add(tag);
}
public void removeTag(Tag tag) {
tags.remove(tag);
}
}
TaskView for JPA query projection:
public interface TaskView {
Long getId();
String getUuid();
String getTitle();
String getDescription();
LocalDateTime getCreatedAt();
}
And JpaRepository interface:
interface TasksCrudRepository extends JpaRepository<Task, Long> {
#EntityGraph(value = "Task.detail", type = EntityGraphType.LOAD)
List<Task> findAll();
List<TaskView> findAllProjectedBy();
}
The last method - findAllProjectedBy() - in the TaskCrudRepository causes the exception pasted at the begnining of this post.
When I remove getId() method from TaskView it starts, but then I am not able to display the id of the Task in the projection.
So the question is what I am missing in this whole classes structure?
I am using:
Spring Boot 2.1.9.RELEASE
Java 11
Hibernate Core 5.3.12.FINAL
JPA 2.2
There is a typo in BaseEntity when defining ID field. Should be camelcase id instead of Id.
#MappedSuperclass
#Getter
#Setter
public abstract class BaseEntity implements Serializable {
#Id
#GeneratedValue
private Long id;
private String uuid = UUID.randomUUID().toString();
#Override
public boolean equals(Object that) {
return this == that ||
that instanceof BaseEntity && Objects.equals(uuid, ((BaseEntity) that).uuid);
}
#Override
public int hashCode() {
return Objects.hash(uuid);
}
}

Hibernate one-to-many relationship java.sql.SQLIntegrityConstraintViolationException: Column 'person_id' cannot be null

I'm new to hibernate, learn doc save persistent object
followed hibernate doc this is person and phone relationship one-to-many
#Entity
#Table(name = "phone")
public class Phone {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "number")
private String number;
#ManyToOne(fetch = FetchType.LAZY)
private Person person;
//omit setter and getter
}
#Entity
#Table(name = "person")
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String username;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "person")
private List<Phone> phones = new ArrayList<>();
//omit getter and setter
}
I'm persistent person and add one phone the error be throw
#Test
public void say() {
Person person = new Person();
person.setUsername("aaaa");
Phone phone = new Phone();
phone.setNumber("111");
person.getPhones().add(phone);
personService.save(person);
}
this is Dao persistent
public class PersonDaoImpl implements PersonDao {
#PersistenceContext
private EntityManager entityManager;
#Override
public void save(Person person) {
entityManager.persist(person);
}
Update service code, service just save person
#Service(value = "personService")
public class PersonServiceImpl implements PersonService {
#Autowired
private PersonDao personDao;
#Transactional
#Override
public void save(Person person) {
personDao.save(person);
}
}
error info:
23:35:47.059 [main] DEBUG org.hibernate.engine.spi.ActionQueue - Executing identity-insert immediately
23:35:47.062 [main] DEBUG org.hibernate.SQL -
insert
into
phone
(number, person_id)
values
(?, ?)
23:35:47.297 [main] DEBUG org.hibernate.engine.jdbc.spi.SqlExceptionHelper - could not execute statement [n/a]
java.sql.SQLIntegrityConstraintViolationException: Column 'person_id' cannot be null
Add the #GeneratedValue annotation to specify that the primary key for both entities will be populated outside of your code.
#Entity
#Table(name = "phone")
public class Phone {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#Column(name = "number")
private String number;
#JoinColumn("person_id")
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Person person;
//omit setter and getter
}
public class Person {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
private String username;
#OneToMany(mappedBy = "person")
private List<Phone> phones = new ArrayList<>();
//omit getter and setter
}
Additionally, you need to persist the Person object instead of the Phone object because there is no cascade configured from Phone to Person. If you can't do that, switch the CascadeType on Person to none and put the cascade on the Phone as shown above.
You should also add a #JoinColumn annotation on the Phone entity so hibernate is aware of the foreign key column.
You Missed something. You can try with this.
Person Entity
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String username;
#OneToMany(mappedBy = "person")
private List<Phone> phones = new ArrayList<>();
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public List<Phone> getPhones() {
return phones;
}
public void setPhones(List<Phone> phones) {
this.phones = phones;
}
//omit getter and setter
}
Phone Entity
#Entity
#Table(name = "phone")
public class Phone {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "number")
private String number;
#ManyToOne(cascade = CascadeType.PERSIST)
private Person person;
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
//ommit setter and getter
}
Phone Dao
public interface PhoneDao {
public Phone save(Phone phone);
}
PhoneDaoImpl
#Repository
public class PhoneDaoImpl implements PhoneDao {
#PersistenceContext
private EntityManager entityManager;
#Override
public Phone save(Phone phone) {
return entityManager.merge(phone);
}
}
PersonDaoImpl
#Repository
public class PersonDaoImpl implements PersonDao{
#PersistenceContext
private EntityManager entityManager;
#Override
public Person save(Person person) {
return entityManager.merge(person);
}
}
Test Method
#Test
#Transactional
#Commit
public void say()
{
Phone phone = new Phone();
phone.setNumber("jghjkhk");
Person person = new Person();
person.setUsername("7576");
phone.setPerson(person);
Phone pers = phoneDao.save(phone);
Assert.assertNotNull(pers);
}
Try now. It will work.
I think that you need to set the value of the person->id and then also use an getter method to pass the id to your phone object instead of passing the person object
Normally people have hibernate set the id of an entity automatically with a surrogate key.
public class Person {
#Id #GeneratedValue // should pick an appropriate strategy here
private long id;
Since you don't have that you must either add it or set it yourself.
Person p = new Person();
p.setId(1); // hopefully unique
The same goes for phone.
As you are not having any generation type on your #Id and id is the primary key which can not be null so either you have to set value of id or have #GeneratedValue annotation on your id field and set strategy either as Auto or Identity.
You can also have your own sequence generation.
Also, you need to do same for the Phone class.

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

java.lang.IllegalArgumentException: expecting IdClass mapping

I have configured composite primary key for my entity Employee as follows
Employee.java:
#Entity
#Table(name="employee")
#Proxy(lazy=false)
#IdClass(EmployeeId.class)
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private EmployeeId employeeId;
private Person person;
private Branch branch;
private boolean isActive;
public Employee() {
}
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name="person", column = #Column(name="person_id")),
#AttributeOverride(name="branch", column = #Column(name="branch_id"))})
public EmployeeId getEmployeeId() {
return employeeId;
}
public void setEmployeeId(EmployeeId employeeId) {
this.employeeId = employeeId;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="person_id")
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="branch_id")
public Branch getBranch() {
return branch;
}
public void setBranch(Branch branch) {
this.branch = branch;
}
#Column(name="is_active")
public boolean getIsActive() {
return isActive;
}
public void setIsActive(boolean isActive) {
this.isActive = isActive;
}
}
EmployeeId.java:
#Embeddable
public class EmployeeId implements Serializable {
private static final long serialVersionUID = 1L;
private Person person;
private Branch branch;
public EmployeeId() {
}
public EmployeeId(Person argPerson, Branch argbranch) {
this.person = argPerson;
this.branch = argbranch;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="person_id", insertable=false, updatable=false)
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="branch_id", insertable=false, updatable=false)
public Branch getBranch() {
return branch;
}
public void setBranch(Branch branch) {
this.branch = branch;
}
}
I created a SessionFactory bean using class org.springframework.orm.hibernate5.LocalSessionFactoryBean and mapped all hbm.xml as a MappingLocations.
My code throws the following error:
Caused by: java.lang.IllegalArgumentException: expecting IdClass mapping
at org.hibernate.metamodel.internal.AttributeFactory$3.resolveMember(AttributeFactory.java:971)
at org.hibernate.metamodel.internal.AttributeFactory$5.resolveMember(AttributeFactory.java:1029)
at org.hibernate.metamodel.internal.AttributeFactory.determineAttributeMetadata(AttributeFactory.java:451)
at org.hibernate.metamodel.internal.AttributeFactory.buildIdAttribute(AttributeFactory.java:128)
at org.hibernate.metamodel.internal.MetadataContext.buildIdClassAttributes(MetadataContext.java:337)
at org.hibernate.metamodel.internal.MetadataContext.applyIdMetadata(MetadataContext.java:269)
at org.hibernate.metamodel.internal.MetadataContext.wrapUp(MetadataContext.java:190)
at org.hibernate.metamodel.internal.MetamodelImpl.initialize(MetamodelImpl.java:219)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:296)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:476)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:707)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:723)
at org.springframework.orm.hibernate5.LocalSessionFactoryBean.buildSessionFactory(LocalSessionFactoryBean.java:504)
at org.springframework.orm.hibernate5.LocalSessionFactoryBean.afterPropertiesSet(LocalSessionFactoryBean.java:488)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFac
How can I avoid this error? I am using spring-orm-4.3.1-RELEASE and hibernate-core-5.2.0.Final.
Update
I have created a sample project and I am getting the following error while running...
Caused by: org.hibernate.AnnotationException: Property of #IdClass not found in entity sample.domain.Employee: employee
Refer the code: https://www.dropbox.com/s/axr8l01iqh0qr29/idclass-using-hibernate5.tar.gz?dl=0
What I did wrong? Kindly provide your inputs here
Your situation corresponds to the chapter 2.4.1 Primary Keys Corresponding to Derived Identities of the JPA 2.1 Specification.
The identity of Employee is derived from identities of Person and Branch. You haven't shown the code of either of them, so I'll assume they have simple primary keys. In that relationship, Person and Branch are "parent entities" and Employee is a "dependant" entity.
The ID of Employee may be mapped using either IdClass or EmbeddedId, not both at the same time.
See chapter 2.4.1.1 Specification of Derived Identities.
If you want to use IdClass, then:
The names of the attributes of the id class and the Id attributes of the dependent entity class must correspond as follows:
The Id attribute in the entity class and the corresponding attribute in the id class must have the same name.
...
If an Id attribute in the entity is a many-to-one or one-to-one relationship to a parent entity, the corresponding attribute in the id class must be of (...) the type of the Id attribute of the parent entity.
So your classes would look like this (getters, setters, superfluous annotations etc. omitted)
#Entity
#IdClass(EmployeeId.class)
public class Employee {
#Id
#ManyToOne
private Person person;
#Id
#ManyToOne
private Branch branch;
}
public class EmployeeId {
private Long person; // Corresponds to the type of Person ID, name matches the name of Employee.person
private Long branch; // Corresponds to the type of Branch ID, name matches the name of Employee.branch
}
If you use EmbeddedId, then:
If the dependent entity uses an embedded id to represent its primary key, the attribute in the embedded id corresponding to the relationship attribute must be of the same type as the primary key of the parent entity and must be designated by the MapsId annotation applied to the relationship attribute. The value element of the MapsId annotation must be used to specify the name of the attribute within the embedded id to which the relationship attribute corresponds.
And the code would look like this:
#Entity
public class Employee {
#EmbeddedId
private EmployeeId id;
#ManyToOne
#MapsId("personId") // Corresponds to the name of EmployeeId.personId
private Person person;
#ManyToOne
#MapsId("branchId") // Corresponds to the name of EmployeeId.branchId
private Branch branch;
}
#Embeddable
public class EmployeeId {
private Long personId; // Corresponds to the type of Person ID
private Long branchId; // Corresponds to the type of Branch ID
}
A composite key mapping can be either done with an IdClass or an Embeddable. If you want to use an IdClass you have to annotate your fields in Employee with #Id.
#IdClass(EmployeeId.class)
class Person{
#Id
private Person person;
#Id
private Branch branch;
}
If you want to use an Embedded as a composite key please remove the #IdClass(EmployeeId.class) annotation from Person. You also don't need the person and branch field in your Person class because those are defined in your Embedded class.
Change to:
#Entity
#Table(name = "employee")
#Proxy(lazy = false)
#IdClass(EmployeeId.class)
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private EmployeeId id;
private Person person;
private Branch branch;
private boolean isActive;
public Employee() {
}
#EmbeddedId
#AttributeOverrides({#AttributeOverride(name = "person", column = #Column(name = "person_id") ),
#AttributeOverride(name = "branch", column = #Column(name = "branch_id") )})
public EmployeeId getId() {
return id;
}
public void setId(EmployeeId id) {
this.id = id;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "person_id")
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "branch_id")
public Branch getBranch() {
return branch;
}
public void setBranch(Branch branch) {
this.branch = branch;
}
#Column(name = "is_active")
public boolean getIsActive() {
return isActive;
}
public void setIsActive(boolean isActive) {
this.isActive = isActive;
}
}
The IdClass shouldnt be defined as Embeddable -
#Entity
#Table(name="employee")
#IdClass(EmployeeId.class)
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#ManyToOne
private Person person;
#Id
#ManyToOne
private Branch branch;
private boolean isActive;
public Employee() { }
//....
}
And -
public class EmployeeId implements Serializable {
private static final long serialVersionUID = 1L;
private Person person;
private Branch branch;
public EmployeeId() {}
public EmployeeId(Person argPerson, Branch argbranch) {
this.person = argPerson;
this.branch = argbranch;
}
}
Read your comment - Can I make a suggestion that you map Employee to person_id and branch_id, and not the JPA objects Person and Branch? This will let us test if your hbm config is correct. Id also suggest posting your hbm config as I think there is information missing from this problem
So the table will be similar to -
#Entity
#Table(name="employee")
#IdClass(EmployeeId.class)
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private Long personId;
#Id
private Long branchId;
private boolean isActive;
public Employee() { }
//....
}
And -
And -
public class EmployeeId implements Serializable {
private static final long serialVersionUID = 1L;
private Long personId;
private Long branchId;
public EmployeeId() {}
public EmployeeId(Person argPerson, Branch argbranch) {
this.person = argPerson;
this.branch = argbranch;
}
}
This link could help you
JPA - EmbeddedId with #ManytoOne
Relationship mappings defined within an embedded id class are not supported.Then you need to change the embeddedId class like this
#Embeddable
public class EmployeeId implements Serializable {
private static final long serialVersionUID = 1L;
private Long personId;
private Long branchId;
public EmployeeId() {
}
public EmployeeId(Long argPerson, Long argbranch) {
this.personId = argPerson;
this.branchId = argbranch;
}
#Column(name = "person_id")
public Long getPersonId() {
return personId;
}
public void setPersonId(Long personId) {
this.personId = personId;
}
#Column(name = "branch_id")
public Long getBranchId() {
return branchId;
}
public void setBranchId(Long branchId) {
this.branchId = branchId;
}
}
JPA Composite Primary Key
Specifies a composite primary key class that is mapped to multiple fields or properties of the entity.
The names of the fields or properties in the primary key class and the
primary key fields or properties of the entity must correspond and
their types must be the same.
The answer is in here. read description for you. enter link description here
(Sample code)
#Entity
#Table(name = "EMP_PROJECT")
#IdClass(ProjectAssignmentId.class)
public class ProjectAssignment {
#Id
#Column(name = "EMP_ID", insertable = false, updatable = false)
private int empId;
#Id
#Column(name = "PROJECT_ID", insertable = false, updatable = false)
private int projectId;
#ManyToOne
#JoinColumn(name = "EMP_ID")
Professor employee;
#ManyToOne
#JoinColumn(name = "PROJECT_ID")
Project project;
....
}
public class ProjectAssignmentId implements Serializable {
private int empId;
private int projectId;
...
}
Mention #IdClass annotation with the class which holds the ID.
Check the answer at this post

ManyToMany with more columns in the relational table

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.

Categories