I occasionally get the following error:
org.hibernate.LazyInitializationException: could not initialize proxy [com.dd.translation.domain.postgres.Offer#OfferKey(offerId=03Y, difId=b3e79b1925ce4e41905a2ce214943d1f)] - no Session
Embeddable:
#Builder
#Embeddable
#Data
#NoArgsConstructor
#AllArgsConstructor
public class OfferKey implements Serializable {
#Column(name = "offer_id", columnDefinition = "CHARACTER VARYING(64) NOT NULL")
private String offerId;
#Column(name = "dif_id", columnDefinition = "CHARACTER VARYING(64) NOT NULL")
private String difId;
}
Embedded object:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
#Table(name = "offers")
public class Offer {
#EmbeddedId
private OfferKey offerId;
private LocalDateTime validDate;
}
There is no other lazy loading in this project and I wouldn't assume I would need #Transactional for it. I am trying to figure out if there is a way to manually set it to eager or if anyone has run into this issue. I personally do not use this pattern but it is in existing code and I am trying to figure out if I need to rip it out or if there is an easy fix.
The error is telling you that the Offer object you are trying to access is not initialized, not the embedded id.
I guess you have something like this in one of your models:
#ManyToOne(fetch = FetchType.LAZY)
Offer offer;
Wherever you load the entity containing that association, you will have to make sure the offer is properly initialized, as apparently the calling code expects it can access offer details.
You can change the FetchType to EAGER, but I wouldn't recommend that. The better solution would be to apply a #EntityGraph on the repository method that instructs it to load the association.
Related
I am trying to build an app with Spring Boot and PostgreSQL where I want to store a Game entity.
The Game has a Setting which has many variation but still limited. I think its worth storing as separate Entity and reuse it for games with the same setting.
#Entity
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class Game {
#Id
#GeneratedValue
#Type(type = "pg-uuid")
private UUID id;
private Setting setting;
// etc, players and game result related stuff
}
#Entity
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class Setting {
#Id
#GeneratedValue
#Type(type = "pg-uuid")
private UUID id;
private String format;
// etc, other customizable settings
}
Want I would like to achive is
To create the Setting entity only if it does not exist. What is the
best way to do this validation?
To setup the right relationship between the Game and Setting. One
Setting entity can belong to any number of Game but one Game can only
have one Setting.
Thank you
To create the Setting entity only if it does not exist. What is the
best way to do this validation?
Adding a new row to your Setting table only if it does not exist needs more specification
You could take this literally and once you
get a Setting in a request check your whole Setting table for one
that matches all of the values of the Setting that you received, if
it exists OK, if it does not exist you save a new one.
However, since you said the settings are limited I would suggest presenting the user with a list of available settings and letting them pick one of those, and having an option to add a new setting if they wish so. If you really want to validate if an exact match to that setting does not exist already, then you can do your validation at this point as well.
To setup the right relationship between the Game and Setting. One Setting entity can belong to any number of Game but one Game can only have one Setting.
This simply requires you to have a setting foreign key in your Game table
Your Game entity class would look like this:
#Entity
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class Game {
#Id
#GeneratedValue
#Type(type = "pg-uuid")
private UUID id;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="setting_id")
private Setting setting;
// etc, players and game result related stuff
}
I have created a Spring Boot JPA micro service with a MySQL back end. I have two simple entities in my model, users and skills. These are used to represent the skills base within my organisation. Each user may have many skills and so I have used a one to many join to model this and can successfully assign skills to users. In my database, I can see that a users_skills table has been created by JPA to achieve this.
However, I am now struggling because, for each skill that a user has, an experience level needs to be specified (e.g. basic, advanced, expert) and I am unsure how to achieve this. I'm not sure whether 'levels' should just be an enum type within the Skill entity, or perhaps it should be a separate entity in its own right? Could I configure JPA so that it generates a users_skills_levels table which would represent this three-way relationship? Any advice would be most welcome!
These are my Entity classes: -
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private String name;
private String email;
#OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true
)
private Set<Skill> skills = new HashSet<>();
getters and setters
}
#Entity
#Table(name = "skills")
public class Skill {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
private String name;
getters and setters
}
That's not possible what you try to achieve.
You should create an Entity for the users_skills_levels. E.g. UserSkillLevel This entity will then have a ManyToOne relationship to User and a ManyToOne relationship to Skills plus the attribute level.
The User has a collection of UserSkillLevel and the Skill entity as well.
Please find a more in-depth example here:
https://thoughts-on-java.org/many-relationships-additional-properties/
I am facing lazy inizialization issue when I added Lombok project into my hibernate project and used its #Getter and #Setter on the entity class.
Entity classes are annotated with #Entity of Javax.persistence as I am using hibernate 5.
Issue stacktrace :-
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:146)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:259)
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:73)
at com.capehenry.domain.user.User_$$_jvst52e_9.getId(User_$$_jvst52e_9.java)
at com.capehenry.business.rs.course.SeatRequestResource.validateSeatRequestCancel(SeatRequestResource.java:338)
at com.capehenry.business.rs.course.SeatRequestResource.cancel(SeatRequestResource.java:220)
Everything was working fine with below code
#Entity
#Audited
#Table(name = "seat_request")
public class SeatRequest extends BaseEntity {
private CourseSchedule courseSchedule;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "courseScheduleId", nullable = false)
public CourseSchedule getCourseSchedule() {
return courseSchedule;
}
public void setCourseSchedule(CourseSchedule courseSchedule) {
this.courseSchedule = courseSchedule;
}
When I do searRequest.getCourseSchedule().getId() it works at rest layer means outside the transaction.
As soon as I change the code to below (add lombok), searRequest.getCourseSchedule().getId() at rest layer starts throwing lazyInitializationException :-
#Entity
#Audited
#Table(name = "seat_request")
#Setter
public class SeatRequest extends BaseEntity {
#ManyToOne(fetch = FetchType.LAZY, optional=false)
#JoinColumn(name = "courseScheduleId", nullable = false)
private CourseSchedule courseSchedule;
NOTE :-
1) I have to compulsory use Lombok project
2) I have to use searRequest.getCourseSchedule().getId() outside Sevrice and trasaction
Please suggest the solution, Thanks in advance!!
I have to use searRequest.getCourseSchedule().getId() outside Service and transaction
I have noticed this just now... If you are outside the service and transaction you will always have that exception. Try to use FetchType.EAGER and it should work.
When you are out of transaction your entities are detached, this means that all collections that you marked as lazy won't be loaded. So you have two options: the first is to perform all calls to collections getters inside the transaction, the second one is to mark as eager your collection, so when Hibernate loads the entity it will also load referenced collection immediately. Alternatively you could map to a DTO your Entity inside your transaction. As long as you are in the transaction the getters of lazy loaded field will always work, so a mapper to the DTO would access all informations. Once that the DTO is out of the transaction you will have access to all fields you have mapped, and than do whatever you want.
Here is how I solved the issue finally!
I thought the issue started after integration with Lombok project but the issue started when the annotations were moved to field level from method (property) level.
Bear with me for the long answer.
Here foreign is refering to the database level foreign tables.
To access any column from foreign table's outside the transaction you need to either use FetchType.Eager (which is default in hibernate for any foreign object) or need to join/subquery that table.
But if you just want to fetch the foreign key(column) with which 2 tables are joined (in our case the ID) and want to keep FetchType.LAZY then you can do it in 2 ways :-
1) Keep annotations (manyToOne, JoinColumn etc) on getter methods
2) If annotations has to be kept on field level then write one more annotation on foreign key field in parent table which is - #Access(AccessType.PROPERTY)
So in above code to solve I added this annotation on id field of course Schedule
#Entity
#Audited
#Table(name = "course_schedule")
#Getter
#Setter
public class CourseSchedule{
#Id
#GenericGenerator(name = "autoincr", strategy = "native")
#GeneratedValue(generator = "autoincr")
#Column(name = "id", unique = true, nullable = false)
#Access(AccessType.PROPERTY)
protected Long id;
..........
}
So no change was required in Seat Request.
In FindBugs, I am getting an issue like FCBL_FIELD_COULD_BE_LOCAL on the class name line of this code:
#Entity
#Table(name = "Student")
#Immutable
#Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class RetrievableStudent extends BaseStudent
{
#Id
#Column(name = "STUDENT_ID")
private long studentId;
#Column(name = "STUD_NOTE")
private String studenetNote;
}
How can I resolve this issue?
In order to resolve that issue you need to use you fields somewhere in your class. What FindBugs is telling you is that your fields in your class are never used as the fields.
How you using your fields that were retrieved from database? Maybe you need to add getters? For now, your fields are useless (unless you don't using them with reflection, which is not good).
My POJO has already getters setters, equals, hashcode.
It worked when I tried adding #JsonProperty annotation, as my POJO is built for API response purposes.
If the POJO is for ORM purposes (means database entity) the on getters and setters #Column annotation should work.
I have a set of JPA POJO's that contain annotations required for mapping to my domain. I also want to expose some REST services that will interact with those domain objects.
My current task is to create an android application to access these REST services. I am not able to use the domain object due to the JPA annotations they contain. The Dalvik compiler complains.
So I am looking for a strategy to be able to leverage these domain objects in a way that an Android project can also use those objects and not have to duplicate those POJO's.
Victor's suggestion to externalise the JPA mappings to XML rather than use annotations would surely work, but might be inconvenient if you're getting your JPA objects from tooling that only generates annotations.
I assume that you need on the client side Java classes that match the objects you will serialise in your REST services.
It is possible, but very tedious, to create DTO objects - POJOs exactly matching the JPA objects with suitable constructors from the JPA objects. This seems like an undue amount of effort.
It must be possible to write a source-code processor to strip the annotations from the Java. I don't think a simple regex scripting solution will work, I guess that truly parsing the source is necessary, so I hesitate to guess how much work this would be. However according to this question's answers the basic set of tools is available. I would start with this approach.
I could work out with following strategy.
This strategy works very well when you dont want fetch whole collection , or fetch with some addition criteria,
, you may retrieve it(collection relation) with named query.
use separate DAO for CRUD operation on JOIN table of many to many relation
e.g.
User can have many accounts and account can be shared by many users.
create domain models/ DAO for all the three tables,
use relation mapping for just retrieval and for DDL use individual properties.
#Entity
#Table(name="account" )
public class Account {
#Id (name="accountid")
private Long accountId;
#Column
private String type;
// removed #OneToMany as it causes issue while serializing to xml
#Transient
private Collection accountUsers;
//rest of the properties n geter setter goes here
}
#Entity
#Table(name="user")
public class User {
#Id(name="userid")
private Long userId;
#Column
private String name;
// by making transient jpa / hibernate does not initialize it with proxy.. so it remains null
/* you can populate this property using named query whenever required .*/
#Transient
private Collection userAccounts;
// rest of the properties n getter setter goes here
}
#Entity
#Table(name="AccountUser")
public class AccountUser {
// whatever your strategy to implement primary key here ,
#Id (name="accountuserid")
private Long accountUserId;
/* please note this annotation , made insertable/updatable false , relation defined just for fetching relation
*/
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "accountid", referencedColumnName = "accountid", insertable = false, updatable = false)
private Account account;
// look at insertable / updatable properties its turned off
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "userid", referencedColumnName = "userid", insertable = false, updatable = false)
private User user;
//
#Column ( name = "userid" )
private Long userId;
#Column ( name = "accountid" )
private Long accountId;
#Column ( name="opendate")
private Date opendate;
}
/* use separate dao to save above beans */
// somthing like this
public class AccountDAOImpl extends GenericDAOImpl implements AccountDAO {
}
public class UserDAOImpl extends GenericDAOImpl implements UserDAO {
}
public class AccountUserDAOImpl extends GenericDAOImpl implements AccountUserDAO {
}
I tried to explain if need any clarification kindly revert back. thanks