Whats wrong in my jpa mapping, im trying to map the parent class with one primary key to the child class with composite key, but it seems its inserting in the wrong table, it already generate 2 table but unfortunately i didn't bind the foreign key(policy_value_summary_id)
#Entity
#Table(name = "POLICY_VALUE_SUMMARY")
public class PolicyValueSummary {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="POLICY_VALUE_SUMMARY_ID")
private Long policyValueSummaryId;
#MapsId("policyValueSummaryId")
#OneToMany
private Set<PolicyValue> policyValues;
}
the child class have composite keys with one is the parent id.
#Entity
#Table(name = "POLICY_VALUE")
public class PolicyValue {
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "policyValueSummaryId", column = #Column(name = "POLICY_VALUE_SUMMARY_ID")),
#AttributeOverride(name = "planAtrId", column = #Column(name = "PLAN_ATR_ID")) })
private PolicyValuePk policyValuePk;
}
This is my child class composite keys.
#Embeddable
public class PolicyValuePk implements Serializable {
private Long policyValueSummaryId;
private Long planAtrId;
}
Im trying to save the policy summary value(parent) with the policy value(child class) like this
PolicyValuePk pk = new PolicyValuePk();
pk.setPlanAtrId(Long.valueOf("1"));
Set<PolicyValue> policyValues = new HashSet<>();
policyValues.add(new PolicyValue(pk));
PolicyValueSummary summary = new PolicyValueSummary();
summary.setPolicyValues(policyValues);
repo.save(summary);
Here is the error that being output to me
Hibernate: select hibernate_sequence.nextval from dual
Hibernate: insert into policy_value_summary (policy_value_summary_id) values (?)
Hibernate: insert into policy_value_summary_policy_values (policy_value_summary_policy_value_summary_id, policy_values_plan_atr_id, policy_values_policy_value_summary_id) values (?, ?, ?)
WARN 6880 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 972, SQLState: 42000
ERROR 6880 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : ORA-00972: identifier is too long
No, prior to Oracle version 12.2, identifiers are not allowed to exceed 30 characters in length. See the document
http://docs.oracle.com/cd/B28359_01/server.111/b28286/sql_elements008.htm#SQLRF00223
But from from version 12.2 they can be up to 128 bytes long
Related
I'm sitting in this peculiar situation where I have the following database design
What this image illustrates is the following:
Table A has a_c_id which is "C id" but without a foreign key reference
Table B has c_id which is "C id" but without a foreign key reference.
Table C has id as primary key.
I'm trying in my java code to create following entities using hibernate and hibernate annotations
#Entity
#Table(name = "A")
Class A {
private Long id;
private List<B> bList; // This is the relation i'm struggling with
}
#Entity
#Table(name = "B")
Class B {
private Long Id;
private Long c_id;
}
I've tried alot of difference combinations using:
#JoinColumn(name = "a_c_id", referencedColumnName = "c_id")
private List<B> bList;
And:
#JoinTable(
name = "C",
joinColumns = {
#JoinColumn(
name = "id",
referencedColumnName = "a_c_id"
)
},
inverseJoinColumns = {
#JoinColumn(
name = "id",
referencedColumnName = "c_id"
),
}
)
private List<B> bList;
This last example with #JoinTable gives me an exception:
Repeated column in mapping for collection: id
which makes me wonder if mapping to the same PK from 2 different tables is not possible.
But i'm starting to run out of ideas on how to do this. I didn't make this database and is wondering if it's even possible to achieve making a relation from A to B without a foreign key to reference.
I thought I could perhaps use #JoinTable, however they refer to the same ID rather than 2 different ones as you would in a JoinTable.
If anyone has some input on what to read up on, or perhaps could come with an idea on how to achieve this without changing the database design, I'd apppreciate it!
EDIT: I've added images of table records:
EDIT 2: I've tried to trace binding values based on me and Nikos conversation:
Hibernate: select a0_.id as id1_0_0_, a0_.a_c_id as a_c_id2_0_0_ from a a0_ where a0_.id=?
2022-07-27 10:25:06.101 TRACE 16404 --- [nio-8080-exec-8] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [1]
2022-07-27 10:25:06.107 TRACE 16404 --- [nio-8080-exec-8] o.h.type.descriptor.sql.BasicExtractor : extracted value ([a_c_id2_0_0_] : [BIGINT]) - [41]
2022-07-27 10:25:06.113 TRACE 16404 --- [nio-8080-exec-8] org.hibernate.type.CollectionType : Created collection wrapper: [eu.sos.auditing.models.HibernateTestModels.A.bList#1]
Hibernate: select blist0_.c_id as c_id2_3_0_, blist0_.id as id1_3_0_, blist0_.id as id1_3_1_, blist0_.c_id as c_id2_3_1_ from b blist0_ where blist0_.c_id=?
2022-07-27 10:25:06.149 TRACE 16404 --- [nio-8080-exec-8] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [1]
EDIT 3: What solved my issue.
Due to Nikos help I was able to solve my issue by changing my class A to the following:
#Entity
#Table(name = "A")
#Data
public class A implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "a_c_id")
private Long aCId;
#OneToMany
#JoinColumn(name="c_id", referencedColumnName = "a_c_id")
private List<B> bList;
}
Kind regards
The solution can be as straightforward as pretending that B.c_id points to A.a_c_id:
Class A {
#OneToMany
// remember, the JoinColumn is in the other table (here B)
// while the referencedColumnName is in this table
#JoinColumn(name="c_id ", referencedColumnName = "a_c_id")
private List<B> bList;
}
EDIT: It is important to include the referencedColumnName, which I has forgotten before the edit. Also see full solution below for another caveat with Serializable.
If the DB constraints are different, you will have to make sure a C record exists before inserting into A and B.
The other solution is NOT to map the relation in the entity classes. Or, rather, map the existing relations, i.e. any of: (1) "A relates to 1 C", (2) "B relates to 1 C", (3) "C relates to many As", (4) "C relates to many Bs". And fetch the Bs that correspond to an A with an independent query.
FULL WORKING SOLUTION (with slightly different names - omitting getters/setters for brevity)
The entities:
#Entity
#Table(name = "AA")
public class Alpha implements Serializable {
#Id
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "a_c_id")
private Charlie charlie;
#OneToMany
#JoinColumn(name="c_id", referencedColumnName = "a_c_id")
private List<Bravo> bList;
}
CAVEAT: I had to make Alpha implements Serializable because of HHH-7668. It applies only to the Hibernate implementation of JPA.
#Entity
#Table(name = "BB")
public class Bravo {
#Id
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "c_id")
private Charlie charlie;
}
#Entity
#Table(name = "CC")
public class Charlie {
#Id
private Long id;
}
Sample code to read, assuming the contents of the tables are as in the question:
EntityManager em = ...;
Alpha a = em.find(Alpha.class, 2L);
a.getbList().forEach(b -> System.out.println(b.getId()));
// prints 3, 4
I'm working on adding a feature to an already developed spring boot web application. The primary entity that has child entities is a Record. It has a few columns/variables that I want to now be in its own, separate entity (CustomerOrder) and exist in a one-to-one relationship with the Record. To summarize:
Record {
thing 1
thing 2
thing 3
}
is now becoming:
CustomerOrder {
thing 1
thing 2
thing 3
}
Record {
CustomerOrder
}
I'm having some issues with what I've produced. Here is the CustomerOrder model's relevant relationship data:
#Entity
#Table(name="customer_orders")
public class CustomerOrder {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
... other columns
#OneToOne(orphanRemoval = true, cascade = CascadeType.ALL, mappedBy="customerOrder", fetch = FetchType.EAGER)
private Record record;
}
And then here is the Record model's relevant data:
#Entity
#Table(name="records")
public class Record extends Auditable<String> implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
... other columns
#OneToOne
#JoinColumn(name="customer_order_id", nullable = false, unique = true)
private CustomerOrder customerOrder;
}
My issue exists when I try to POST a record, when a user tries creating one in the ui. Here is the POST method for a record:
#PostMapping
public ResponseEntity<?> saveRecord(#RequestBody Record recordBody, BindingResult result) {
if(!result.hasErrors()) {
if(recordBody.getHardwareItems().isEmpty()) {
record = recordsService.save(recordBody);
} else {
// Save the record first, recordId is required on hardwareItems
// TODO: investigate Spring Hibernate/JPA rules - is there a way to save parent before children to avoid a null recordId
CustomerOrder customerOrder = recordBody.getCustomerOrder();
recordBody.setCustomerOrder(new CustomerOrder());
customerOrder.setRecord(record);
customerOrder = customerOrdersService.save(customerOrder);
record = recordsService.save(recordBody);
}
} else {
return new ResponseEntity<>(result.getAllErrors(), HttpStatus.BAD_REQUEST);
}
// Return the location of the created resource
uri = ServletUriComponentsBuilder.fromCurrentRequest().path("/{recordId}").buildAndExpand(record.getId()).toUri();
return new ResponseEntity<>(uri, HttpStatus.CREATED);
}
The error I receive is the following:
2021-02-19 00:46:28.398 WARN 29990 --- [io-8080-exec-10] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1364, SQLState: HY000
2021-02-19 00:46:28.398 ERROR 29990 --- [io-8080-exec-10] o.h.engine.jdbc.spi.SqlExceptionHelper : Field 'record_id' doesn't have a default value
This makes sense to me at least, since I'm trying to save the CustomerOrder object that depends on a Record object, which has yet to have been persisted. So, how do I go about changing up the order and/or creating and persisting a Record object so that I can then save the CustomerOrder object to it?
You need to mark your column record_id as AI(AUTO_INCREMENT) in your table definition.
ALTER TABLE records CHANGE record_id INT(6) NOT NULL AUTO_INCREMENT;
Your primary key is record_id, add #Column(name = "record_id", nullable = false)
#Entity
#Table(name="records")
public class Record extends Auditable<String> implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "record_id", nullable = false)
private Long id;
... other columns
#OneToOne
#JoinColumn(name="customer_order_id", nullable = false, unique = true)
private CustomerOrder customerOrder;
}
I have an entity class which uses auto generated id from database (PostgreSQL). It has been persisting fine without requiring me to specify an id to it. e.g.
#Entity public class MyEntity {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
// ... other columns
}
Now I want to add a List of associated entities owned by this entity class with uni-directional association. e.g.
#Entity public class MyEntity {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToMany(cascade = CascadeType.ALL) #JoinColumn(name="pid")
private List<SubEntity> subEntities;
// ... other columns
}
#Entity public class SubEntity implements Serializable {
#Id private Integer pid; // refer to id of MyEntity
#Id private String name; // pid, name forms a composite key for SubEntity
// ... other columns
}
Then I bumped into an issue that JPA (Hibernate in this case) was generating SQLs like:
INSERT INTO MYENTITY (...) VALUES (...)
INSERT INTO SUBENTITY (pid, ...) VALUES (null, ...)
It failed when trying to insert a null value to pid as it has not null constraint in the database schema. If I bypass this, Hibernate then generates an update statement to update the null value with the generated id from MyEntity:
UPDATE SUBENTITY SET pid = ? WHERE pid = null AND name = ?
I get that the auto generated id is not known until after the insert to MyEntity, so it updates afterward. But I wonder if there is a solution so that Hibernate does the insert to MyEntity ONLY first, get the generated id THEN does the inserts to SubEntity with the correct pid and no update afterward?
This should be possible. Please create an issue in the Hibernate issue tracker with a test case that reproduces this issue. Apart from that, I would suggest you try using a sequence generator as that is more scalable anyway.
I have a very small doubt regarding #OneToMany mapping.
I have a model student and another model attendance.
A student can have multiple attendance. but student model should only be able to retrieve the attendance info.
But when I am trying to change some student info I am getting below error as it is trying to update attendance record.
here is my mapping
#Entity
#Table(name="student_detail")
#Getter #Setter
public class StudentDetailsModel {
#Id
#Column(name="reg_no",updatable = false, nullable = false)
private String regNo;
#OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.ALL })
#JoinColumn(name = "reg_no")
private List<AttendanceModel> attendances;
}
and the exception I a getting.
update
student_detail
set
address=?,
alt_contact_number=?,
blood_group=?,
contact_number=?,
dob=?,
father_name=?,
first_name=?,
gender=?,
last_name=?,
middle_name=?,
mother_name=?,
photo_url=?,
school_id=?
where
reg_no=?
Hibernate:
update
attendance
set
reg_no=null
where
reg_no=?
2019-01-13 12:12:52.922 WARN 10708 --- [nio-8081-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: 23502
2019-01-13 12:12:52.923 ERROR 10708 --- [nio-8081-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: null value in column "reg_no" violates not-null constraint
Detail: Failing row contains (null, 1, 2019-01-05, t, 2).
2019-01-13 12:12:52.926 INFO 10708 --- [nio-8081-exec-1] o.h.e.j.b.internal.AbstractBatchImpl : HHH000010: On release of batch it still contained JDBC statements
org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [reg_no]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
my attenance model is as follows
#Entity
#Table(name="attendance")
#Getter #Setter
public class AttendanceModel {
//#EmbeddedId
//private AttendanceId attendanceId;
#Id
#Column(name="attendance_id")
private long id;
#Column(name="reg_no")
private String regNo;
#Column(name="subject_id")
private long subjectId;
#Column(name="class_date")
private Date classDate;
#Column(name="present")
private boolean present;
}
Could you show me Student Model.If i look your code post : You using Unidirectional relationship.
I think it must :
#OneToMany(fetch = FetchType.LAZY , cascade = CascedeType.ALL)
#JoinColumn(name="attendance_id")
private List<AttendanceModel> attendances = new ArrayList<>();
I am trying to do a one-to-many mapping using hibernate to insert some info into the DB but every time data is getting updated in the table rather than inserting a new row
I have 2 entities: ReportMaster and Reportdetail. where many Reportdetail data contain the ID which is Foreign key mapped to report master primary key column Id
#Entity
#Table(name = "ReportMaster")
public class ReportMaster implements Serializable {
private Integer repId;
private Set<ReportDetail> reportDetails = new HashSet<ReportDetail>();
#Id
#GeneratedValue
#Column(name = "RepId", unique = true, nullable = false)
public Integer getRepId() {
return this.repId;
}
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "reportMaster")
public Set<ReportDetail> getReportDetails() {
return this.reportDetails;
}
2nd entity is:
#Entity
#Table(name = "ReportDetail")
public class ReportDetail implements Serializable {
private String repColumn;
private String colData;
//.......corresponding getters and setters
private ReportMaster reportMaster;
#ManyToOne(targetEntity = ReportMaster.class)
#JoinColumn(name = "RepId")
public ReportMaster getReportMaster() {
return this.reportMaster;
}
I want to insert a new row into the reportdetails table, but it is getting updated:
ReportMaster report=new ReportMaster(req.getReportName(), req.getCid(), req.getReportDesc(), new Date());
report.addDetail(new ReportDetail(repcol,desc);
getTemplate().save(obj);
The generated HQL is:
org.hibernate.SQL - insert into ReportMaster (CreateDate, CustomerID, RepDesc, ReportName) values (?, ?, ?, ?)
org.hibernate.SQL - update ReportDetail set RepId=? where ColData=? and RepColumn=?
2013-02-16 10:13:34,109[http-6060-1] ERROR org.hibernate.event.def.AbstractFlushingEventListener - Could not synchronize database state with session
org.hibernate.jdbc.BatchedTooManyRowsAffectedException: Batch update returned unexpected row count from update [0]; actual row count: 2; expected: 1
at org.hibernate.jdbc.Expectations$BasicExpectation.checkBatched(Expectations.java:71)
at org.hibernate.jdbc.Expectations$BasicExpectation.verifyOutcome(Expectations.java:46)
at org.hibernate.jdbc.NonBatchingBatcher.addToBatch(NonBatchingBatcher.java:24)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2403)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2307)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2607)
at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:92)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:250)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:234)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:142)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
You should set the property 'reportMaster' of ReportDetail like this:
ReportMaster report=new ReportMaster(req.getReportName(), req.getCid(),req.getReportDesc(), new Date());
// create detail and set its master
ReportDetail detail = new ReportDetail(repcol,desc)
detail.setReportMaster(report);
report.addDetail(detail);
getTemplate().save(obj);
Follow this document.
Hope this helps.