Ebean not persisting foreign keys - scala

I have a simple Entity class like;
case class Place(var name: String) extends Model with GeoLocPoint with HasGeoLoc with ContainsTime with HasId {
var number: String = _ // Building number on the street.
var placeGroupId: Long = _
var chainId: Long = _
#OneToOne
#JoinColumn(name = "logo_id")
var logo: Image = _
#OneToOne
#JoinColumn(name = "cover_id")
var cover: Image = _
...
}
And the image class is;
package models.images
import javax.persistence.Entity
import models.HasId
import models.places.placegroups.places.{Place, LinkedToPlace}
import play.api.libs.json._
import play.db.ebean.Model
import play.db.ebean.Model.Finder
/**
* Created by asheshambasta on 31/12/14.
*/
#Entity
case class Image(
filePath: String,
src: String,
format: String,
role: String // C: cover, L: logo, G: gallery
) extends Model with HasId with LinkedToPlace {
var caption: String = _
}
Now when a place is created, I'm attaching a default image to it;
newPlace.save
val imgDir = Play.current.configuration.getString("static.dir").getOrElse("/tmp")
val imgUrl = Play.current.configuration.getString("static.url.img").getOrElse("/static/img")
val imgType = Play.current.configuration.getString("img.default.type").getOrElse("image/jpeg")
val coverFile = Play.current.configuration.getString("img.default.cover.file").getOrElse("default-cover.jpg")
val logoFile = Play.current.configuration.getString("img.default.cover.file").getOrElse("default-cover.jpg")
val cover = new Image(imgDir + "/" + coverFile, imgUrl + "/" + coverFile, imgType, "C")
cover.place = newPlace
cover.save
val logo = new Image(imgDir + "/" + logoFile, imgUrl + "/" + logoFile, imgType, "L")
logo.place = newPlace
logo.save
newPlace.cover = cover
newPlace.logo = logo
newPlace.update
But then every time, I see that the images get persisted correctly with the right place_id but the place logo and the cover don't. They stay null.
This seems straight forward, and yet it doesn't work. Does anyone have any pointers to why?

There are some innacuracies in provided code. There is
logo.place = newPlace
but there is no place field in Image class.
There is also place_id column mentioned but I cannot see it in the code. But there are logo_id and cover_id columns. When you do one-to-one relation you should add join-column only on one side of this relation. So it should be only logo_id and cover_id columns on Place table. Adding only place_id column on Image table would not work because there would be two rows in Image table with the same place_id and this would be impossible to distinguish which should be mapped to logo field and which to cover field.
I made some corrections and simplifications to make this code compile and work.
Place.scala:
#Entity
class Place( var aId: Int, var aNumber: String) extends Model{
#Id
val id:Int=aId
#Column(name="number")
var number: String = aNumber
#OneToOne
#JoinColumn (name = "logo_id")
var logo: Image = _
#OneToOne
#JoinColumn(name = "cover_id")
var cover: Image = _
}
Image.class:
#Entity
#Table(name = "image")
class Image(var aId: Int, var aCaption: String) extends Model {
#Id
val id:Int=aId
#Column(name="caption")
var caption: String = aCaption
}
test method:
"EbeanTest" should {
"placeTest" in new WithApplication {
val newPlace = new Place(1, "aaa")
newPlace.save
val logo: Image = new Image(1, "111");
logo.save
val cover: Image = new Image(2, "222");
cover.save
newPlace.logo=logo
newPlace.cover=cover
newPlace.update
val cList = Ebean.find(classOf[Place]).findList()
for (
element <- cList
) println(element.id+" "+element.number+" "+element.logo.id+" "+element.cover.id)
}
}

Related

Converting a data class to a #MappedEntity data class in Micronaut using Kotlin

Firstly, I'm new to micronaut and kotlin.
I'm trying to convert a data class that I receive as a request body to my API. I need to persist data from this json into a Postgres table. I wasn't able to add #field annotation to a #MappedEntity data class to validate empty fields with a message. Hence, I decided to use a normal data class to parse the request body and then create a new object of the #MappedEntity class to run the save query.
The issue:
I have a #GeneratedValue Id column in my mapped Entity class. When I try to create a new object of this class, I'm not able to leave the Id column blank.
Controller:
#Post("/contribute")
#Status(HttpStatus.CREATED)
fun processIndividualContribution(
#Valid #Body individualContributionRequestTO: IndividualContributionRequestTO
): Mono<IndividualContributionDTO>
Request Body Data CLass:
#JsonIgnoreProperties(ignoreUnknown = true)
data class IndividualContributionRequestTO(
#field: NotEmpty(message = "Transaction Id cannot be null") val transactionId: String,
#field: NotEmpty(message = "First Name can't be null") val firstName: String,
val lastName: String? = null,
#field: NotEmpty(message = "Email address can't be null") val emailAddress: String,
#field: NotEmpty(message = "Individual Contribution can't be null") val contribution: String,
)
{
fun toPurchasedDataEntity(individualContributionRequestTO: IndividualContributionRequestTO): PurchaseDataAll{
return PurchaseDataAll(
purchaserFirstName = individualContributionRequestTO.firstName,
purchaserLastName = individualContributionRequestTO.lastName,
purchaserEmail = individualContributionRequestTO.emailAddress,
contribution = individualContributionRequestTO.contribution
)
}
}
Mapped Entity Class:
#MappedEntity
#Table(name="purchased_all")
data class PurchaseDataAll (
#Id
#GeneratedValue
#Column(name = "purchase_id")
val purchaseId: Int,
#Column(name = "transaction_id")
val transactionId: String,
#Column(name = "purchaser_first_name")
val purchaserFirstName: String,
#Column(name = "purchaser_last_name")
val purchaserLastName: String? = null,
#Column(name = "purchaser_email")
val purchaserEmail: String,
#Column(name = "contribution")
val contribution: String,
#DateCreated
#Column(name = "created_ts")
var createdTs: LocalDateTime? = null,
#DateUpdated
#Column(name = "updated_ts")
var updatedTs: LocalDateTime? = null
)
The function toPurchasedDataEntity doesn't compile due to the missing purchaseId field in the returned object.
Is there a way I can parse the request body data class to the mapped entity class by ignoring the auto generated field?
In Kotlin, you'll have to prefix annotations witn field: like shown below. I also changed def for purchaseId so you don't have to specify it when mapping from view class (DTO) to entity class.
IMHO, I think it's a good approach to separate entity classes from view classes as you've done in your question.
#MappedEntity
#Table(name="purchased_all")
data class PurchaseDataAll (
#field:Id
#field:GeneratedValue
#field:Column(name = "purchase_id")
var purchaseId: Int? = null,
#field:Column(name = "transaction_id")
val transactionId: String,
#field:Column(name = "purchaser_first_name")
val purchaserFirstName: String,
#field:Column(name = "purchaser_last_name")
val purchaserLastName: String? = null,
#field:Column(name = "purchaser_email")
val purchaserEmail: String,
#field:Column(name = "contribution")
val contribution: String,
#field:DateCreated
#field:Column(name = "created_ts")
var createdTs: LocalDateTime? = null,
#field:DateUpdated
#field:Column(name = "updated_ts")
var updatedTs: LocalDateTime? = null
)

spark groupBy operation hangs at 199/200

I have a spark standalone cluster with master and two executors. I have an RDD[LevelOneOutput] and below is LevelOneOutput class
class LevelOneOutput extends Serializable {
#BeanProperty
var userId: String = _
#BeanProperty
var tenantId: String = _
#BeanProperty
var rowCreatedMonth: Int = _
#BeanProperty
var rowCreatedYear: Int = _
#BeanProperty
var listType1: ArrayBuffer[TypeOne] = _
#BeanProperty
var listType2: ArrayBuffer[TypeTwo] = _
#BeanProperty
var listType3: ArrayBuffer[TypeThree] = _
...
...
#BeanProperty
var listType18: ArrayBuffer[TypeEighteen] = _
#BeanProperty
var groupbyKey: String = _
}
Now I want to group this RDD based on userId, tenantId, rowCreatedMonth, rowCreatedYear. For that I did this
val levelOneRDD = inputRDD.map(row => {
row.setGroupbyKey(s"${row.getTenantId}_${row.getRowCreatedYear}_${row.getRowCreatedMonth}_${row.getUserId}")
row
})
val groupedRDD = levelOneRDD.groupBy(row => row.getGroupbyKey)
This gives me the data in key as String and value as Iterable[LevelOneOutput]
Now I want to generate one single object of LevelOneOutput for that group key. For that I was doing something like below:
val rdd = groupedRDD.map(row => {
val levelOneOutput = new LevelOneOutput
val groupKey = row._1.split("_")
levelOneOutput.setTenantId(groupKey(0))
levelOneOutput.setRowCreatedYear(groupKey(1).toInt)
levelOneOutput.setRowCreatedMonth(groupKey(2).toInt)
levelOneOutput.setUserId(groupKey(3))
var listType1 = new ArrayBuffer[TypeOne]
var listType2 = new ArrayBuffer[TypeTwo]
var listType3 = new ArrayBuffer[TypeThree]
...
...
var listType18 = new ArrayBuffer[TypeEighteen]
row._2.foreach(data => {
if (data.getListType1 != null) listType1 = listType1 ++ data.getListType1
if (data.getListType2 != null) listType2 = listType2 ++ data.getListType2
if (data.getListType3 != null) listType3 = listType3 ++ data.getListType3
...
...
if (data.getListType18 != null) listType18 = listType18 ++ data.getListType18
})
if (listType1.isEmpty) levelOneOutput.setListType1(null) else levelOneOutput.setListType1(listType1)
if (listType2.isEmpty) levelOneOutput.setListType2(null) else levelOneOutput.setListType2(listType2)
if (listType3.isEmpty) levelOneOutput.setListType3(null) else levelOneOutput.setListType3(listType3)
...
...
if (listType18.isEmpty) levelOneOutput.setListType18(null) else levelOneOutput.setListType18(listType18)
levelOneOutput
})
This is working as expected for small size of input, but when I try to run on the larger set of input data, group by operation is getting hang at 199/200 and I don't see any specific error or warning in stdout/stderr
Can some one point me why the job is not proceeding further...
Instead of using groupBy operation I have created paired RDD like below
val levelOnePairedRDD = inputRDD.map(row => {
row.setGroupbyKey(s"${row.getTenantId}_${row.getRowCreatedYear}_${row.getRowCreatedMonth}_${row.getUserId}")
(row.getGroupByKey, row)
})
and updated the processing logic, which solved my issue.

Scala: JavaFx Tableview does not display data

So I have tried to load in some data into my Tableview but it doesnt work because every time I start my application the tableview is empty but it doesn't show the prompt message that its actually empty.
My Code:
#FXML var anchorpane: AnchorPane = _
// Example for the Product Entity
#FXML var tableview: TableView[Product] = _
#FXML var c1: TableColumn[Product,Integer] = _
#FXML var c2: TableColumn[Product,String] = _
#FXML var c3: TableColumn[Product,Double] = _
//create data for inserting in an observerList
val data: ObservableList[Product] = FXCollections.observableArrayList(
new Product(55,"hallo",33.0)
new Product(10,"hello",20.3)
)
override def initialize(location: URL, resources: ResourceBundle): Unit = {
//Für die columns namen setzen
c1.setCellValueFactory(new PropertyValueFactory[Product,Integer]("id"))
c2.setCellValueFactory(new PropertyValueFactory[Product,String]("name"))
c3.setCellValueFactory(new PropertyValueFactory[Product,Double]("price"))
//inserting data from the observableArrayList
tableview.setItems(data)
}
But my tableview remains empty...
Here is how it looks like:
My Product class:
package fhj.swengb.homework.dbtool
import java.sql.{ResultSet, Connection, Statement}
import scala.collection.mutable.ListBuffer
/**
* Created by loete on 27.11.2015.
*/
object Product extends Db.DbEntity[Product] {
val dropTableSql = "drop table if exists product"
val createTableSql = "create table product (id integer, name string, price double)"
val insertSql = "insert into product (id, name, price) VALUES (?, ?, ?)"
def reTable(stmt: Statement): Int = {
stmt.executeUpdate(Product.dropTableSql)
stmt.executeUpdate(Product.createTableSql)
}
def toDb(c: Connection)(p: Product): Int = {
val pstmt = c.prepareStatement(insertSql)
pstmt.setInt(1, p.id)
pstmt.setString(2, p.name)
pstmt.setDouble(3, p.price)
pstmt.executeUpdate()
}
def fromDb(rs: ResultSet): List[Product] = {
val lb: ListBuffer[Product] = new ListBuffer[Product]()
while (rs.next()) lb.append(Product(rs.getInt("id"), rs.getString("name"), rs.getDouble("price")))
lb.toList
}
def queryAll(con: Connection): ResultSet = query(con)("select * from product")
}
case class Product(id:Int, name: String, price:Double) extends Db.DbEntity[Product] {
def reTable(stmt: Statement): Int = 0
def toDb(c: Connection)(t: Product): Int = 0
def fromDb(rs: ResultSet): List[Product] = List()
def dropTableSql: String = "drop table if exists product"
def createTableSql: String = "create table product (id integer, name String, price Double"
def insertSql: String = "insert into person (id, name, price) VALUES (?, ?, ?)"
}

Using scala constants in constant expressions

I have constants, that are made of other, smaller constants. For example
object MyConstants {
final val TABLENAME = "table_name";
final val FIELDNAME = "field_name";
final val INDEXNAME = TABLENAME + "_" + FIELDNAME + "_ix"; // this one does not want to be constant
}
I want these to be true constants because I use them in annotations.
How do I make it work? (on scala 2.11)
What I want is
interface MyConstants {
String TABLENAME = "table_name";
String FIELDNAME = "field_name";
String INDEXNAME = TABLENAME + "_" + FIELDNAME + "_ix";
}
but in scala. Scalac does not pick up constants for usage in annotations if it compiles them from java class/interface (see SI-5333) so I decided to put them in a scala object. It works for literals, and for expressions with literals, but not for expressions with other constants.
With this code:
import javax.persistence.Entity
import javax.persistence.Table
import org.hibernate.annotations.Index
object MyConstants {
final val TABLENAME = "table_name";
final val FIELDNAME = "field_name";
final val INDEXNAME = TABLENAME + "_" + FIELDNAME + "_ix";
}
#Entity
#Table(name = MyConstants.TABLENAME)
#org.hibernate.annotations.Table(
appliesTo = MyConstants.TABLENAME,
indexes = Array(new Index(name = MyConstants.INDEXNAME, columnNames = Array(MyConstants.FIELDNAME))))
class MyEntity {
}
I get a following error on line indexes = ...
annotation argument needs to be a constant; found: MyConstants.INDEXNAME
Edit:
After fiddling around with a few build configurations, I think this is actually a scala-ide specific issue. The code does indeed compile alright when I build project with gradle or sbt. I do use build tool for my actual projects, so in the end it's about having a few incomprehensible markers in the IDE - annoying, but has little to do with functionality.
I used constants in JPA with scala. This code compiles, and I used it:
FreeDays.scala
#Entity
#Table(name = "free_days")
#NamedQueries(
Array(
new NamedQuery(name = JpaQueries.IS_FREE_DAYS, query = "SELECT f FROM FreeDays f WHERE f.dateOfFreeDay = :" + JpaQueries.DATE)
)
)
class FreeDays {
def this(id: Int, name: String, dateOfFreeDay: Date) = {
this()
this.id = id
this.name = name
this.dateOfFreeDay = dateOfFreeDay
}
#Id
#GeneratedValue
var id: Long = _
var name: String = _
#Column(name = "date_of_free_day")
#Temporal(TemporalType.DATE)
var dateOfFreeDay: Date = _
}
JpaQueries.scala
object JpaQueries extends JpaQueries
sealed trait JpaQueries {
final val IS_FREE_DAYS = "IS_FREE_DAYS"
final val DATE = "date"
}

Salat and Embeded MongoDb Documents

I have a case class that is made up of 2 embeded documents, one of which is a list. I am having some problems retriving the items in the list.
Please see my code below:
package models
import play.api.Play.current
import com.novus.salat._
import com.novus.salat.dao._
import com.mongodb.casbah.Imports._
import se.radley.plugin.salat._
import com.novus.salat.global._
case class Category(
_id: ObjectId = new ObjectId,
category: Categories,
subcategory: List[SubCategories]
)
case class Categories(
category_id: String,
category_name: String
)
case class SubCategories(
subcategory_id: String,
subcategory_name: String
)
object Category extends ModelCompanion[Category, ObjectId] {
val collection = mongoCollection("category")
val dao = new SalatDAO[Category, ObjectId](collection = collection) {}
val CategoryDAO = dao
def options: Seq[(String,String)] = {
find(MongoDBObject.empty).map(it => (it.category.category_id, it.category.category_name)).toSeq
}
def suboptions: Seq[(String,String,String)] = {
find(MongoDBObject.empty).map(it => (it.category.category_id, it.subcategory.subcategory_id, it.subcategory.subcategory_name)).toSeq
}
}
I get the error: value subcategory_id is not a member of List[models.SubCategories] which doesnt make any sense to me.
You are doing this:
def suboptions: Seq[(String,String,String)] = {
find(MongoDBObject.empty).map(category => {
val categories: Categories = category.category
val categoryId: String = categories.category._id
val subcategory: List[Subcategory] = category.subcategory
val subcategoryId: String = subcategory.subcategory_id //here you are trying to
//get id from list of subcategories, not one of them
val subcategoryName: String = subcategory.subcategory_name //same here
(categoryId, subcategoryId, subcategoryName)).toSeq
}
}
BTW. using snake_case in Scala is quite uncommon, val/var names should be in camelCase, see this
Edit: You can make it simple by doing this:
case class Category(
_id: ObjectId = new ObjectId(),
id: String,
name: String,
subcategories: List[Subcategory]
)
case class Subcategory(
id: String,
name: String
)
//not tested
def suboptions: Seq[(String, String, String)] = {
val categories = find(MongoDBObject.empty)
for{
category <- categories;
subcategory <- category.subcategories
} yield (category.id, subcategory.id, subcategory.name)
}