Kotlin spring JPA. lateinit property has not been initialized exception from getting lazy field of JPA entity - spring

I use Spring Boot, Hibernate, Kotlin
In build.gradle.kts:
apply(plugin="org.hibernate.orm")
tasks.withType<org.hibernate.orm.tooling.gradle.EnhanceTask>().configureEach {
options.enableLazyInitialization = true
options.enableDirtyTracking = true
options.enableAssociationManagement = true
}
User entity:
#Entity
#Table(name = "user")
class User(
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
var id: Long = -1,
#Column(name = "user_name", unique = true, nullable = false, length = 20)
var userName: String = "",
#Column(unique = true, nullable = false)
var email: String = "",
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "user_roles", joinColumns = [JoinColumn(name = "user_id")],
inverseJoinColumns = [JoinColumn(name = "role_id")])
var roles: MutableSet<Role> = mutableSetOf()
) {
#OneToOne(fetch = FetchType.LAZY, cascade = [CascadeType.ALL], mappedBy = "user", optional = false)
#LazyToOne(LazyToOneOption.PROXY)
#LazyGroup( "person" )
lateinit var person: Person
....
}
I get it by:
#Transactional
#Component
class UserServiceImpl (private val userRepository: UserRepository){
override fun getUserData(id: Long): Optional<UserView> {
return userRepository.findById(id).map { UserView.build(it, it.person) }
}
...
}
And it.person throws lateinit property has not been initialized exception, but Person was loaded( I see it by hibernate log ). Getting roles works fine.
I have same result without #LazyToOne(LazyToOneOption.PROXY) and #LazyGroup( "person" ).
SOLVED:
#OneToOne(fetch = FetchType.LAZY, cascade = [CascadeType.ALL])
#JoinColumn(name = "person_id", referencedColumnName = "id")
#LazyToOne(LazyToOneOption.PROXY)
#LazyGroup( "person" )
lateinit var person: Person

Related

Spring Boot JPA - Projection selecting all fields from table when has a collection

I trying to get two fields and a #ElementCollection from entity using projection with interface, but the JPA are selecting all fields from my entity and when i remove the method that get the list of my #ElementCollection the JPA select the only two fields.
My entity class:
#Entity(name = "users")
data class User(
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null,
#Column(nullable = false)
var name: String? = null,
#Column(unique = true, nullable = false)
var cpf: String? = null,
#Column(name = "phone_number", nullable = false)
var phoneNumber: String? = null,
#Column(unique = true, nullable = false)
var email: String? = null,
#Column(name = "password_hash")
var passwordHash: String? = null,
#Column(name = "password_hash_recovery")
var passwordHashRecovery: String? = null,
#Column(name = "password_hash_recovery_date")
var passwordHashRecoveryDate: String? = null,
#Column(name = "self_employed", nullable = false)
var selfEmployed: Boolean? = null,
#JoinColumn(name = "user_photo", referencedColumnName = "id")
#OneToOne(fetch = FetchType.LAZY)
var userPhoto: File? = null,
#JoinColumn(name = "id_location", referencedColumnName = "id")
#OneToOne(fetch = FetchType.LAZY)
var location: Location? = null,
#Column(name = "rating_star", nullable = false)
#Enumerated(EnumType.STRING)
var ratingStar: RatingStar = RatingStar.ONE,
#JoinColumn(name = "id_area", referencedColumnName = "id")
#OneToOne(fetch = FetchType.LAZY)
var area: Area? = null,
#OneToMany(mappedBy = "user", cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
var workTimes: List<WorkTime> = arrayListOf(),
#OneToMany(mappedBy = "contractor", cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
var contractorOrders: List<Order>? = arrayListOf(),
#OneToMany(mappedBy = "selfEmployed", cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
var selfEmployedOrders: List<Order>? = arrayListOf(),
) {
#ElementCollection(fetch = FetchType.EAGER)
#Enumerated(EnumType.STRING)
#CollectionTable(
name = "profiles_authorties",
joinColumns = [JoinColumn(name = "user_id", referencedColumnName = "id")],
)
#Column(name = "authority")
private val _authorities: MutableSet<ProfileAuthorities> = HashSet()
init {
_authorities.add(ProfileAuthorities.CLIENT)
}
fun setAsAdmin() =
_authorities.add(ProfileAuthorities.ADMIN)
fun getAuthorities(): Set<ProfileAuthorities> = _authorities
}
My interface for projection:
interface LoginUserProjection {
fun getId(): Long
fun getPasswordHash(): String
fun getAuthorities(): Set<ProfileAuthorities>
}
The result query is:
Hibernate: select user0_.id as id1_12_, user0_.id_area as id_area11_12_, user0_.cpf as cpf2_12_, user0_.email as email3_12_, user0_.id_location as id_loca12_12_, user0_.name as name4_12_, user0_.password_hash as password5_12_, user0_.password_hash_recovery as password6_12_, user0_.password_hash_recovery_date as password7_12_, user0_.phone_number as phone_nu8_12_, user0_.rating_star as rating_s9_12_, user0_.self_employed as self_em10_12_, user0_.user_photo as user_ph13_12_ from users user0_ where user0_.id=?
Hibernate: select authoriti0_.user_id as user_id1_8_0_, authoriti0_.authority as authorit2_8_0_ from profiles_authorties authoriti0_ where authoriti0_.user_id=?
when i remove fun getAuthorities(): Set<ProfileAuthorities> from LoginUserProjection the result is:
Hibernate: select user0_.id as col_0_0_, user0_.password_hash as col_1_0_ from users user0_ where user0_.id=?
My repository method:
#Repository
interface UserRepository : JpaRepository<User, Long> {
fun <T> getUserProjectionById(id: Long, projection: Class<T>): T?
}

Hibernate model with multiple many-to-many relations slow performance when persisting

We have following schema as described on diagram below. There's an Entity with bidirectional many-to-many relations with two other entities(Relation1, Relation2). There's also many-to-many relation between Entity-Relation2 relationship itself and Relation1 entity. When we create an instance of Entity and persist into database, it's working fine, except that operation takes too much time. I'd like to ask, if there are any possible optimizations on Hibernate level, which could boost performance of save operation.
Here's diagram:
Model definitions:
class Entity : AbstractJpaPersistable() {
var name: String? = null
var description: String? = null
#OneToMany(mappedBy = "entity", fetch = FetchType.LAZY, cascade = [CascadeType.PERSIST], orphanRemoval = true)
var entityRelations1: List<EntityToRelation1> = emptyList()
#OneToMany(mappedBy = "entity", fetch = FetchType.LAZY, cascade = [CascadeType.PERSIST], orphanRemoval = true)
var entityRelations2: List<EntityToRelation2> = emptyList()
}
#Entity
class Relation2: AbstractJpaPersistable(){
#OneToMany(mappedBy = "relation2", fetch = FetchType.LAZY, cascade = [CascadeType.PERSIST], orphanRemoval = true)
var entityRelations2: List<EntityToRelation2> = emptyList()
}
#Entity
class Relation1: AbstractJpaPersistable(){
#OneToMany(mappedBy = "relation1", fetch = FetchType.LAZY, cascade = [CascadeType.PERSIST], orphanRemoval = true)
var entityRelations1: List<EntityToRelation1> = emptyList()
#OneToMany(mappedBy = "relation1", fetch = FetchType.LAZY, cascade = [CascadeType.PERSIST], orphanRemoval = true)
var entityRelations2Relations1: List<EntityToRelation2ToRelation1> = emptyList()
}
#Entity
class EntityToRelation2 {
#EmbeddedId
var entityToRelation2Id: EntityToRelation2Id = EntityToRelation2Id()
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("entityId")
#JoinColumn(name = "entity_id", insertable = false, updatable = false)
var entity: Entity? = null
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("relation2Id")
#JoinColumn(name = "relation2_id", insertable = false, updatable = false)
var relation2: Relation2? = null
#OneToMany(mappedBy = "entityToRelation2", fetch = FetchType.LAZY, cascade = [CascadeType.PERSIST], orphanRemoval = true)
var entityRelations2Relations1: List<EntityToRelation2ToRelation1> = emptyList()
}
#Embeddable
class EntityToRelation2Id : Serializable {
#Column(name = "entity_id")
var entityId: Int? = null
#Column(name = "relation2_id")
var relationId: Int? = null
}
#Entity
class EntityToRelation1 {
#EmbeddedId
var entityToRelation1Id = EntityToRelation1Id()
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("entityId")
#JoinColumn(name = "entity_id", insertable = false, updatable = false)
var entity: Entity? = null
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("relation1Id")
#JoinColumn(name = "relation1_id", insertable = false, updatable = false)
var relation1: Relation1? = null
}
#Embeddable
class EntityToRelation1Id : Serializable {
#Column(name = "entity_id")
var entityId: Int? = null
#Column(name = "relation1_id")
var relation1Id: Int? = null
}
#Entity
class EntityToRelation2ToRelation1 {
#EmbeddedId
var entityToRelation2ToRelation1Id = EntityToRelation2ToRelation1Id()
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("entityToRelation2Id")
#JoinColumns(
JoinColumn(name = "entity_id"),
JoinColumn(name = "relation2_id")
)
var entityToRelation2: EntityToRelation2? = null
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("relation1Id")
#JoinColumn(name = "relation1_id", insertable = false, updatable = false)
var relation1: Relation1? = null
}
#Embeddable
class EntityToRelation2ToRelation1Id : Serializable {
var entityToRelation2Id: EntityToRelation2Id? = null
#Column(name = "relation1_id")
var relation1Id: Int? = null
}

JPA - Issue with OneToOne relationship when two foreign keys are primary key to entity

Two foreign keys act as primary key in entity for OneToOne, I'm getting error "Provided id of the wrong type for class ....."
When I tried to POST data, It's getting inserted correctly but GET is not working.
If I change OneToOne to OneToMany it is working for POST & GET both.
Request:
{
"items": [
{
"applicant": {
"guests": [
{
"seqNumber": 1,
"name": "name",
"gender": "gender"
}
]
}
}
]
}
Back Reference:
reservation.getItems().forEach(i -> {
i.setReservation(reservation);
i.getApplicant().setItem(i);
i.getApplicant().getGuests().forEach(g -> g.setApplicant(i.getApplicant()));
});
Reservation Entity:
#Entity
#Getter
#Setter
public class Reservation {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID_RESERVATION", nullable = false, updatable = false)
private String reservationId;
#OneToMany(mappedBy = "reservation", cascade = CascadeType.ALL, orphanRemoval = true)
#JsonManagedReference
private Set<Item> items = new HashSet<>();
}
Item Entity:
#Entity
#Getter
#Setter
#IdClass(Item.ItemKey.class)
public class Item {
#Id
#Column(name = "ID_ITEM_RESERVATION", nullable = false, updatable = false)
private long itemReservationId;
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_RESERVATION", referencedColumnName = "ID_RESERVATION", nullable = false, updatable = false)
#JsonBackReference
private Reservation reservation;
#OneToOne(mappedBy = "item", cascade = CascadeType.ALL)
#JsonManagedReference
private Applicant applicant;
#Data
static class ItemKey implements Serializable {
private Reservation reservation;
private long itemReservationId;
}
}
Applicant Entity:
#Entity
#Getter
#Setter
#IdClass(Applicant.ApplicantKey.class)
public class Applicant {
#Id
#OneToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "ID_RESERVATION", referencedColumnName = "ID_RESERVATION", nullable = false, updatable = false),
#JoinColumn(name = "ID_ITEM_RESERVATION", referencedColumnName = "ID_ITEM_RESERVATION", nullable = false, updatable = false)
})
#JsonBackReference
private Item item;
#OneToMany(mappedBy = "applicant", cascade = CascadeType.ALL, orphanRemoval = true)
#JsonManagedReference
private Set<Guest> guests = new HashSet<>();
#Data
static class ApplicantKey implements Serializable {
private Item item;
}
}
Guest Entity:
#Entity
#Getter
#Setter
#IdClass(Guest.GuestKey.class)
public class Guest {
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "ID_RESERVATION", referencedColumnName = "ID_RESERVATION", nullable = false, updatable = false),
#JoinColumn(name = "ID_ITEM_RESERVATION", referencedColumnName = "ID_ITEM_RESERVATION", nullable = false, updatable = false)
})
#JsonBackReference
private Applicant applicant;
#Id
#Column(name = "S_NUMBER", nullable = false, updatable = false)
private Short seqNumber;
#Column(name = "N_NAME")
private String name;
#Column(name = "CD_GENDER")
private String gender;
#Data
static class GuestKey implements Serializable {
private Applicant applicant;
private Short seqNumber;
}
}
Expected output must be same as Request but getting error " ... Provided id of the wrong type for class ..."
Here is the code.

Spring JpaRepository manyToMany bidirectional should save instead of update

if got a language table and a system table with a many-to-many relationship:
Language:
#JsonAutoDetect
#Entity
#Table(name = "language")
public class Language implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "language_id", nullable = false)
private int languageId;
#Column(name = "language_name", nullable = false)
private String languageName;
#Column(name = "language_isocode", nullable = false)
private String languageIsoCode;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "system_language", joinColumns = {#JoinColumn(name = "language_id", updatable = false)},
inverseJoinColumns = {
#JoinColumn(name = "system_id", updatable = false)}, uniqueConstraints = {
#UniqueConstraint(columnNames = {
"language_id",
"system_id"
})})
private List<System> systems;
public Language() {
}
// GETTER & SETTERS
// ....
}
System
#JsonAutoDetect
#Entity
#Table(name = "system")
public class System implements Serializable {
#Id
#Column(name = "system_id", nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer systemId;
#Column(name = "system_name", nullable = false, unique = true)
private String systemName;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "university_id", nullable = false)
private University university;
#JoinColumn(name = "calender_id", nullable = false)
#OneToOne(fetch = FetchType.EAGER)
private Calendar calender;
#OneToMany(mappedBy = "system")
#LazyCollection(LazyCollectionOption.FALSE)
private List<SystemUserRole> systemUserRoleList;
#OneToMany(mappedBy = "system")
#LazyCollection(LazyCollectionOption.FALSE)
private List<Role> roleList;
#OneToOne(mappedBy = "system")
#LazyCollection(LazyCollectionOption.FALSE)
private CsmUserEntity csmUserEntity;
#ManyToMany(mappedBy = "systems")
#LazyCollection(LazyCollectionOption.FALSE)
private List<Language> languages;
public System() {
}
// GETTER & SETTERS
// ....
}
When im writing a first dataset (systemId=1, language_id=20) into the table, everything works fine. But when i try to write a second dataset with the same language_id but with other system_id (systemId=2, language_id=20), then the existing dataset gets updated. But i want to have a new dataset instead. What can i do?
Thanks in advance!

Spring Security, Refresh Token & NotSerializableException

I have the following entities:
#Entity
#Table(name = "ct_users")
#JsonIgnoreProperties("password", "enabled", "driver", "reviews")
open class User(
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Int = 0,
#Column
val phone: String = "",
#Column
val password: String = "",
#Column
val enabled: Boolean = false,
#OneToOne(fetch = FetchType.LAZY, mappedBy="profile")
var driver: Driver? = null,
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "ct_reviews_rel",
joinColumns = arrayOf(JoinColumn(name = "user_id")),
inverseJoinColumns = arrayOf(JoinColumn(name = "review_id"))
)
#JsonManagedReference
var reviews: List<Review>? = null
) : Serializable
And related drivers table:
#Entity
#Table(name = "ct_drivers")
#JsonIgnoreProperties("password", "profile")
class Driver(
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Int = 0,
#Column(name = "first_name")
var firstName: String = "",
#Column(name = "last_name")
var lastName: String = "",
#Column(name = "rating")
var rating: Double = 5.0,
#Column(name = "reviews")
var reviewsCount: Int = 0,
#Column(name = "invited_by")
var invitedBy: Int? = 0,
#Column(name = "position_prev", columnDefinition = "geometry(Point,4326)")
var positionPrev: Point = Helpers.geometry(0.0, 0.0),
#Column(columnDefinition = "geometry(Point,4326)")
var position: Point = Helpers.geometry(0.0, 0.0),
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
var profile: User? = null,
#Transient
var distance: Int = 0
) : Serializable
When i try to refresh token (/oauth/token?grant_type=refresh_token&client_id=abc&client_secret=abcd&refresh_token=...), i got the following error message:
{
"error": "server_error",
"error_description": "java.io.NotSerializableException: org.geolatte.geom.jts.PointSequenceCoordinateSequenceFactory"
}
How can i fix it? And what is the cause of this problem?
Helpers.geometry func:
fun geometry(lat: Double, lng: Double): Point {
return GeometryFactory(PrecisionModel(), 4326).createPoint(Coordinate(lat, lng))
}
My fault. Problem was in my UserDetailsService,

Resources