JPA Hibernate Problem for One to One Relationship with Embedded ID - spring-boot

I am struggling with the following problem that I've been trying to solve. After checking solutions on StackOverflow and articles on Baeldung I still get different JPA errors when trying to map the following ONE-TO-ONE relationship between 2 Oracle SQL tables with composite PK in a SpringBoot application:
MASTER
ID
VERSION
1
2022.1
Constraint:
PK_MASTER PRIMARY KEY(ID, VERSION)
MASTER_DETAILS
MASTER_ID
VERSION
DETAILS
1
2022.1
details
Constraint:
PK_MASTER_DETAILS PRIMARY KEY(MASTER_ID, VERSION)
FK_MASTER_DETAILS FOREIGN KEY(MASTER_ID, VERSION) REFERENCES MASTER(ID, VERSION)
After some failures in trying to map it using the #OneToOne JPA annotation with both classes having #EmbeddedId set on the composite PK, I also installed JPA Buddy to check how it will be generated and that resulted in the following 4 classes:
Master.java
#Getter
#Setter
#Entity
#Table(name = "master")
public class Master {
#EmbeddedId
private MasterId id;
#OneToOne(mappedBy = "master")
private MasterDetails masterDetails;
}
MasterId.java
#Getter
#Setter
#Embeddable
public class MasterId implements Serializable {
private static final long serialVersionUID = 8254837075462858051L;
#Column(name = "id", nullable = false)
private BigDecimal id;
#Lob
#Column(name = "version", nullable = false)
private String version;
}
MasterDetails.java
#Getter
#Setter
#Entity
#Table(name = "master_details")
public class MasterDetails {
#EmbeddedId
private MasterDetailsId id;
#MapsId
#OneToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumns({
#JoinColumn(name = "master_id", referencedColumnName = "id", nullable = false),
#JoinColumn(name = "version", referencedColumnName = "version", nullable = false)
})
private Master master;
#Lob
#Column(name = "details", nullable = false)
private String details;
}
MasterDetailsId.java
#Getter
#Setter
#Embeddable
public class MasterDetailsId implements Serializable {
private static final long serialVersionUID = -8375336118866998644L;
#Column(name = "master_id", nullable = false)
private BigDecimal masterId;
#Lob
#Column(name = "version", nullable = false)
private String version;
}
When running the SpringBoot application with this JPA structure the run time error received is:
org.hibernate.PropertyNotFoundException: Could not locate field [id] on class [org.project.packages.MasterDetails]
After removing the #MapsId that cause this error the application starts but when trying to insert data in the tables I get the following error:
org.hibernate.id.IdentifierGenerationException: null id generated for:class org.project.packages.MasterDetails
Checking in the H2 test database I noticed that the FK on the Master_Details table was not present, but only the PK was set.
I would appreciate any help in pointing out how this problem can be solved: other annotations required (Cascade/FetchType) or in case there are any changes to be made to the database level (I also tried adding a separate identifier column in the Master_Details table defined as PK and only keep the FK to the Master table). Thanks in advance!

After many tries, I figured out to solve the issue.
I had to use a common key between the two entities and also FetchType.LAZY.
MasterDetails.class
public class MasterDetails {
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name="ID", column=#Column(name="MASTER_ID"))
})
private MasterId id;
#OneToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumns({
#JoinColumn(name = "master_id", referencedColumnName = "id", nullable = false),
#JoinColumn(name = "version", referencedColumnName = "version", nullable = false)
})
private Master master;
#Lob
#Column(name = "guidance", nullable = false)
private String guidance;
}
Master.class
public class MasterSheet {
#EmbeddedId
private MasterId id;
#OneToOne(mappedBy = "master", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private MasterDetails masterDetails;
}

Related

JPA Repository.findByKeyEquals() returns not present value, but the value is exist on db

I'm developing an application that queries a database.
There are a few issues right now.
history.isPresent() == false when calling Optional<History> findByKeyEquals() intermittently. but value is exist on database
This is the information I got while tracking the issue.
All child entities are non-null.
In most cases, if the same function is re-executed, it is searched.
But sometimes it doesn't happen intermittently.
i think that i use incorrectly table relationship annotation (#OneToMany,#ManyToOne options..)
I want to solve this issue.
this is my code
History (Parent)
#Table(
indexes = {
#Index(columnList = "key", unique = true),
})
#Entity
#Getter
#ToString
#Audited
public class History implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(updatable = false, nullable = false, columnDefinition = "BIGINT UNSIGNED")
private Long id;
#Setter
#Column(nullable = false, columnDefinition = "CHAR(36)")
private String key = UUID.randomUUID().toString();
#Setter
#Temporal(TemporalType.TIMESTAMP)
#NotAudited
private Date started = new Date();
#Setter
#Temporal(TemporalType.TIMESTAMP)
#NotAudited
private Date finished;
#Setter
#OneToMany(
cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
mappedBy = "history",
orphanRemoval = true)
#NotAudited
private List<Content> contents = new ArrayList<>();
...
}
Content (Child)
#Table
#Entity
#Getter
#Audited
public class Content implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(updatable = false, nullable = false, columnDefinition = "BIGINT UNSIGNED")
private Long id;
#Setter
#Column(columnDefinition = "LONGTEXT")
#NotAudited
private String content;
#Setter
#ManyToOne(targetEntity = History.class, fetch = FetchType.Lazy, optional = false)
#Audited
private History history;
...
}
Repository
public interface HistoryRepository
extends JpaRepository<History, Long>, RevisionRepository<History, Long, Integer> {
Optional<History> findByKeyEquals(final String key);
}

OneToOne JPA issue

I have 2 class
public class User {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String age;
#OneToOne
#JoinColumn(name = "address_id", referencedColumnName = "id")
private Address address;
}
and
public class Address {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String building;
private String country;
#OneToOne(mappedBy = "address")
private User user;
}
in my table address, I have a few rows.
When I insert table user with data
{
"id":null,
"name":"Foo",
"age":"18",
"address":{
"id":1,
"building":"Too",
"country":"ABS"
}
}
Table user have 1 row with address_id =1.
I insert same data as above
Table user have 2 row with address_id =1.
My answer is: why 2 table connected by one to one can happen the above case?
You can find your answer here
Why #OneToOne is allowing duplicate associations?
Basically, #JoinColumn(name = "address_id", referencedColumnName = "id") alone doesn't serve the semantics of one-to-one in the database, you need to add unique=true into the #JoinColumn, which makes it #JoinColumn(name = "address_id", referencedColumnName = "id", unique = true).
Side-note: I suggest you drop your tables and then re-creating them before trying this out. If you are using Hibernate, you can set hibernate.hbm2ddl.auto to create-drop

Inner join on two tables in spring boot

I have 2 entities and want to perform an inner join on the ID of these two tables. How do I do that? After joining the tables, how do I get the values?
First entity: Employee.java
#Entity
#Table(name = "emp")
public class Employee {
#Id
#Column(name = "id", nullable = false)
private int id;
#Column(name = "language", nullable = false)
private String language;
Second entity: Username.java
#Entity
#Table(name = "users")
public class Username {
#Id
#Column(name = "id", nullable = false)
private int id;
#Column(name = "name", nullable = false)
private String name;
Thanks
I don't know it's helpful for your or not but,
You have to give relationship between those table first(Here i defined bidirectional relationship).
I suppose there is #OneToOne mapping. As like follow,
In Employee Table,
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "username_id")
private Username username;
#OneToOne(mappedBy = "employee")
private Employee employee;
Same way whenever you need those data base on requirement then Place Query as following way in your Employee Repository,
#Query(nativeQuery = true, value="<your-join-query>")
public Employee getEmployeeAllDetails();
For more brief detail follow this kind of tutorials which give you better idea regurding working mechenisum.
https://howtodoinjava.com/
https://www.baeldung.com/

Could not fetch the SequenceInformation from the database ERROR but still everything works

I've created user and userRole tables
user entity
#Entity
#Table(name = "USERS")
public class User {
#Id
#Column(name = "USERNAME", nullable = false, unique = true)
private String username;
#Column(name = "PASSWORD", nullable = false)
private String password;
#Column(name = "ENABLED", nullable = false)
private boolean enabled = true;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user", fetch = FetchType.EAGER)
private Set<UserRole> userRole = new HashSet<>();
userRole entity
#Entity
#Table(name = "USER_ROLES", uniqueConstraints = #UniqueConstraint(
columnNames = { "ROLE", "USERNAME" }))
public class UserRole {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "user_role_id",
unique = true, nullable = false)
private Integer userRoleId;
#Column(name = "ROLE")
private String role;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "USERNAME")
private User user;
When i launch my app i get an Error and this stack trace:
ERROR JdbcEnvironmentImpl:420 - Could not fetch the SequenceInformation from the database
org.h2.jdbc.JdbcSQLException: Column "start_with" not found [42122-197]
But i don't have any 'start_with' columns. Before my UserRole entity was without userRoleId column and everything worked fine but then i added it to do 'role' column not unique and then this happened. But still everything works fine, i just disturbed by this error, what can be the couse of it?
I suggest checking your Hibernate dialect.
I had a similar error because of a start_value column that Hibernate was looking for in sequences of my PostgresQL database. This field name is a default value in Hibernate's SequenceInformationExtractorLegacyImpl class which has many subclasses, each depending on your precise database server and its version. Hibernate loads the right class according to the dialect you specify.
In my case, I was using the org.hibernate.dialect.PostgreSQLDialect dialect, a (deprecated) class meant to be used with a PostgesQL 8.2 version. I switched to org.hibernate.dialect.PostgreSQL9Dialect since my database was hosted on a PostgresQL 9 server. And the issue was gone.

Spring MVC - loading data from database

I have two entities which are in many to many relation and I can't load Set <Category> categories. These fields are filled in the database.
#Entity
#Table(name="Product")
public class Product {
#Id
#GeneratedValue
private int idProduct;
private String status;
private String name;
#ManyToMany(fetch = FetchType.EAGER, mappedBy= "products")
private Set <Category> categories;
}
#Entity
#Table(name="Category")
public class Category {
#Id
#GeneratedValue
private int idCategory;
private String name;
#ManyToMany(fetch = FetchType.EAGER)
private Set <Product> products;
}
This returns nothing int the view and the loop doesn't rotate even once.
<c:forEach items="${product.categories}" var="items">
<p>${items.name}</p>
</c:forEach>
I join the schema. Could someone write what to do to make it work, please?
enter image description here
This not works.
#Entity
#Table(name="Category")
public class Category {
#Id
#GeneratedValue
private int idCategory;
private String name;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "Product_Category", joinColumns = {
#JoinColumn(name = "Category_idCategory", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "Product_idProduct",
nullable = false, updatable = false) })
private Set <Product> product;
Hibernate infers the sql that it needs to create from the annotation on the objects. Your product entity is telling hibernate to get information about the SQL join from the Category entity. This is from the mappedBy clause in the #ManyToMany annotation.
When it goes to the Category entity, it doesn't find what it needs, and it just gives an empty set.
Most #ManyToMany annotations are done with a join table. Here is a sample join table annotation
#JoinTable(name = "product_to_category", joinColumns = {
#JoinColumn(name = "category_id", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "product_id",
nullable = false, updatable = false) })
Depending on your schema, you might need to adjust the above annotation to get it working. It will give you a good start.

Resources