here's the game plan -
- This project is nothing serious so eni.ola isn't going to get all worked up about it (I wish!)
- I'll create the entities (and database stuff generally) to start with,
- then I'll create a backend interface to manage the entities.
- interface the app with toodledo (I plan to create an xml based cache or some sort)
- Finally throw in some lift.
so let's start with the database thingy...
I set up the project as a plain scala project by installing maven and running the following from the commandline (actually I used eclipse to create a maven project here and then added the scala nature to the project but I have no aim of writing a tutorial on q4e):mvn org.apache.maven.plugins:maven-archetype-plugin:1.0-alpha-7:create \
-DarchetypeGroupId=org.scala-tools.archetypes \
-DarchetypeArtifactId=scala-archetype-simple \
-DarchetypeVersion=1.1 \
-DremoteRepositories=http://scala-tools.org/repo-releases \
-DgroupId=your.proj.gid -DartifactId=your-proj-id
A simple scala maven project comes configured already with support for junit4 and specs - so you can start testing right away. However, you've got to set up dependencies for eclipselink, derby and one or two other things. This is done by adding the following to the to the dependencies section of your pom.xml file...
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>1.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
<version>10.4.2.0</version>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0</version>
</dependency>
And then adding the following repository to the repositories section of the pom.xml file.
<repository>We'll be using 2 classes for the entities - Tasque and Folder. A folder would contain a list of tasques (there's a one to many mapping here) and many tasque may belong to a folder (manytoone mapping). Scala makes doing stuff like this real easy. Here's the code for these two classes...
<id>EclipseLink Repo</id>
<url>http://www.eclipse.org/downloads/download.php?r=1&nf=1&file=/rt/eclipselink/maven.repo</url>
</repository>
package eni.ola
import javax.persistence._
@Entity
class Tasque {
@Id @GeneratedValue{ val strategy=GenerationType.TABLE }
var id:Int = _
var description = ""
var createdOn:Long = _
@ManyToOne
var folder:Folder = _
}
@Entity
class Folder {
@Id @GeneratedValue{ val strategy=GenerationType.TABLE }
var id:Int = _
var name = ""
var description = ""
var tags = ""
var createdOn:Long = _
@OneToMany{ val mappedBy="folder", val targetEntity=classOf[Tasque] }
var tasques:java.util.List[Tasque] = new java.util.ArrayList[Tasque]()
}
The major things to note right now is that for the oneToMany annotation we set the targetEntity to the 'Tasque' class (i.e. classOf[Tasque]). I think this isn't necessary for good ole' java but thank God scala is not good ole' java. In any case, you'll notice all the other normal stuff like @Entity annotation to make the class as an entity, @Id and @GeneratedValue to make the field as the annotation's id and to say that the value of the id should be auto-generated using a TABLE. We also make use of the oneToMany and ManyToOne annotations on the Folder and Tasque classes respectively. Note that the mappedBy field of the Folder class' oneToMany (tasques field) annotation refers to 'folder' which is the name of the field for the corresponding ManyToOne annotation in the Tasque class. So basically we've created two entities... just like that...
but before we go on we need to specify the persistence mapping file
This file (persistence.xml) basically contains all the configuration data needed to get the jpa implementation (eclipselink, in our case) to work. This file is listed below<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="tasquePU">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>eni.ola.Tasque</class>
<class>eni.ola.Folder</class>
<properties>
<property name="eclipselink.target-database" value="DERBY" />
<property name="eclipselink.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver" />
<property name="eclipselink.jdbc.url" value="jdbc:derby:tasquedb;create=true" />
<property name="eclipselink.jdbc.user" value="" />
<property name="eclipselink.jdbc.password" value="" />
<property name="eclipselink.ddl-generation" value="drop-and-create-tables" />
<property name="eclipselink.ddl-generation.output-mode" value="database" />
</properties>
</persistence-unit>
</persistence>
The important thing to note here is that the entity classes are listed in this file along with the necessary info like location of the database, what type of dbms you'll be using, the name of the driver class, our the jpa implementation should handle ddl generation etc. You may visit the eclipselink website for more info.
Another thing that didn't work for me was where to put this persistence file. The persistence.xml should be put in the 'META-INF' folder and this folder should be on the classpath of your project so that the necessary classes can find it. The problem for me was that even though, I put this folder under the src folder, the generated 'META-INF' in the target folder didn't contain any xml file... so I had to manually copy the persistence file to the target folder after each "mvn clean" (of course you can tell that I almost never run that maven command).
the icing on the cake
As the icing on the cake (or just to make sure the program works) here's the junit test code...package eni.ola
import org.junit.Test
import org.junit.Before
import org.junit.Assert._
import javax.persistence._
class EntityTest {
var factory:EntityManagerFactory = _
@Before
def setup() = {
factory = Persistence.createEntityManagerFactory("tasquePU")
val em = factory.createEntityManager
em.getTransaction.begin
val q = em.createQuery("select f from Folder f")
if(q.getResultList.size==0){
val f = new Folder
f.createdOn = System.currentTimeMillis
f.name = "First Folder Ever"
f.description = "This folder was created for testing purposes only"
f.tags = "test junit jpa"
em.persist(f)
}
em.getTransaction.commit
em.close
}
//change this function to return only one em per session
private def em = factory.createEntityManager
@Test
def testFolder() = {
val q = em.createQuery("select f from Folder f")
assertTrue(q.getResultList.size >= 1)
//assertTrue(false)
}
}
Yes, it is a messed-up test. I should have a scala object that inherits from the EntityTest class and that contains the factory (should is the keyword). But since I'm not cut out for hard work I've just gone with a straight forward class (terrible terrible programming I say).
The way jpa is defined, you create a factory and then create an entitymanager from the factory. The entity manager is used to manage your entities (duh) and that's it! It really is that simple until you run into issues with jar versions and other java black magic :(
In any case that's all for now folks... I'll upload the zipped file for the project when I'm feeling like it.
by the way
did you know Spurs