Hibernate having issue with generating sequence value with Postgres - java

I am using hibernate entity and a sequence generator with it.
Database is Postgres and hibernate version is - 5.4.24.Final.
I have a table X with a sequence "seq_x" and below is the hibernate model for it.
#Audited
#Entity
#Table(name = "x")
public class XModel {
private Long id;
private String strCol;
#Id
#Column(name = "id_gen", columnDefinition = "serial")
#GeneratedValue(strategy= GenerationType.IDENTITY)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name = "str_col")
public String getStrCol() {
return strCol;
}
public void setStrCol(String strCol) {
this.strCol = strCol;
}
}
Now the problem occurs when inserting a value in table X using hibernate model.
Sometimes the insertion fails as i have not explicitly specified the sequence name "seq_x" using #GenericGenerator and hibernate was looking for a default sequence name "x_id_gen_seq". That seems to be a valid behaviour. In this case i see the below query in logs throwing exception:
select currval('x_id_gen_seq')
Now Sometimes, the insertion works and in logs i see
"2022-06-13 11:44:25,431 DEBUG [org.hibernate.id.IdentifierGeneratorHelper] (default task-116) Natively generated identity: 523"
Now, i am literally confused what could be the reason behind it. I tried going through the hibernate source code but could not find much.

Related

Spring Data JDBC - Many-to-One Relationship

I can't seem to find any reference online with regards to using a Many-To-One mapping in Spring JDBC. I just saw in the documentation that is not supported but I'm not sure if this is the case.
My example is that I want to map my AppUser to a particular Department.
For reference, AppUser joins to Department table using DEPARTMENT_ID
#Table(value="m_appuser")
public class AppUserProjectionTwo {
#Id
private Long id;
private String firstname;
private String middlename;
private String lastname;
#Column("DEPARTMENT_ID")
private DepartmentProjection departmenProjection;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
However, it seems that it won't map properly.
#Table("M_DEPARTMENT")
public class DepartmentProjection {
#Id
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
The created query looks like this. I was looking for something more of the opposite in which M_APPUSER.department_ID = Department.id
[SELECT "m_appuser"."ID" AS "ID", "m_appuser"."LASTNAME" AS "LASTNAME", "m_appuser"."FIRSTNAME" AS "FIRSTNAME", "m_appuser"."MIDDLENAME" AS "MIDDLENAME", "departmenProjection"."ID" AS "DEPARTMENPROJECTION_ID" FROM "m_appuser" LEFT OUTER JOIN "M_DEPARTMENT" AS "departmenProjection" ON "departmenProjection"."DEPARTMENT_ID" = "m_appuser"."ID" WHERE "m_appuser"."FIRSTNAME" = ?];
Thanks
I just saw in the documentation that is not supported but I'm not sure if this is the case.
I can confirm it is not supported.
Many-To-One relationships cross the boundaries of aggregates.
References across aggregates must be modelled as ids of the referenced aggregate.
If you don't do this Spring Data JDBC will consider the reference a One-To-One relationship and part of the same aggregate which will have effects you don't want for a Many-To-One relationship, like the referenced entity getting deleted when the referenced entity gets deleted. Which would be correct for a One-To-One relationship within the same aggregate.
This is explained in more detail in https://spring.io/blog/2018/09/24/spring-data-jdbc-references-and-aggregates

JPA overwrite record with auto_increment primary key

I've searched but nothing found for me. I have an entity class like this belove:
#Entity
public class Report {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "descrizione")
private String descrizione = null;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDescrizione() {
return descrizione;
}
public void setDescrizione(String descrizione) {
this.descrizione = descrizione;
}
}
And a table into mysql db with auto_increment pk. But I don't know why the auto_increment works only when I start the web service. Later, hibernate just overwrite the last record without create a new one with the auto incremented primary key.
Into the application.properties I have this configuration:
spring.jpa.hibernate.ddl-auto=none
spring.datasource.url=jdbc:mysql://localhost:3306/civicsense?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=AAAAAAA
Some help will be appreciated
First, change your int to Long. And you can omit the strategy, since for MySQL GenerationType.IDENTITY is the same as GenerationType.AUTO which is equivalent to just add #GeneratedValue.
#Id #GeneratedValue
private Long id;
Also, your problem might be the way you are adding your entity. You might want to use saveAndFlush() in this case, since changes will be flushed to DB immediately in this command, which might be prevent your issue, because your actual method might not being committing on time.
You need to change:
#GeneratedValue(strategy = GenerationType.IDENTITY)
for
#GeneratedValue(strategy = GenerationType.AUTO)
and in the application.properties use:
spring.jpa.hibernate.ddl-auto=update

Is it possible to still use Hibernate criteria, if changing the hbm.xml files by removing entire mapping relationships?

I've been working on a project which is now in production mode. Now I've been told to remove the mappings completely from the .hbm.xml files so that I need to handle every relationships manually in the program code. It's really a big problem coz the every DB operation which I've written is in Hibernate Criteria.
Just consider the following Criteria
Criteria criteria = getSession().createCriteria(Table1.class,"table1");
criteria.createAlias("table1.table2", "table2")
.createAlias("table1.table3", "table3")
.createAlias("table3.table4", "table4")
.createAlias("table3.table5", "table5")
.setProjection(Projections.projectionList()
.add(Projections.property("id"),"id")
.add(Projections.property("c1"),"c1")
.add(Projections.property("c2"),"c2")
.add(Projections.property("c3"),"c3")
.add(Projections.property("table2.c1"),"table2.c1")
.add(Projections.property("table2.c2"),"table2.c2")
.add(Projections.property("table3.c1"),"table3.c1")
.add(Projections.property("table5.c1"),"table3.table5.c1"))
.add(Restrictions.eq("table4.c1", Constants.STATUS_ENABLED))
.setResultTransformer(new AliasToBeanNestedResultTransformer(Table1.class));
return criteria.list();
This is the criteria which is written when all the relationships are present in .hbm.xml files. Now you can understand what will be problem I'm going to face when removing the mapping from .hbm.xml files. TBH, I've to rework entire DAO classes by removing Criteria and replacing it with HQL. Also I'll not be able to fetch the result directly as object by using HQL.
Is it possible to only make small changes to the criteria (like defining the join between tables in the criteria itself) so that I'll get the same output even after removing the mappings from .hbm.xml files..?
Yes you can use the Java Persistence Annotations in the Entity classes and will work the same way the .hbm.xml classes do.
Take this for example
#Entity
public class Employee {
#SequenceGenerator(name="EMPLOYEE_SEQ", sequenceName="EMPLOYEE_SEQ", initialValue=1, allocationSize=1)
#Id #GeneratedValue(strategy=GenerationType.AUTO, generator="EMPLOYEE_SEQ")
private int id;
#Column(nullable = false, length = 50)
private String name;
#ManyToOne(targetEntity = Country.class, optional = true, fetch = FetchType.LAZY)
#JoinColumn(name = "loanID", updatable = false, insertable = false)
private Loan loan;
#Column(name = "loanID", updatable = true, insertable = true)
private Integer loanID;
public int getId() {
return id;
}
public void setCompanyID(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getLoanD() {
return loanID;
}
public void getLoanD(Integer loanID) {
this.loanID = loanID;
}
}
And then you just use the criterias like you used to.

Why do the Oracle hibernate dialects produce invalid schemas?

In my project, I need to support multiple databases from multiple vendors (notably, Oracle, MSSQL and MySQL).
I have a class which creates .sql files for each DB type based on my annotated hibernate model classes.
I have a class MyEntity.java like this:
#Entity
#Table(name = "my_table")
public class MyEntity implements java.io.Serializable {
private Integer id;
private Date timestamp;
private String data;
public MyEntity() {
}
#Id
#SequenceGenerator(name = "my_table_seq", sequenceName = "my_table_seq")
#GeneratedValue(strategy = GenerationType.AUTO, generator = "my_table_seq")
#Column(name = "id", unique = true, nullable = false)
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "timestamp", nullable = false)
public Date getTimestamp() {
return this.timestamp;
}
public void setTimestamp(Date timestamp) {
this.timestamp = timestamp;
}
#Column(name = "data", length = 65535)
public String getData() {
return this.data;
}
public void setData(String data) {
this.data = data;
}
}
This class was produced by hbm2java using reverse engineering from a MySQL database. The "data" column was a TEXT and the "timestamp" column was a TIMESTAMP.
When I use the SchemaExporter class to generate SQL for other DB types (like oracle for example) it produces invalid plsql. This class (using the Oracle10gDialect produces oracle sql like this:
create table my_table (
id integer not null,
data varchar(65535),
timestamp datetime not null
);
varchar(65535) is invalid because varchars can't be that long, and datetime is apparently not a real datatype.
When using the MySQL dialect, it correctly defines "data" as a MEDIUMTEXT type.
Why does it generate invalid oracle SQL? Is there some way I can hint to hibernate that it should use a text type? Or am I going to have to extend the oracle dialect to fix up all the data types?
Note: I'm using oracle 11.2.0.2.v7 and hibernate 4.2.8.FINAL
Another Note: Similar invalid results are produced for MSSQL (varchar(65535) is invalid).
Disclaimer: I'm new to Oracle

How to add a JPA relationship against legacy database

I'm coming from a C# entity framework background and looking at JPA in a Java project so I'm hoping that what I'm facing is just a conceptual problem.
I've got a legacy database that I can't alter the schema of and I need to write a DAL.
I've generated (simplified for the example) the following entities...
#Entity
#Table(name = "crag", catalog = "rad_dbo")
public class CragEntity {
private int id;
#Column(name = "id")
#Id
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
private int fkSubRegionId;
#Column(name = "fk_subRegionId")
#Basic
public int getFkSubRegionId() {
return fkSubRegionId;
}
public void setFkSubRegionId(int fkSubRegionId) {
this.fkSubRegionId = fkSubRegionId;
}
}
and
#Table(name = "subRegion", catalog = "rad_dbo")
#Entity
public class SubRegionEntity {
private int id;
#Column(name = "id")
#Id
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
I've tried adding a relationship to CragEntity so that I can access its subRegion
#ManyToOne
#JoinColumn(name="fk_SubRegionId",nullable=false)
private SubRegionEntity subRegion;
but when I try to run
select c from CragEntity c where c.subRegion.region = :area
I get an exception
java.lang.RuntimeException: org.hibernate.QueryException: could
not resolve property: subRegion of: uk.co.bmc.rad.dal.CragEntity
Hopefully this is possible and I'm being slow...
Many thanks in advance for any help!
In your query you are searching for the property "subRegion" though in your entity definition you have the name "fkSubRegionId", so you must change the var name or the query. ;)
EDIT: Sorry i misreaded the relation.
Can you access the property (without making an HQL query) with the relationship inside the code?
Unless, you want to pick only certain fields in your query I would recommend a query like:
from CragEntity c where c.subRegion.region='theRegion'
It turns out there were several issues - one conceptual, one with how IntelliJ had generated a relationship I was copying and one between the chair and keyboard...
IntelliJ had picked the region to subregion relationship with the owner at the "wrong" end - probably a schema issue rather than IntelliJ's fault. Once I realised that and figured out the fix I could copy that to CragEntity and SubRegionEntity
In CragEntity I added:
private SubRegionEntity subRegion;
#ManyToOne
#JoinColumn(name="fk_SubRegionId",nullable=false)
public SubRegionEntity getSubRegion() {
return subRegion;
}
public void setSubRegion(SubRegionEntity subRegion) {
this.subRegion = subRegion;
}
and then in SubRegionEntity I added:
private List<CragEntity> crags;
#OneToMany(mappedBy = "subRegion")
List<CragEntity> getCrags() {
return crags;
}
public void setCrags(List<CragEntity> crags) {
this.crags = crags;
}
Also, it seem that any entity class that is going to be one end of a relationship has to implement serializable (I guess the entities get serialized into the owner. So that needed adding onto SubRegionEntity and RegionEntity
The silliness on my part was of course that the query should have been c.subRegion.region.name otherwise I was comparing an object of type RegionEntity with a string... doh - very stupid mistake on my part.
I'm new to TDD but as always as soon as I wrote tests for what I thought should be happening with the existing code I was walked through my errors (and given google keywords by the exceptions and errors :-))

Categories

Resources