Hibernate 6 perfroms update query after select query - java

I wrote a simple entity named "User" and another one called "Company". User entity has company inside with #ManyToOne(fetch = LAZY) mapping. When I create the session object and call get() method Hibernate executes select query and after it update, why?
User entity:
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode
#ToString
#Builder
#Entity
#Table(name = "users")
public class User2 {
#Id
#GeneratedValue(strategy = IDENTITY)
private Long id;
#Column(unique = true)
private String username;
#Embedded
#EqualsAndHashCode.Exclude
private PersonalInfo personalInfo;
#Enumerated(STRING)
#EqualsAndHashCode.Exclude
private Role role;
#Type(JsonType.class)
#EqualsAndHashCode.Exclude
private String info;
#ManyToOne(fetch = LAZY)
#JoinColumn(name = "company_id")
#EqualsAndHashCode.Exclude
#ToString.Exclude
private Company company;
}
Company entity:
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode
#ToString
#Builder
#Entity
public class Company {
#Id
#GeneratedValue(strategy = IDENTITY)
private Long id;
private String name;
}
Main:
public class HibernateEntityMappingRunner {
public static void main(String[] args) {
try (SessionFactory sessionFactory = buildSessionFactory();
Session session = sessionFactory.openSession()
) {
session.beginTransaction();
User2 user2 = session.get(User2.class, 2);
session.getTransaction().commit();
}
}
}
Hibernate console log:
Hibernate:
select
u1_0.id,
u1_0.company_id,
u1_0.info,
u1_0.birth_date,
u1_0.firstname,
u1_0.lastname,
u1_0.role,
u1_0.username
from
users u1_0
where
u1_0.id=?
Hibernate:
update
users
set
company_id=?,
info=?,
birth_date=?,
firstname=?,
lastname=?,
role=?,
username=?
where
id=?

See https://hibernate.org/community/contribute/intellij-idea/#debugging for details. The problem is that some of your toString() implementations (which the IntelliJ debugger invokes) access state of uninitialized entity/collection proxies, which will cause lazy initialization and hence a select query. If you have pending flushes (insert/update/delete) which affect one of the tables that are about to be selected from, Hibernate forcefully executes pending flushes.
By changing your toString() implementations or changing how IntelliJ shows objects in the debugger, you can avoid this.

Related

How to set schema location for #ManyToMany Entity Collection with JPA

I want to create 2 tables in my PostgreSQL database while using the Spring-boot JPA.
#Entity
#Table(schema = "test")
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
And my User class
#Entity
#Table(schema = "test")
#Data
#NoArgsConstructor
#AllArgsConstructor
public class User {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
private String group_id;
private String email;
#ManyToMany(fetch = FetchType.EAGER)
private Collection<Role> roles = new ArrayList<>();
}
The 2 tables above are created in test schema but the user_roles is trying to be created without any schema selected and I get the error
Caused by: org.postgresql.util.PSQLException: ERROR: no schema has been selected to create in
So my question is: How to assign the schema to the user_roles table that JPA is trying to create

cascade = CascadeType.ALL doesn´t work on OneToOne relation

I have one problem with One to One relation.
I have one USER that has only one TEAM
User Entity
#Entity
#Table(name = "USER")
#Data #NoArgsConstructor #AllArgsConstructor #Builder
public class User implements Serializable {
private static final long serialVersionUID = 3233149207833106460L;
#Id
#GeneratedValue
#Column(name = "USERID")
private Long id;
...
#OneToOne(mappedBy = "user")
private Team team;
}
Team Entity
#Entity
#Table(name = "TEAM")
#Data #NoArgsConstructor #AllArgsConstructor #Builder
public class Team implements Serializable {
private static final long serialVersionUID = 3233149207833106460L;
#Id
#GeneratedValue
#Column(name = "TEAMID")
private Long id;
...
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "USERID")
private User user;
}
This is the way I´m trying to create object.
//create Team and add to User
Team team = Team
.builder()
.value(TeamConstants.INITIAL_VALUE)
.name(userDetailsForm.getUsername()+" "+TeamConstants.TEAM)
.country(userDetailsForm.getUsername()+" "+TeamConstants.COUNTRY)
.build();
User user = User
.builder()
.username(userDetailsForm.getUsername())
.password(this.passwordEncoder.encode(userDetailsForm.getPassword()))
.email(userDetailsForm.getEmail())
.registerDate(DateUtil.getNow())
.role(userDetailsForm.getRole())
.team(team)
.build();
team.setUser(user);
this.userRepository.save(user);
And when I try to save user (and create automatically the son team), it gaves me the error
Blockquote
org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.soccermanager.business.entities.user.User.team -> com.soccermanager.business.entities.team.Team;
Any idea with the solution?
This is my first question here, I hope its correct.
Thanks for your time
please interact with Team entity then user will be change follow CascadeType
or you need add
#OneToOne(mappedBy = "user", cascade = CascadeType.ALL) private Team team; in User class.
Please try both and you can see the different. Hope this post answer can help with you
You should save your team entity before your user.
You should do as you do only if you are working in the same JPA Transaction unit. If not, it will not work and you get that exception.
Try adding #Transactional annotation on the method, with optional params like propagation = Propagation.REQUIRED and readonly=false because you are creating/updating objects here :
#Transactional(propagation = Propagation.REQUIRED, readOnly = false)
protected User saveTeamWithUser(UserDetailsForm form) {
// ...
}

Problems making a many-to-many relationship work across 2 microservices using Spring Boot & Hibernate

So for this assignment, I'm supposed to have 2 microservices (task-service & user-service) with each having their own database.
A task can have multiple users assigned it but multiple users can be assigned to multiple tasks so therefore it's a many-to-many relationship.
Both microservices are Spring Boot applications using Hibernate and I'm struggling to make this relationship happen between task and user inside the task-service because I don't think it's a good idea to copy the user model that already exists in the user-service, over to the task-service.
This assignment assumes that there is consistency in the user-ids across both databases without using foreign keys.
For context:
Desired entity relationship diagram generated from Hibernate:
Task.java [task-service]
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#EqualsAndHashCode(callSuper = true)
public class Task extends BaseEntity {
private String name, description;
#ManyToOne
private Lane lane;
#OneToMany
private List<TaskUser> users;
}
TaskUser.java [task-service]
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Data
#EqualsAndHashCode
public class TaskUser {
#Id
private Long id;
#Column(name = "task_id")
private Long taskId;
#Column(name = "user_id")
private Long userId;
}
User.java [user-service]
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Data
#EqualsAndHashCode(callSuper = true)
#ToString(callSuper = true)
public class User extends BaseEntity {
#Column(unique = true)
private String username;
}
Currently, Hibernate is generating both a task_user & task_users table inside the task-service database and I'd like to just have 1 intermediate table instead of two.
Question: How would I make this many-to-many relationship work with Hibernate whilst working with two different microservices?
Maybe a better hibernate mapping :
Task.java [task-service]
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#EqualsAndHashCode(callSuper = true)
public class Task extends BaseEntity {
private String name, description;
#ManyToOne
private Lane lane;
#OneToMany(mappedBy="task")
private List<TaskUser> users;
}
TaskUser.java [task-service]
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Data
#EqualsAndHashCode
public class TaskUser {
#Id
private Long id;
#ManyToOne
#JoinColumn(name="task_id", nullable=false)
private Task task;
#Column(name = "user_id")
private Long userId;
}
Use case: assign an already existing task to an already existing user.
You can assign multiple users at a time to 1 single task.
PostRequest
/tasks/allocation/new
Request Body:
{
"task-id": 12345,
"users": [
{
"username": "user-1",
"user-id": 101
},
{
"username": "user-2",
"user-id": 102
},
{
"username": "user-3",
"user-id": 103
}
]
}
Post request is having one task and list of user-details to be allocated to that task.
TaskAllocation.java
#Data
public class TaskAllocation{
#jsonProperty("task-id")
private long taskId;
#JsonProperty("users")
private List<Users> userList;
}
Users.java
#Data
public class Users{
#jsonProperty("username")
private String username;
#JsonProperty("user-id")
private Long userId;
}
RestController
#PostMapping("/tasks/allocation/new")
public CompletableFuture<ResponseEntity<?>> assignTaskToUsers(#ResponseBody #Valid TaskAllocation taskAllocation){
// service call
}
Inside service:
fetch the task from task db(verify if task exist)
If needed, fetch details of users from user-service(for each user), need a async or rest call to user-service. Task service is not having details of users.
For each user:
1.Create new Allocation
Set task Id
Set user-id or username
Save
Task is already existing
Table Task-Allocation
--------------------------------------------------------------
alloc-id(Pk) task-Id(Fk) user-id timetamp
--------------------------------------------------------------
1 12345 101 123123123123
2 12345 102 123123123123
3 12345 103 123123123123
Entities
Task and TaskAllocation has 1:n relationship i.e. task-allocation table consists of multiple records with same task-id.
Task.java
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "sirf_tournament")
public class Task extends Auditable<String> implements Serializable {
#Id
#GeneratedValue
private Long taskId;
private String taskName;
// others
#OneToMany(
fetch = FetchType.LAZY,
mappedBy = "task",
cascade = CascadeType.ALL,
orphanRemoval = true)
private Collection<TaskAllocation> taskAllocation = new HashSet<>();
}
TaskAllocation.java
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "sirf_tournament")
public class TaskAllocation extends Auditable<String> implements Serializable {
#Id
#GeneratedValue
private Long allocId;
private Long userId;
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "taskId")
private Task task;
// others
}
User table and other related entities are not mentioned here. Task service has no information of users.
It would be good if you verify every users(all ids can be verified in a single call, return invalid ids from user service) and task ids before persisting them into table.

ManyToOne realtionship in Java Spring Boot

Well, I have a class Feeding.java and a class User.java
One Feeding should have one User but a User can have many Feedings.
This is my Feeding.java class:
#Getter
#Setter
#EqualsAndHashCode
#NoArgsConstructor
#Entity
#Table(name = "tblFeeding")
public class Feeding {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer idFeeding;
private LocalDateTime dateFeeding;
private double amountFeeding;
private String foodFeeding;
#ManyToOne
#JoinColumn(name = "id_user")
private User user;
}
This is my User.java class:
#Entity
#Getter
#Setter
#Table(name = "tbl_User")
public class User implements UserDetails {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer idUser;
private String nameUser;
private String email;
private String passwordUser;
The problem at the moment is that I can save feeding object with an userId that doesn't exist and this shouldn't be possible.
A foreign constraint is missing. If you have created the table with Hibernate ORM it shouldn't happen but if you've created the table in a different way, it's possible that the foreign constraint has not been created.
You would need to run a SQL query similar to this one:
ALTER TABLE Feeding
ADD CONSTRAINT id_user_fk
FOREIGN KEY (id_user) REFERENCES User;
Note that the query might be different, you need to check the exact query for your database.
This query is from the Hibernate ORM documentation example for many-to-one associations.

Criteria API ignore LAZY fetch

I have two entities Customer and Product with relation one to many with lazy strategy.
Problem is fetch not lazy, products collection always fetch all related products.
public Optional<Customer> findById(final long customerId) {
final EntityManager entityManager = sessionFactory.createEntityManager();
final CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
final CriteriaQuery<Customer> criteriaQuery = criteriaBuilder.createQuery(Customer.class);
final Root<Customer> customerMetamodel = criteriaQuery.from(Customer.class);
criteriaQuery.where(criteriaBuilder.equal(customerMetamodel.get("id"), customerId));
final TypedQuery<Customer> query = entityManager.createQuery(criteriaQuery);
return Optional.ofNullable(query.getSingleResult());
}
As you can see no one mention about fetch eager, and in entity classes the same, all lazy:
Customer
#Data
#Builder
#EqualsAndHashCode
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "customer")
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int age;
#OneToMany(mappedBy = "customer", fetch = FetchType.LAZY)
private List<Product> products;
}
Product
#Entity
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode
#ToString(exclude = "customer")
#Table(name = "product")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private BigDecimal price;
#ManyToOne(fetch = FetchType.LAZY)
private Customer customer;
}
Why lazy work as eager and how to enable real lazy mode in this case?
UPDATE_1
Integration test with H2
#Test
public void testFindById() {
Customer customer = customerRepository.create(Customer.builder().age(20).name("Denis").build());
productRepository.create(Product.builder().customer(customer).price(new BigDecimal(100)).build());
productRepository.create(Product.builder().customer(customer).price(new BigDecimal(200)).build());
productRepository.create(Product.builder().customer(customer).price(new BigDecimal(300)).build());
final Customer result = customerRepository.findById(customer.getId()).orElseThrow();
assertEquals("Denis", result.getName());
assertThrows(LazyInitializationException.class, () -> System.out.println(result.getProducts()));
}
I call .getProducts() but expect LazyInitializationException because session already closed.
I guess you are using Spring? Try disabling spring.jpa.properties.hibernate.enable_lazy_load_no_trans which is an anti-pattern anyway.
Reason is EntityManager hold open session. And load subentities on demand. In my case .getProducts().
For close session directly use entityManager.close().

Categories