This is my node entity class:
#NodeEntity
class SutStateEntity(
#Id
#GeneratedValue
#Index(unique = true)
val id: String) {
def this(sutStateIdentifier: SutStateIdentifier) = this(sutStateIdentifier.hash)
def this() = this("")
}
I am using the unique hash value of sutStateIdentifier as my id.
When I store the a SutStateEntity inside of a transaction:
val sutStateEntity = new SutStateEntity(sutStateIdentifier)
session.save(sutStateEntity)
I get the following exception:
Exception in thread "main" org.neo4j.ogm.exception.core.MappingException: `Field with primary id is null for entity ....SutStateEntity#34aa8b61`
I have read that this error occurs if I have not specified a default constructor which I did.
edit:
The following example works:
#Id
#GeneratedValue
var id: java.lang.Long = 0L
I guess, I have to change the field id to var but it still does not work if I use a String. Not even with a java.lang.String.
The assigment is wrong. This works:
#Id
#GeneratedValue
var id: java.lang.Long
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.
I am working on Cassandra Spring-data module using Scala. I have created domain model & setup crud repository.
Spring data repository is setup as below:
#Repository
trait SampleRepository extends CassandraRepository[Sample, SampleKey]
Table domain model is setup as below:
#Table
case class Sample(#PrimaryKey
sampleKey: SampleKey,
#Column("surName")
surName: String)
Primary Key is composite key. Id column as Partition key. name column as Cluster key
#PrimaryKeyClass
case class SampleKey(
#PrimaryKeyColumn( `type` = PrimaryKeyType.PARTITIONED, ordinal = 0, name = "id")
id: Int,
#PrimaryKeyColumn( `type` = PrimaryKeyType.CLUSTERED, ordinal = 1, name = "name")
name: String
)
In scala type is a valid keyword hence used backward quotes. I am expecting this to work but when I start the application but I get
Suppressed: org.springframework.data.mapping.MappingException: Composite primary key type [com.barclays.decisionstore.trac.model.SampleKey] has no fields annotated with #PrimaryKeyColumn
at org.springframework.data.cassandra.core.mapping.PrimaryKeyClassEntityMetadataVerifier.verify(PrimaryKeyClassEntityMetadataVerifier.java:91)
... 262 common frames omitted
Suppressed: org.springframework.data.mapping.MappingException: At least one of the #PrimaryKeyColumn annotations must have a type of PARTITIONED
at org.springframework.data.cassandra.core.mapping.PrimaryKeyClassEntityMetadataVerifier.verify(PrimaryKeyClassEntityMetadataVerifier.java:98)
... 262 common frames omitted
This means type parameter to annotation is not recognized by Spring-Data.
The same code with java model classes works without issue.
Would appreciate any pointers or substitute to solve this.
Actually java bean properties not recognized other than annotations.
Scala does not bind instance variables as bean properties by default at case classes. For this you need to add #BeanProperty annotations to each properties.
import scala.beans.BeanProperty
#PrimaryKeyClass
case class SampleKey(#BeanProperty
#PrimaryKeyColumn(`type` = PrimaryKeyType.PARTITIONED, ordinal = 0, name = "id")
id: Int,
#BeanProperty
#PrimaryKeyColumn(`type` = PrimaryKeyType.CLUSTERED, ordinal = 1, name = "name")
name: String)
Also if you get an object construction error, add a constructor with default values (this requires to make variables var):
#PrimaryKeyClass
case class SampleKey(#BeanProperty
#PrimaryKeyColumn(`type` = PrimaryKeyType.PARTITIONED, ordinal = 0, name = "id")
var id: Int,
#BeanProperty
#PrimaryKeyColumn(`type` = PrimaryKeyType.CLUSTERED, ordinal = 1, name = "name")
var name: String) {
def this() = this(0, "")
}
Hope this helps!
Its working when used with class instated of case class.
#PrimaryKeyClass
class SimpleKey{
#PrimaryKeyColumn( `type` = PrimaryKeyType.PARTITIONED, ordinal = 0, name = "id")
var id: Int =_
#PrimaryKeyColumn( `type` = PrimaryKeyType.CLUSTERED, ordinal = 1, name = "name")
var name: 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 = _
}