« Return to Thread: [scala] From JPA question to ScalaQuery to Collection Abstractions

[scala] From JPA question to ScalaQuery to Collection Abstractions

by Meredith Gregory :: Rate this Message:

Reply to Author | View in Thread

All,

Again -- many thanks to the posters and responders on this thread. i've moved on to attempt to use ScalaQuery as the target for the SQL persistence. Stefan Szeiger describes his work here.

It seems to be a week of meeting resistance, however. i've been attempting to build the simplest possible collection to use as a toy to test my compilation strategy. This has turned out to be quite a little brain-teaser.

For me simpler means: contains less information. In this sense, a Set is simpler than a List. The simplest possible collection i can imagine (that still supports associativity -- and i don't know if i can call something that doesn't support associativity a collection) is a monad. A monad doesn't commit to what the collection contains; only parametrically commits to how to put things into the collection; and only parametrically commits to how to flatten the collection (which is tantamount to supporting associativity).

After thinking about it for half a day, and revisiting the Moors-Piessens-Odersky (MPO) paper on higher-kinded types, i realized that the MPO construction doesn't actually apply to Scala in practice (someone please tell me if i'm wrong) because they fundamentally rely on constructions using type members and not type parameters. Unfortunately, most of the collections libraries are written using type parameters. So, i decided to roll my own and see what i got.

Here's what i came up with last night.

// smallest expression of monad i can find
trait MBrace[C[_] <: MBrace[C,A],A] {
  def nest( a : A ) : C[A]
  def flatten[T <: C[C[A]]]( bsq : T ) : C[A]
}

// one of the simplest witnesses of monad i can find
class MBraceSeq[A]( a_ : A* ) extends Seq[A] with MBrace[MBraceSeq,A] {
  override def nest( a : A ) = new MBraceSeq[A]( a )
  override def flatten[T <: MBraceSeq[MBraceSeq[A]]]( bsq : T ) : MBraceSeq[A] = {
    (new MBraceSeq[A]( ) /: bsq)( {
      ( acc : MBraceSeq[A], e : MBraceSeq[A] ) => ( acc ++ e ).asInstanceOf[MBraceSeq[A]]
    } )
  }
  override def length = a_.length
  override def elements = a_.elements
  override def apply( n : Int ) = a_.apply( n )
}

i was mostly ok with this -- apart from some desiderata/concerns regarding the Seq methods being visible at this level. i woke up this am and realized that this is not even wrong. It requires an instance of MBrace to invoke the nest or flatten methods.

Geez, i feel like a first year undergrad. All i want to do is write the simplest possible collection on which i can then test a persistence strategry and i run into roadblocks everywhere. ;-)

Best wishes,

--greg

On Wed, Jun 24, 2009 at 8:00 AM, Derek Chen-Becker <dchenbecker@...> wrote:
Well, in my real-world experience I've never had very complex models and I've never used TABLE-PER-CLASS either, so I don't really have a feel for what's not possible. Generally I think that JPA (which is a subset of Hibernate) covers a good portion of people's needs for ORM, but it definitely has some big missing functionality (e.g. no ordered collections until JPA 2). I think that in this case Greg is pushing the limits on a relatively unused corner of JPA (I've never seen someone use TABLE-PER-CLASS before) and he's hitting some bugs. I want to make clear that I think that what Greg is attempting is entirely possible. My earlier comment about rolling your own ORM was because I misunderstood what he was doing with abstract classes. In my mind, there would be a couple of places where you might want to roll your own stuff:

  1. An existing schema that doesn't map well to an object graph. We have a legacy database at work where the brilliant developers decided that nothing should be normalized and data can exist in one of four tables based on some arcane business logic. You can still build an ORM layer on something like this, but essentially all you're doing is creating a thin wrapper on ResultSets
  2. You have very specific data that you need to retrieve with possibly complex joins, and you need it to be as performant as possible. There are some tricks you can do with EJB QL to make it prefetch things, but often you can end up with multiple round trips to the database and/or data that you don't need being fetched. In certain scenarios I could see this being an issue.
This list isn't intended to be exhaustive, these are just the top two that come to mind. In terms of Hibernate vs others like JPOX, TopLink, etc, I'd say that Hibernate is a first-class provider and at least equal to the others.

Derek


On Wed, Jun 24, 2009 at 2:21 AM, TSP <tim.pigden@...> wrote:

Derek
Would you distinguish between what is achievable in a specific ORM
such as Hibernate from JPA in this statement or would you think it
applies to all. I've got to "go with hibernate" in any case because of
widespread use of UserTypes. Unlike Greg, in my case I can hand-craft
all my hibernate xml files if I need.

Tim

On Jun 23, 1:46 am, Derek Chen-Becker <dchenbec...@...> wrote:
> For sufficiently complex relationships, JPA is not a good fit. Beyond a
> certain point it's usually simpler to roll your own. I think that this is
> somewhat of a failing of the model, but it's not a simple problem to solve
> in the generic case.
>
> Derek
>
> On Mon, Jun 22, 2009 at 6:45 PM, Derek Chen-Becker <dchenbec...@...>wrote:
>
>
>
> > Ah, sorry, I lost track of the thread.
>
> > On Mon, Jun 22, 2009 at 4:55 PM, Meredith Gregory <
> > lgreg.mered...@...> wrote:
>
> >> Derek,
>
> >> You are correct and i noted and reported this on Scala on Friday. However,
> >> if you have a chain of the form
>
> >> AbstractClass <- Class <-contains- AbstractClass <-Class <-contains- ...
>
> >> The @MappedSuperclass solution fails at level 2.
>
> >> Best wishes,
>
> >> --greg
>
> >> On Mon, Jun 22, 2009 at 3:52 PM, Derek Chen-Becker <dchenbec...@...
> >> > wrote:
>
> >>> Something I just want to throw out into the discussion: Since you're
> >>> using table-per-class, having a @Table annotation on AbstractContainer
> >>> doesn't do anything since abstract classes can't have instances. Tables are
> >>> only generated for abstract classes if you're using a JOINED inheritance
> >>> strategy. You might want to look at using the MappedSuperclass annotation
> >>> for the abstract base class instead. If I change the AbstractContainer def
> >>> to:
>
> >>> @MappedSuperclass
> >>> public abstract class AbstractContainer implements java.io.Serializable {
>
> >>> and then modify MySampleFuContainer to:
>
> >>> public class MySampleFuContainer extends AbstractContainer {
>
> >>> then I seem to get the proper schema:
>
> >>>     create table lingo_production.MySampleFuContainer_table (
> >>>         id varchar(255) not null,
> >>>         uuid varchar(255),
> >>>         mysamplingmumble__idSuper varchar(255),
> >>>         primary key (id),
> >>>         unique (uuid)
> >>>     );
>
> >>> Having said that, I think that the behavior you're currently seeing
> >>> appears to be a bug.
>
> >>> Derek
>
> >>> On Mon, Jun 22, 2009 at 3:43 PM, Meredith Gregory <
> >>> lgreg.mered...@...> wrote:
>
> >>>> Kris,
>
> >>>> Here<http://svn.biosimilarity.com/src/open/codesamples/trunk/hibernate/>is a link to the self-contained example that now uses just Java. i included
> >>>> the target dir in the repo to speed up investigation, but you can just blow
> >>>> that away and build from scratch. The example is currently written to
> >>>> Java1.6, but also exhibits the same behavior under Java1.5. To run the
> >>>> example
>
> >>>> > svn co
> >>>>http://svn.biosimilarity.com/src/open/codesamples/trunk/hibernate
> >>>> ...
> >>>> > env PATH=<path-to-java1.6>:$PATH JAVA_HOME=<path-to-java1.6> mvn clean
> >>>> compile process-classes
>
> >>>> If you switch comment and decl at line 22 in
> >>>> src/main/java/maxb/hbex2/MySampleFuContainer.java then you see the error.
> >>>> The schema goes from
>
> >>>> create table lingo_production.MySampleFuContainer_table (
> >>>>         id_AbstractContainer varchar(255) not null,
> >>>>         varchar(255) not null,
> >>>>         uuid varchar(255),
> >>>>         mysamplingmumble__idSuper varchar(255),
> >>>>         primary key (id),
> >>>>         unique (uuid)
> >>>>     );
>
> >>>> to
>
> >>>> create table lingo_production.MySampleFuContainer_table (
> >>>>         id_AbstractContainer varchar(255) not null,
> >>>>         id varchar(255),
> >>>>         mysamplingmumble_ tinyblob,
> >>>>         uuid varchar(255),
> >>>>         primary key (id_AbstractContainer),
> >>>>         unique (id_AbstractContainer)
> >>>>     );
>
> >>>> Best wishes,
>
> >>>> --greg
>
> >>>> On Mon, Jun 22, 2009 at 1:38 PM, Meredith Gregory <
> >>>> lgreg.mered...@...> wrote:
>
> >>>>> Kris,
>
> >>>>> Thanks for the suggestion. i've now got a tiny little example that
> >>>>> compiles on its own that illustrates the problem. Changing the inheritance
> >>>>> strategy to JOINED makes no difference. Hibernate still does the wrong
> >>>>> thing.
>
> >>>>> Best wishes,
>
> >>>>> --greg
>
> >>>>> On Mon, Jun 22, 2009 at 8:55 AM, Kris Nuttycombe <
> >>>>> kris.nuttyco...@...> wrote:
>
> >>>>>> This may be off the mark, but I'm wondering if the reason that you're
> >>>>>> having difficulty with the parallel inheritance hierarchy problem is
> >>>>>> not your use of TABLE_PER_CLASS inheritance. In my application, I have
> >>>>>> a similar construct, but I am using JOINED_TABLE inheritance. This
> >>>>>> allows for a normal foreign key relationship to be created in the
> >>>>>> database between C2_table and the base table for CThing, with the
> >>>>>> result that Hibernate will generate the query for CThing member as a
> >>>>>> union. Using table per class inheritance, I would expect Hibernate to
> >>>>>> need to synthesize an additional dtype field in C2_table along with
> >>>>>> the key column in order to enforce the uniqueness of the keys to the
> >>>>>> joined entities, and I don't believe that it does this.
>
> >>>>>> I'm not sure how the fact that the code is generated is particularly
> >>>>>> relevant; surely if it's possible to hand-write a successful solution,
> >>>>>> then your code generator could be made aware of how to construct a
> >>>>>> viable solution?
>
> >>>>>> Kris
>
> >>>>>> On Fri, Jun 19, 2009 at 8:47 PM, Meredith
> >>>>>> Gregory<lgreg.mered...@...> wrote:
> >>>>>> > All,
>
> >>>>>> > i had a similar problem and found the source of the issues. Spse you
> >>>>>> have a
> >>>>>> > container hierarchy (CTop <- C2) side-by-side with a contained
> >>>>>> hierarchy
> >>>>>> > (CThing <- CThing1). The inheritance at the top of the container
> >>>>>> hierarchy,
> >>>>>> > CTop, causes hibernate to bail on tracking the relations and punt to
> >>>>>> > embedded values instead. Rewriting the top to be a @MappedSuperClass
> >>>>>> fixes
> >>>>>> > the problem in this specific case. However, if your hierarchy is
> >>>>>> deep,
> >>>>>> > you're screwed.
>
> >>>>>> > If anybody has a suggestion for a workaround, i'm all ears. The
> >>>>>> problem is
> >>>>>> > that it would appear that both Mr Crowley and i are generating Java
> >>>>>> + JPA
> >>>>>> > code. So, the solution needs to be algorithmic and not 1-off.
>
> >>>>>> > Perhaps the best solution is to find an alternative to hibernate as
> >>>>>> this is
> >>>>>> > a particularly irritating bug.
>
> >>>>>> > Best wishes,
>
> >>>>>> > --greg
>
> >>>>>> > @Entity
> >>>>>> > @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
> >>>>>> > abstract class CTop {
> >>>>>> >    ...
> >>>>>> >    @Id
> >>>>>> >     @GeneratedValue(generator = "system-uuid")
> >>>>>> >     @GenericGenerator(name = "system-uuid", strategy = "uuid")
> >>>>>> >     private String id_CTop;
> >>>>>> > }
>
> >>>>>> > @Entity
> >>>>>> > @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
> >>>>>> > abstract class CThing {
> >>>>>> >    ...
> >>>>>> >    @Id
> >>>>>> >     @GeneratedValue(generator = "system-uuid")
> >>>>>> >     @GenericGenerator(name = "system-uuid", strategy = "uuid")
> >>>>>> >     private String id_CThing;
> >>>>>> > }
>
> >>>>>> > @Entity
> >>>>>> > @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
> >>>>>> > @Table(name = "C2_table", catalog = "mydb_production",
> >>>>>> uniqueConstraints = {
> >>>>>> > @UniqueConstraint(columnNames = "uuid") })
> >>>>>> > class C2 extends CTop {
> >>>>>> >    CThing thing;
> >>>>>> > ...
> >>>>>> >   @OneToOne
> >>>>>> >     @JoinColumn
> >>>>>> >     public CThing getThing() {
> >>>>>> >         return this.thing;
> >>>>>> >     }
> >>>>>> >     public void setThing( CThing thing ) {
> >>>>>> >         this.thing = thing;
> >>>>>> >     }
>
> >>>>>> > @Column(name = "uuid", unique = false, nullable = true, insertable =
> >>>>>> true,
> >>>>>> > updatable = true)
> >>>>>> >     public String getUuid() {
> >>>>>> >         return this.uuid;
> >>>>>> >     }
>
> >>>>>> >     public void setUuid(String uuid) {
> >>>>>> >         this.uuid = uuid;
> >>>>>> >     }
>
> >>>>>> >     @Id
> >>>>>> >     @GeneratedValue(generator = "system-uuid")
> >>>>>> >     @GenericGenerator(name = "system-uuid", strategy = "uuid")
> >>>>>> >     @Column(name = "id", unique = false, nullable = true, insertable
> >>>>>> = true,
> >>>>>> > updatable = true)
> >>>>>> >     public String getId() {
> >>>>>> >         return this.id;
> >>>>>> >     }
>
> >>>>>> > }
>
> >>>>>> > @Entity
> >>>>>> > @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
> >>>>>> > @Table(name = "CThing1_table", catalog = "mydb_production",
> >>>>>> > uniqueConstraints = { @UniqueConstraint(columnNames = "uuid") })
> >>>>>> > class CThing1 extends CThing {
> >>>>>> > ...
> >>>>>> >   // lots of ground type fields
>
> >>>>>> > @Column(name = "uuid", unique = false, nullable = true, insertable =
> >>>>>> true,
> >>>>>> > updatable = true)
> >>>>>> >     public String getUuid() {
> >>>>>> >         return this.uuid;
> >>>>>> >     }
>
> >>>>>> >     public void setUuid(String uuid) {
> >>>>>> >         this.uuid = uuid;
> >>>>>> >     }
>
> >>>>>> >     @Id
> >>>>>> >     @GeneratedValue(generator = "system-uuid")
> >>>>>> >     @GenericGenerator(name = "system-uuid", strategy = "uuid")
> >>>>>> >     @Column(name = "id", unique = false, nullable = true, insertable
> >>>>>> = true,
> >>>>>> > updatable = true)
> >>>>>> >     public String getId() {
> >>>>>> >         return this.id;
> >>>>>> >     }
>
> >>>>>> > }
>
> >>>>>> > On Tue, Jun 16, 2009 at 1:45 PM, Derek Chen-Becker <
> >>>>>> j...@...>
> >>>>>> > wrote:
>
> >>>>>> >> John Nilsson wrote:
> >>>>>> >> > Hi,
>
> >>>>>> >> > I think the showSql property has been deprecated in favor of
> >>>>>> log4j
> >>>>>> >> > loggers.
>
> >>>>>> >> > If you set the log4j level to TRACE for org.hibernate you'll get
> >>>>>> >> > everything Hibernate has to say about what
>
> ...
>
> read more »



--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Lift" group.
To post to this group, send email to liftweb@...
To unsubscribe from this group, send email to liftweb%2Bunsubscribe@...
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en
-~----------~----~----~----~------~----~------~--~---




--
L.G. Meredith
Managing Partner
Biosimilarity LLC
1219 NW 83rd St
Seattle, WA 98117

+1 206.650.3740

http://biosimilarity.blogspot.com

 « Return to Thread: [scala] From JPA question to ScalaQuery to Collection Abstractions