I am trying to simply test a repository. Here is my test code:
#ContextConfiguration(classes = [
DatabaseTestConfiguration::class
])
#TestPropertySource(
properties = [
"spring.jpa.hibernate.ddl-auto=create-drop",
"spring.flyway.enabled=false"
]
)
#AutoConfigureTestDatabase
#AutoConfigureDataJpa
#AutoConfigureTestEntityManager
internal open class UserTest {
#Autowired
private lateinit var entityManager: TestEntityManager
#Autowired
private lateinit var userRepository: UserRepository
#Test
#Transactional
open fun whenSuccessfullyFoundAnyDuplicatedUserThenReturnTrue() {
val user = User()
user.name = NAME
user.surname = SURNAME
user.gender = GENDER
entityManager.persistAndFlush(user)
assertEquals(true, userRepository.anyDuplicate(
name = user.name,
surname = user.surname,
gender = user.gender,
))
}
companion object {
const val NAME = "John"
const val SURNAME = "Snow"
const val GENDER = "male"
}
Here is my DatabaseTestConfiguration:
#TestConfiguration
#EntityScan(value = [
"com.username.db.entities"
])
#EnableJpaRepositories(
"com.username.db.repositories"
)
class DatabaseTestConfiguration {
}
This is my repository:
#Repository
interface UserRepository: JpaRepository<User,Long> {
#Query(
"""
SELECT CASE
WHEN COUNT(user.id) > 0 THEN TRUE
ELSE FALSE END
FROM User user
WHERE user.name = :name AND user.surname = :surname AND user.gender = :gender
)
"""
)
fun anyDuplicate(
#Param("name") name: String?,
#Param("surname") surname: String?,
#Param("gender") gender: String?
): Boolean
}
This is my entity:
#Entity
#Table(name = "user")
class User {
#Id
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "cdr_generator"
)
#SequenceGenerator(
name = "cdr_generator",
sequenceName = "cdr_seq",
allocationSize = 1
)
#Column(name = "id", nullable = false)
var id: Long? = null
#Column(name = "name", nullable = false)
var name: String? = null
#Column(name = "surname", nullable = false)
var surname: String? = null
#Column(name = "gender", nullable = false)
var gender: String? = null
}
Now when i am testing. this will always fail my test. When i debug it, i get that in my test it never enters userRepository.anyDuplicate() function. I assume that the problem is with configuring test. Can anybody help?
Related
I have these entities
#Entity(name = "inspiration")
class InspirationEntity(
#Id
var uuid: UUID? = null,
#Column(name = "display_name")
var displayName: String,
#CreationTimestamp
#Column(name = "created_at")
#Temporal(TemporalType.TIMESTAMP)
var createdAt: Date?,
#UpdateTimestamp
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "last_modified_date")
var lastModifiedDate: Date?,
#OneToMany(targetEntity = BaseSliderEntity::class)
#Cascade(CascadeType.ALL)
var sliderList: List<BaseSliderEntity>,
)
#Entity(name = "base_slider")
#Inheritance(strategy = InheritanceType.JOINED)
//TODO investigate DiscriminatorColumn anotation and best practices, because it shows some warning in logs
#DiscriminatorColumn(name = "slider_type", discriminatorType = DiscriminatorType.STRING)
abstract class BaseSliderEntity(
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
open var id: Long? = null,
)
#Entity(name = "dish_of_the_day")
#DiscriminatorValue("DISH_OF_THE_DAY")
class DishOfTheDayEntity(
#Column(name = "title_en")
var titleEN: String,
#Column(name = "title_de")
var titleDE: String,
) : BaseSliderEntity()
#Entity(name = "inspiration_screen_link")
#DiscriminatorValue("LINK")
class InspirationScreenLinkEntity(
#Enumerated(EnumType.STRING)
var destination: Destination,
) : BaseSliderEntity()
this is my dto
data class InspirationDTO(
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
var uuid: UUID?,
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
var createdAt: Date?,
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
var lastModifiedDate: Date?,
val displayName: String,
val inspirationScreenItemList: List<InspirationScreenItemDTO>,
)
and this is InspirationScreenItemDTO
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
#JsonSubTypes(
JsonSubTypes.Type(value = DishOfTheDayDTO::class, name = "DishOfTheDay"),
JsonSubTypes.Type(value = InspirationScreenContinuousSliderDTO::class, name = "InspirationScreenContinuousSlider"),
JsonSubTypes.Type(value = InspirationScreenLinkDTO::class, name = "InspirationScreenLink"),
JsonSubTypes.Type(value = InspirationScreenPagingSliderDTO::class, name = "InspirationScreenPagingSlider"),
JsonSubTypes.Type(
value = InspirationScreenRecentlyViewedSliderDTO::class,
name = "InspirationScreenRecentlyViewedSlider"
),
JsonSubTypes.Type(value = InspirationScreenTagsSliderDTO::class, name = "InspirationScreenTagsSlider"),
)
open class InspirationScreenItemDTO
when I try to update
like this
#Transactional
fun update(uuid: UUID, inspirationDTO: InspirationDTO): InspirationDTO {
var entity = inspirationRepository.findById(id).get()
val updatedEntity: InspirationEntity = inspirationMapper.convertToEntity(inspirationDTO)
entity.sliderList = updatedEntity.sliderList
val result: InspirationEntity = inspirationRepository.save(entity)
return inspirationMapper.convertToDto(result)
}
this is my swagger post
{
"displayName": "ED",
"inspirationScreenItemList": [
{
"type": "DishOfTheDay",
"titleEN": "GGGGGGG",
"titleDE": "GGGGGGG"
}
]
}
in DishofDto table it creates two rows first and updated, and inspiration remains one row which is okay but dish of the day shouldn't contain two rows, it should be just updated one.
My solution was to add uuid id as primary key in inspiration which works with deleting by id and then put toUpdateDto under the same id and save , but I'm not sure it's good solution.
I am fairly new to Spring Boot and unable to find out why my CRUD repository seems to keep throwing an exception saying "Operation is not supported for read-only collection" when sending a request to a Put endpoint. All other repositories seem to work just fine. Here's my code:
User.kt
package com.karbal.tutortek.entities
import com.karbal.tutortek.dto.userDTO.UserPostDTO
import java.sql.Date
import javax.persistence.*
#Entity
#Table(name = "users")
data class User(
#Id
#Column(name = "id", nullable = false)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_generator")
#SequenceGenerator(name = "user_generator", sequenceName = "user_seq", allocationSize = 1)
var id: Long? = null,
#Column(name = "firstName", nullable = false)
var firstName: String = "",
#Column(name = "lastName", nullable = false)
var lastName: String = "",
#Column(name = "birthDate", nullable = false)
var birthDate: Date = Date(System.currentTimeMillis()),
#Column(name = "rating", nullable = false)
var rating: Float = 0.0F,
#OneToMany(mappedBy = "user")
var payments: List<Payment> = listOf(),
#OneToMany(mappedBy = "user")
var topics: List<Topic> = listOf()
){
constructor(userPostDTO: UserPostDTO) : this(
null,
userPostDTO.firstName,
userPostDTO.lastName,
userPostDTO.birthDate,
userPostDTO.rating
)
fun copy(user: User){
firstName = user.firstName
lastName = user.lastName
birthDate = user.birthDate
rating = user.rating
payments = user.payments
topics = user.topics
}
}
UserController.kt
package com.karbal.tutortek.controllers
import com.karbal.tutortek.dto.userDTO.UserGetDTO
import com.karbal.tutortek.dto.userDTO.UserPostDTO
import com.karbal.tutortek.entities.User
import com.karbal.tutortek.services.UserService
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.*
import org.springframework.web.server.ResponseStatusException
import java.util.*
#RestController
class UserController(val userService: UserService) {
#PostMapping("/users/add")
fun addUser(#RequestBody userDTO: UserPostDTO): UserGetDTO {
val user = User(userDTO)
return UserGetDTO(userService.saveUser(user))
}
#DeleteMapping("/users/{id}")
fun deleteUser(#PathVariable id: Long){
val user = userService.getUser(id)
if(user.isEmpty) throw ResponseStatusException(HttpStatus.NOT_FOUND, "User not found")
userService.deleteUser(id)
}
#GetMapping("/users/all")
fun getAllUsers() = userService.getAllUsers().map { u -> UserGetDTO(u) }
#GetMapping("/users/{id}")
fun getUser(#PathVariable id: Long): UserGetDTO {
val user = userService.getUser(id)
if(user.isEmpty) throw ResponseStatusException(HttpStatus.NOT_FOUND, "User not found")
return UserGetDTO(user.get())
}
#PutMapping("/users/{id}")
fun updateUser(#PathVariable id: Long, #RequestBody userDTO: UserPostDTO){
val user = User(userDTO)
val userInDatabase = userService.getUser(id)
if(userInDatabase.isEmpty) throw ResponseStatusException(HttpStatus.NOT_FOUND, "User not found")
val extractedUser = userInDatabase.get()
extractedUser.copy(user)
userService.saveUser(extractedUser)
}
}
UserService.kt
package com.karbal.tutortek.services
import com.karbal.tutortek.entities.User
import org.springframework.stereotype.Service
import com.karbal.tutortek.repositories.UserRepository
#Service
class UserService(val database: UserRepository) {
fun getAllUsers(): List<User> = database.getAllUsers()
fun saveUser(user: User) = database.save(user)
fun deleteUser(id: Long) = database.deleteById(id)
fun getUser(id: Long) = database.findById(id)
}
UserRepository.kt
package com.karbal.tutortek.repositories
import com.karbal.tutortek.entities.User
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.CrudRepository
import org.springframework.stereotype.Repository
#Repository
interface UserRepository : CrudRepository<User, Long> {
#Query("SELECT * FROM users", nativeQuery = true)
fun getAllUsers(): List<User>
}
UserPostDTO.kt
package com.karbal.tutortek.dto.userDTO
import java.sql.Date
data class UserPostDTO(
var firstName: String,
var lastName: String,
var rating: Float,
var birthDate: Date
)
The JSON that I send:
{
"firstName": "Thomas",
"lastName": "Thompson",
"rating": 4.7,
"birthDate": "2000-02-03"
}
Post works fine. Put works fine on other entities in my code. But here it always responds with 500 and a message "Operation is not supported for read-only collection". Any ideas why this could be happening?
Just solved this after a couple of hours. Changed lists in User class to mutable lists and now it works fine.
I have a Kotlin Spring Boot application with these following entities:
import com.fasterxml.jackson.annotation.JsonIgnore
import javax.persistence.*
#Entity
#Table(name = "users")
class Users(
#Id
var username: String = "",
#JsonIgnore
var password: String = "",
#JsonIgnore
var enabled: Boolean = false,
#ElementCollection
#CollectionTable(name = "authorities", joinColumns = [JoinColumn(name = "username")])
#MapKeyColumn(name = "users")
private var authorities : MutableSet<Authorities> = mutableSetOf()
)
and
#Embeddable
class Authorities(
var authority: String
)
When I call this endpoint:
#GetMapping("/users")
fun getUserById(#RequestParam(value = "username") username : String) : ResponseEntity<Users>{
val optionalUser = usersService.getByUsername(username)
return if(optionalUser.isPresent) ResponseEntity.ok(optionalUser.get())
else ResponseEntity(HttpStatus.BAD_REQUEST)
}
it returns correctly, as you can see in this json:
{
"username": "test#gmail.com"
}
But it doesn't return the list of authorities that belongs to this user, and I wanted to have it returned. My question is: How could I do it?
Thank you so much for helping
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
I need to save 2 models at the same time in the transaction, but...
org.postgresql.util.PSQLException: ERROR: null value in column "book_id" violates not-null constraint
I don't understand what is going wrong. My models:
a) Chapter
#Entity
class Chapter() : AuditModel() {
constructor(number: Int, title: String) : this() {
this.number = number
this.title = title
}
#Column(nullable = false)
var number: Int? = null
#Column(nullable = false)
lateinit var title: String
#Column(nullable = false)
var progress: Int = 0
#ManyToOne
#JoinColumn(name = "book_id")
lateinit var book: Book
}
b) Book
#Entity
class Book() : AuditModel() {
constructor(title: String, author: String) : this() {
this.title = title
this.author = author
}
#Column(nullable = false)
lateinit var title: String
#Column(nullable = false)
lateinit var author: String
#OneToMany(mappedBy = "book", cascade = [CascadeType.PERSIST])
val chapters: MutableSet<Chapter> = HashSet()
}
And function where I save models:
#Transactional
fun createBook(title: String, author: String): Boolean {
val book = Book(title, author)
val chapter = Chapter(1, "Example - 1")
book.chapters.add(chapter)
return bookRepository.save(book) != null
}
How to fix it? I'm new in Spring and it's totally incomprehensible to me.