JPA Insert Into Large ManyToMany - spring

I have those 2 entities (stores and products) I have the products already and I want to assign it to the store, the problem is that a store could have thousands of products and to make an "insert into" I have to have the products in memory and then save the store with the product set. That makes me have a StackOverflowError and is super inefficient.
Is there a way to do something like this?
insert into product_store (product_id, store_id) VALUES (:productIds, :storeId)
Entities:
#Entity
#Table(name = "stores")
data class Store(#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0,
#Column
val name: String,
#Column
val street: String,
#Column
val streetNumber: String,
#Column
val postalCode: Int,
#Column
val city: String,
#Column
val state: String,
#Column
val latitude: Double,
#Column
val longitude: Double,
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "product_store",
joinColumns = [JoinColumn(name = "store_id")],
inverseJoinColumns = [JoinColumn(name = "product_id")])
val products: Set<Product>
) : DateAudit()
Product Entity:
#Entity
#Table(name = "products")
data class Product(#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0,
#Column(nullable = false)
val codeBar: String,
#Column(nullable = false)
val name: String,
#Column(columnDefinition = "text")
val description: String,
#Column(columnDefinition = "text")
val ingredients: String?,
val picture: String,
#OneToMany(fetch = FetchType.EAGER, cascade = [CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH])
val nutriments: Set<Nutriment>,
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "brand_id")
val brand: Brand,
#ManyToMany(fetch = FetchType.EAGER, cascade = [CascadeType.MERGE])
#JoinTable(name = "product_category",
joinColumns = [JoinColumn(name = "product_id")],
inverseJoinColumns = [JoinColumn(name = "category_id")])
val categories: Set<Category>
) : DateAudit()

Fixed like this
#Modifying
#Query("INSERT INTO product_store (product_id, store_id) " +
"(SELECT p.id, s.id FROM products p, stores s WHERE p.market_id IN (:productMarketIds) AND s.id = :storeId)", nativeQuery = true)
fun saveProductsToStoreByProductMarketIds(storeId: Long, productMarketIds: Set<String>)

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?
}

How to send POST request with Many to many relationship in Spring Boot

Anyone have any ideas on how I could do postmapping for the Many-to-Many relationship? Getting the data works, but this is what I'm having trouble with
I tried using the "guide" but unfortunately I don't understand it very well yet
Here is my entities:
Album
#Entity
public class Album implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
Long id;
String name;
String artist;
String cover;
#ManyToMany(fetch = FetchType.LAZY, cascade =
{
CascadeType.DETACH,
CascadeType.MERGE,
CascadeType.REFRESH,
CascadeType.PERSIST
})
#JoinTable( name = "user_albums",
joinColumns = #JoinColumn(name = "album_id", nullable = false),
inverseJoinColumns = #JoinColumn(name = "user_id", nullable = false)
)
#JsonBackReference
#OnDelete(action = OnDeleteAction.CASCADE)
private Set<User> users = new HashSet<>();
User:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank
#Size(max = 20)
private String username;
#NotBlank
#Size(max = 50)
#Email
private String email;
#NotBlank
#Size(max = 120)
private String password;
#ManyToMany(fetch = FetchType.LAZY, cascade =
{
CascadeType.DETACH,
CascadeType.MERGE,
CascadeType.REFRESH,
CascadeType.PERSIST
})
#JoinTable( name = "user_albums",
joinColumns = #JoinColumn(name = "user_id", nullable = false),
inverseJoinColumns = #JoinColumn(name = "album_id", nullable = false)
)
#OnDelete(action = OnDeleteAction.CASCADE)
private Set<Album> albums = new HashSet<>();
I tried it this way but it didn't work
#PostMapping("/users/mal/{userId}/album")
public ResponseEntity<Album> addAlbum(#PathVariable(value = "userId") Long userId, #RequestBody Album albumRequest, User userRequest) {
Album newMal = userRepo.findById(userId).map(user -> {
long albumId = userRequest.getId();
user.addAlbum(albumRequest);
return albumRepo.save(albumRequest);
}).orElseThrow(() -> new RuntimeException("Not found USER with id = " + userId));
return new ResponseEntity<>(newMal, HttpStatus.CREATED);

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

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

Delete just one side of a manytomany relationship Hibernate

I have two tables that have a manytomany relationship:
first one is ad ( represents all the products)
#Entity
#Table(name = "ad")
public class Ad {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "admin_id")
private Admin admin;
#ManyToMany(mappedBy = "ads", fetch = FetchType.LAZY)
private List<Order> orders = new ArrayList<>();
Second one is order:
#Entity
#Table(name = "`order`")
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne( cascade=CascadeType.
#JoinColumn(name = "buyer_id")
private Buyer buyer;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "order_ad", joinColumns = {
#JoinColumn(name = "order_id", referencedColumnName = "id", nullable = false, updatable = false) }, inverseJoinColumns = {
#JoinColumn(name = "ad_id", referencedColumnName = "id", nullable = false, updatable = false) })
private List<Ad> ads = new ArrayList<>();
when I delete order using its repository that is representing a cancellation so I don't want the ads to be deleted as well.
How can I do that?
PS: I can't find a replacement for the orphanRemoval of the onetomany relationship

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