What I want is to have two user types that inherit from one user super class. When I'm authenticating a user through username/password I don't care who they are at that point. However once they start making requests once logged in it will become important then.
I don't know what I should be using for the inheritance type and the repositories in kotlin for this.
#MappedSuperClass
open class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
val Id: Long = 0
val username:String = ""
val password:String = ""
}
type1
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
data class Type1(
val property1: String) : User
{
val property2: String = ""
}
type2
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
data class Type2(
val property1: String) : User
{
val property2: String = ""
}
What I'd like to have is a user repository to be able to look at user name and password, and then repositories for the other two to get the specific properties of each. I tried
#Repository
interface UserRepository: JpaRepository<User, Long> {
fun getUserByUsername(username: String)
}
and then
#Repository
interface Type1Repository: UserRepository<Type1, Long>
but it wouldn't accept the <> in the type 1 repository.
I haven't found a good resource talking about this for kotlin yet. If you know of one please pass it along. Thanks.
Like shown in here: https://ideone.com/JmqsDn you are just missing the types in your intermediate interface, ie.:
interface UserRepository<User, Long>: JpaRepository<User, Long> {
fun getUserByUsername(username: String)
}
Side note: kotlin 1.1+ is required for data classes to inherit from other classes.
Related
I'm trying to update an object. As part of the process I load the old one from the database and "transfer" its property values to a new object.
But when I try to persist the new object I get the following error:
org.hibernate.PersistentObjectException: detached entity passed to persist: some.package.domain.Exercise
My entity looks as the following
#Entity
class Exercise(
val name: String,
val description: String,
#JsonIgnore
#ManyToOne
val creator: User,
#OneToMany(fetch = EAGER)
#Fetch(SUBSELECT)
val videos: MutableList<Video> = mutableListOf(),
#Id
#GeneratedValue(strategy = SEQUENCE)
val id: Long = 0
)
My code for persisting looks as the following
#Singleton
#Transactional
class ExerciseServiceDefault(override val repository: ExerciseRepository,
private val entityManager: EntityManager) : ExerciseService {
override fun update(id: Long, exercise: Exercise): Exercise {
val existing = get(id)
val new = Exercise(exercise.name, exercise.description, existing.creator, existing.videos, existing.pictures, id)
return repository.save(new)
// return entityManager.merge(new)
}
...
If I change the above code to use entityManager.merge(new) everything works just fine. But I'd rather not have to inject the entityManager.
Any clue about how I can make the updating happen using my repository?
I've tried adding cascade = [MERGE] to my relations to no avail.
Scala does not get first class support as Kotlin in Spring.
I tried to create a Spring Boot API application with Scala.
Spring Boot 2.2.0.M5
Spring Data JPA
H2
Scala 2.13
I created a JPA Entity with case class like:
#Entity
case class Post(#BeanProperty title: String, #BeanProperty content: String) {
def this() {
this(null, null)
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#BeanProperty
var id: Long = _
#BeanProperty
val createdOn: LocalDateTime = LocalDateTime.now()
override def toString: String = s"Post[id:$id, title:$title, content:$content, createdOn:$createdOn]"
}
And create a Repository using trait, it works.
trait PostRepository extends JpaRepository[Post, Long]
I want to try bean validation.
class PostForm {
#NotNull
#NotEmpty
#BeanProperty var title: String = _
#BeanProperty var content: String = _
}
And in the controller, create a POST method like:
#PostMapping
def save(#RequestBody #Valid form: PostForm, errors: BindingResult) = errors.hasErrors match {
case true => {
badRequest().build()
}
case _ => {
val data = Post(title = form.title, content = form.content)
val saved = posts.save(data)
created(ServletUriComponentsBuilder.fromCurrentContextPath().path("/{id}").buildAndExpand(saved.id).toUri).build()
}
}
It works.
But the model classes are little tedious. I am trying to use a case class like the following:
case class PostForm(#NotNull #NotEmpty #BeanProperty title: String, #BeanProperty content: String)
The validation does not work.
When we are modeling for JPA etc, case class or generic class is better?
Why we can not apply the Bean Validation annotations as Kotlin data clase in the case class?
Update: Got this work like:
case class PostForm(#(NotNull#field) #(NotEmpty#field) #BeanProperty title: String, #BeanProperty content: String)
The source codes is hosted on my Github.
Case class fields are considered as vals by default, which means you can't set a new value to them. #BeanProperty, however, is to automatically generate field setters and getters.
You may try adding var keywords to the fields explicitly.
case class PostForm(
#NotNull #NotEmpty #BeanProperty var title: String,
#BeanProperty var content: String
)
My User model
#Entity
#Table(name="users")
data class User(
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = -1,
#Column(unique=true)
val username: String) : Serializable
Two records in the database
The query is fine.
[2018-02-23T14:32:07.066+0100] [Payara 4.1] [FINE] []
[org.eclipse.persistence.session./file:/Users/youri/Downloads/payara41/glassfish/domains/domain1/applications/Kwetter-1.0-SNAPSHOT/WEB-INF/classes/_kwetter.sql]
[tid: _ThreadID=28 _ThreadName=http-thread-pool::http-listener-1(4)]
[timeMillis: 1519392727066] [levelValue: 500] [[ SELECT ID, USERNAME
FROM users]]
But it outputs two empty objects, instead of two User objects
[{},{}]
Abstract Dao where I use the Entity Manager
#Stateless
abstract class Abstract<T : Serializable> {
#PersistenceContext
private lateinit var entityManager: EntityManager
abstract fun getEntityClass(): Class<T>
open fun find(id: Long): T {
return entityManager.find(getEntityClass(), id)
}
// this returns the weird two empty objects
open fun all(): List<T> {
val builder = entityManager.criteriaBuilder
val c = builder.createQuery(getEntityClass())
c.from(getEntityClass())
val query = entityManager.createQuery(c)
return query.resultList
}
}
You should use var instead of val since Kotlin won't make a setter for val fields. JPA needs to have mutable properties (which means getters & setters).
TLDR: use var instead of val
Spring Data JPA introduces a nice feature, "query by example" (QBE). You express your search criteria by constructing an instance of the entity.
You do not have to write JPQL. It uses less "magic" than does repository query derivation. The syntax is nice. It prevents explosions of trivial repository code. It survives refactors very well.
There's a problem though: QBE only works if you can partially construct an object.
Here's my entity:
#Entity
#Table(name="product")
data class Product(
#Id val id: String,
val city: String,
val shopName: String,
val productName: String,
val productVersion: Short
)
Here's my repository (empty! this is a nice thing about QBE):
#Repository
interface ProductRepository : JpaRepository<Product, String>
And here's how you would fetch a List<Product> — all the products that are sold in some shop, in some city:
productRepository.findAll(Example.of(Product(city = "London", shopName="OkayTea")))
Or at least, that's what I want to do. There's a problem. It's not possible to construct this object:
Product(city = "London", shopName="OkayTea")
This is because Product's constructor requires that all its fields be defined. And indeed: that's what I want most of the time.
The usual compromise in Java would be: construct entities using no-args constructor, make everything mutable, have no guarantees about completedness.
Is there a nice Kotlin pattern to solve this problem:
generally require that all args are instantiated on construction
provide also some mechanism to produce partially-constructed instances for use with Example API
Admittedly these look like totally conflicting goals. But maybe there's another way to approach this?
For example: maybe we can make a mock/proxy object, which appears to be a Product, but doesn't have the same construction constraints?
You can query by example using kotlin data classes with non-null fields, however it will not look as good as java code.
val matcher = ExampleMatcher.matching()
.withMatcher("city", ExampleMatcher.GenericPropertyMatcher().exact())
.withMatcher("shopName", ExampleMatcher.GenericPropertyMatcher().exact())
.withIgnorePaths("id", "productName", "productVersion")
val product = Product(
id = "",
city = "London",
shopName = "OkayTea",
productName = "",
productVersion = 0
)
productRepository.findAll(Example.of(product, matcher))
If you are using it for integration tests and you don't want to pollute your Repository interface with methods which are only used in said tests and also
you have a lot of fields in the database entity class, you can create an extension function that extracts fields which will be ignored in the query.
private fun <T : Any> KClass<T>.ignoredProperties(vararg exclusions: String): Array<String> {
return declaredMemberProperties
.filterNot { exclusions.contains(it.name) }
.map { it.name }
.toTypedArray()
}
and use it like this:
val ignoredFields = Product::class.ignoredProperties("city", "shopName")
val matcher = ExampleMatcher.matching()
.withMatcher("city", ExampleMatcher.GenericPropertyMatcher().exact())
.withMatcher("shopName", ExampleMatcher.GenericPropertyMatcher().exact())
.withIgnorePaths(*ignoredFields)
because the parameters on the primary constructor is not optional and not nullable. you can makes parameters nullable and set a default value null for each, for example:
#Entity
#Table(name = "product")
data class Product(
#Id val id: String? = null,
val city: String? = null,
val shopName: String? = null,
val productName: String? = null,
val productVersion: Short? = null
)
However, you must operates Product properties with safe-call ?., for example:
val product = Product()
// safe-call ---v
val cityToLowerCase = product.city?.toLowerCase()
I have scala class like:
#Entity("users")
class User(#Required val cid: String, val isAdmin: Boolean = false, #Required val dateJoined: Date = new Date() ) {
#Id var id: ObjectId = _
#Reference
val foos = new ArrayList[Foo]
}
If it was a Java class I would simply put implements java.io.Serializable but this does not work in scala. Also is foos as declared above is private or public?
How do I use a #serializable scala object?
foos is public unless marked otherwise
scala 2.9.x also have an interface named Serializable, you may extends or mixin this. before 2.9.x the #serializable is the only choice.
You can add Serialized annotation on your Scala Class (at JPA Entity for example):
Because Serializable is a trait, you can mix it into a class, even if
your class already extends another class:
#SerialVersionUID(114L)
class Employee extends Person with Serializable ...
Se more details at this link:
https://www.safaribooksonline.com/library/view/scala-cookbook/9781449340292/ch12s08.html
An example of my Entity (JPA) class writed in scala, using Serialized properties:
import javax.persistence._
import scala.beans.BeanProperty
import java.util.Date
#SerialVersionUID(1234110L)
#Entity
#Table(name = "sport_token")
class Token() extends Serializable {
#Id
#SequenceGenerator(name="SPORT_TOKEN_SEQ",catalog="ESPORTES" , sequenceName="SPORT_TOKEN_SEQ", allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE , generator="SPORT_TOKEN_SEQ")
#BeanProperty
var id: Int = _
#BeanProperty
#Column(name="token")
var token: String = _
#BeanProperty
#Column(name="active")
var active: Int = _
}