Scala's #BeanProperty for Options - scala

Is it possible to implement BeanProperty for Optional variables? It would be useful with JPA.
It would be great if:
#BeanProperty var status: Option[String]
would add the following methods to the class:
def setStatus(s: String) { status = Some(s) }
def getStatus: String = status.get

Unfortunately not, but there is a simple workaround that brings best of both worlds:
#BeanProperty
var status: String
def statusOption = Option(status)
Note that JPA does not understand Option[T]. BTW if you use field-access as opposed to getter/setter access in JPA, #BeanProperty isn't even needed - the JPA provider will scan Java fields instead.

Related

What is the best way modeling in Scala with Spring Data JPA

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
)

Scala case class constructor error when used as POJO strategy in Ignite persistence

I have a case class in Scala as so:
class StateCache(greeting: String, first_name: String, last_name: String)
I am using Spark + Ignite to have a write through cache that persists to Cassandra. When setting the persistence-settings.xml file to:
<persistence keyspace="testing_ignite" table="people_test">
<keyPersistence class="java.lang.Long" strategy="PRIMITIVE" column="index"/>
<valuePersistence class="StateCache" strategy="POJO"/>
</persistence>
I receive the following error:
Java class 'StateCache' couldn't be used as POJO cause it doesn't have no arguments constructor. I have added in an empty constructor but it does not resolve the error. Any help would be much appreciated.
According to the documentation for POJO (highlighting is mine):
Stores each field of an object as a column having corresponding type in Cassandra table. Provides ability to utilize Cassandra secondary indexes for object fields. Could be used only for POJO objects following Java Beans convention and having their fields of simple java type which could be directly mapped to corresponding Cassandra types.
Your class StateCache does not follow Java Beans spec. To fix this you need:
declare all the fields as var (instead of val)
use the #BeanProperty annotation on them
declare an empty constructor
The code would go like this:
class StateCache(#BeanProperty var greeting: String,
#BeanProperty var first_name: String,
#BeanProperty var last_name: String) {
def this() {
this(null,null,null);
}
}
This is not an idiomatic Scala but this is what you need to interact with the world that uses Java conventions.

Salat serialisation error

I'm currently having issues with Salat. Hope you guys can help me!
Here's the case class that is driving me crazy:
object UserDAO extends SalatDAO[User, ObjectId](
collection = DB("users") //Returns the "users" MongoCollection
)
case class User(
_id: ObjectId = new ObjectId,
firstName: String,
lastName: String,
screenName: String,
phoneNumber: PhoneNumber,
validated: Boolean = false)
PhoneNumber is an instance of type com.google.i18n.phonenumbers.Phonenumber$PhoneNumber (I'm using libphonenumber)
This is my custom transformer:
class PhoneNumberTransformer extends CustomTransformer[PhoneNumber, String] {
val phoneNumberUtils = PhoneNumberUtil.getInstance()
def deserialize(b: String) = phoneNumberUtils.parse(b, "UK")
def serialize(a: PhoneNumber) = phoneNumberUtils.format(a, PhoneNumberFormat.INTERNATIONAL)
}
This is my custom context:
package object model {
implicit val ctx = new Context {
val name = "Custom Salat Context"
}
ctx.registerCustomTransformer(new PhoneNumberTransformer)
}
If I try to insert a new User document using UserDAO, I get this exception:
project java.lang.IllegalArgumentException: can't serialize class com.google.i18n.phonenumbers.Phonenumber$PhoneNumber
project at org.bson.BasicBSONEncoder._putObjectField(BasicBSONEncoder.java:284)
project at org.bson.BasicBSONEncoder.putObject(BasicBSONEncoder.java:185)
project at org.bson.BasicBSONEncoder.putObject(BasicBSONEncoder.java:131)
project at com.mongodb.DefaultDBEncoder.writeObject(DefaultDBEncoder.java:33)
[...]
Any idea on how to solve this?
Thanks
Salat developer here. I'm not familiar with libphonenumber but this is most likely breaking down because it looks like you're trying to serialize an inner class.
Something to try. If you copy pasted the PhoneNumber class to the top level of a local package (not inside an object, trait, or class), extending the relevant class/interface that bring the i18n goodness, and changed the type param to point at this class instead, does it work?
If so, the problem is that Salat doesn't support inner classes. If not, we'll have to look further.
You cannot serialize Java classes directly to Salat. You need to write either a custom salat serializer or write PhoneNumber as a case class

How can one use Amazon's DynamoDBMapper in Scala?

I'm having trouble using Amazon's DynamoDBMapper in Scala code. The main sticking point is getting the JVM to recognize #DynamoDBHashkey when it is used in a case class, like:
case class MyCoolCaseClass(#DynamoDBHashKey(attributeName = "my_id") myId: String) {}
Any pointers from someone who has integrated this client library into a Scala project? (I'm hoping to not simply fallback to the low-level API, though that may be a decent decision once exhausting my options with the Mapper).
I had to do this:
import annotation.meta.beanGetter
import beans.BeanProperty
import com.amazonaws.services.dynamodbv2.datamodeling._
#DynamoDBTable(tableName="DEMOTAB")
case class DemoItem( // it's a case class for the free stuff, but can be ordinary class
#(DynamoDBHashKey #beanGetter) // would not work without meta annotation
#BeanProperty var id:String, // must be var or mapper can't instantiate one
#BeanProperty var number:Integer
) {
def this() = this(null, null) // needed by DynamoDB Mapper to instantiate
}
The DynamoDB mapper uses reflection to find the getters and setters. The SDK assumes Java-style conventions, i.e. that your getters and setters start with "get" or "is", and setters start with "set". You can see the reflection code on github.
I've been able to get it working, but it feels just like writing Java :(
#DynamoDBTable(tableName = "awesome_table")
class TheBestClass {
private var id : Integer = _
#DynamoDBHashKey
def getId() = id
def setId(_id: Integer) = id = _id
}
This works for me, including the boolean
#DynamoDBTable(tableName = "User")
case class User(
#(DynamoDBHashKey #field)
#(DynamoDBAutoGeneratedKey #field)
#BeanProperty var id: String,
#(DynamoDBAttribute #field)
#BeanProperty var firstName: String,
#(DynamoDBAttribute #field)
#BeanProperty var lastName: String,
#(DynamoDBAttribute #field)
#BeanProperty var active: Boolean
)
{
def this() = this(null, null, null, false)
}

Setting format of setters & getters in Scala case classes

In Scala, I have a case class:
case class MonthSelectionInfo(monthSelection: MonthSelection.Value, customMonth:Int = 0, customYear:Int = 0) {
def this(monthSelection: MonthSelection.Value) = {
this(monthSelection, 0, 0)
}
}
object MonthSelection extends Enumeration {
type MonthSelection = Value
val LastMonth, ThisMonth, NextMonth, CustomMonth = Value
}
When I have an instance of the case class, I have to use
myMonthSelectionInfo.monthSelection
and
myMonthSelectionInfo.eq(newMonthSelection)
to get & set the MonthSelection instance contained within.
Is there any nice Scala way to format the getter & setter to look more like regular Java POJOs? e.g.
myMonthSelectionInfo.setMonthSelection(newMonthSelection)
There is #BeanProperty annotation to generate getters and setters for fields.
case class MonthSelectionInfo(#reflect.BeanProperty var monthSelection: MonthSelection.Value)
scala> val ms = MonthSelectionInfo(MonthSelection.LastMonth)
ms: MonthSelectionInfo = MonthSelectionInfo(LastMonth)
scala> ms.setMonthSelection(MonthSelection.ThisMonth)
sscala> ms.getMonthSelection
res4: MonthSelection.Value = ThisMonth
In object oriented programming, getters and setters are something that most would agree have some real world benefits. Unfortunately, they can sometimes be annoying to write. They usually do not consist of a lot of code , but when you have the write the same thing over and over and over it gets old really fast. In my experience, most getters and setters are very much alike so it stands to reason that there must be a “better” way to accomplish the same result.
This link may help you.
We can use #BeanProperty in scala 2.11
import scala.beans.BeanProperty
case class Employee(#BeanProperty id: Long, #BeanProperty name: String, #BeanProperty age: Long)