On Tuesday, 6 de November de 2012 at 20:32, Kostas kougios wrote:
Ok, to continue the discussion in this separate thread from https://groups.google.com/forum/?fromgroups=#!topic/scala-user/TNG6KG0y2F8
...
@ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} ) @JoinTable(name="Flight_Company", joinColumns = @JoinColumn(name="FLIGHT_ID"), inverseJoinColumns = @JoinColumn(name="COMP_ID") ) public X getX()...
I couldn't compile the project, I am not very familiarized with sbt and it always gives me a weird error that I can't do much about it. This time it couldn't find an artifact:
com.typesafe.sbteclipse#sbteclipse-plugin;2.1.0-M2: not found
1. I think you put too much emphasis on STM where as you are coding an ORM library. An API "should carry it's weight", and mapperdao as an ORM library does carry it's weight and only that. MapperDao does ORM, not STM. That has the benefit that someone who don't want to use STM (most of the cases) can use mapperdao,
but also anyone who wants to use STM can use mapperdao and on top of that add his preferred STM to achieve what activate achieves.
I am a bit unclear on what would happen if activate client code is like:
----
var website
transactional {
website = new Website("http://activate-framework.org", counter)
}
website.url="something else"
---
Is the modified url still reflected in the database and in all mutable instance inside the JVM?
If not then there is a significant flaw of the approach as it allows hard to find bugs. Mutability is a big .. bugger!
If activate currently allows this code then I would recommend if somehow magically activate was throwing an exception if someone tried to modify the url outside of the transaction.
2. MapperDao and immutable classes is a major benefit to any client code. It seems to me that Activate entities have to be mutable:
class Website(var url: String, val counter: Counter)
extends Entity
where as mapperdao entities can be immutable. Mutability is a source of lots of bugs and a maintenance nightmare for big projects. In my commercial experience so far we never used STM but we very often use immutable domain models.
3. Regarding mapperdao's entities, yes indeed there is a bit more coding and are a bit more involved. In fact the extra code is the only drawback but there are a lot of benefits:
a. With mapper dao the domain class don't depend on the ORM library, i.e. the mapperdao entity looks like:
class Website(val id: Int, val url: String, val counter: Counter)
where as the activate entity depends on activate:
class Website(var url: String, val counter: Counter) extends Entity
b. customization of mappings ,i.e. fields that map to different column names, data conversion before storing/retrieval (i.e. convert enums to string or integers), sorting of loaded related data etc, I am going to do a quick demo:
// immutable domain model, doesn't depend on the ORM library
case class Product(name: String, attributes: List[Attribute]) // the non-persisted entity doesn't contain an id because the id is autogenerated by the database and available only for persisted entities
case class Attribute(name: String, value: String)
...
object ProductEntity extends Entity[Int, SurrogateIntId, Product] {
val id = key("id") autogenerated (_.id) // the persisted entity retrieves the autogenerated id. All persisted entities are Product with SurrogateIntId
val name = column("name") to (_.name.toLowerCase) // assuming we would like to convert the name to lower case when stored in the database
val attributes = manytomany(AttributeEntity) to (_.attributes) // here we map it as many to many but we could map it as one to many
def constructor(implicit m) = new Product(id, name, m(attributes).toList.sortWith(_.name<_.name)) with SurrogateIntId // when we load the attributes, we want them to be sorted by name
}
So as you see the mappings offer a great deal of flexibility. Now i.e. in order to do the toLowerCase in activate, we might have to (please correct me if I am wrong)
transaction {
product.name=title.toLowerCase // this is visible to all instances within the jvm ???
}
product.name=title // restore the original value, is this visible to all instances within the jvm???
c. Avoiding annotations: an ORM that uses annotations within the domain classes not only couples the domain classes to the ORM tool but also ends up with ugly code. I.e. how many times our hibernate getters ended up having this:@ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} ) @JoinTable(name="Flight_Company", joinColumns = @JoinColumn(name="FLIGHT_ID"), inverseJoinColumns = @JoinColumn(name="COMP_ID") ) public X getX()...
So more lines of code for the ORM rather than properties or behavior!!! Sooner or later if an ORM needs to support client code it will end up like that. MapperDao embraces these needs instead of trying to hide them to "reduce" the number of lines. Also note that to read annotations you need reflection.
d. MapperDao queries IMHO are more natural than Activate ones, probably due to mapperdao's mappings. I.e. copying from activate sample project:
// Queries
// The query operators available are :==, <, :>, <=, >=, isNone, isSome, :||, :&&, like and matches.
// Note that the queries can be made about abstract entities (abstract trait and class).
// Perform queries within transactions
transactional {
val result = query {
(person: Person) => where(person.name :== "John2") select (person)
}
for (person <- result)
println(person.name)
}
So why the ":>" operator instead of ">"? Why the ":||" or ":&&" ? MapperDao DSL had only to use "===" instead of "=="
The same query with mapperdao looks like a sql and hence a programmer doesn't have to learn anything new:
val pe=PersonEntity
(select from pe where pe.name === "John2").toList(queryDao)
the operators that you can use with mapperdao are === , > , < , <= , >= , and , or . Except the "===", the rest match a typical select SQL.
Also IMHO mapperdao DSL is more readable, i.e. compare:
activate:
query { (person: Person) => where(person.name :== "John2") select (person) }
mapperdao:
(select from pe where pe.name === "John2").toList(queryDao)
val pe=PersonEntity
4. does activate support more than 1 database? It seems "transactional" and "query" are static and "magically" get the database connection. MapperDao supports multiple databases. What if the WebSite entity was stored across multiple databases? Which value from which database does the JVM instance reflect?
5. I still have to see how activate deals with auto-generated id's and also how many-to-many , one-to-many, many-to-one and one-to-one are mapped. I.e. when an entity contains a Set[X], is that a many to many or one to many?
6. And how does activate deal with inheritance?
There is more functionality that mapperdao's model allows. I.e. you can map entities that don't live in a database (i.e. entities retrieved via a web service call or maybe legacy Hibernate entities). Also it supports entities with composite primary keys and so on. MapperDao follows the principle "do one thing and do it well" allowing client code to have an STM layer on top if they wish.