blog van Paul Hildebrand
JPA with Scala
Recently I was wondering whether it was possible to use JPA (Java Persistence API) with Scala. So I did some fiddling around and found out that it is quite easy with Scala (but that’s not a surprise).
I used the following libraries in this example:
– Hibernate 3.5.6.Final (JPA2 implementation)
– Hsqldb 1.8.0.7
– Spring 3.0.5
– Scala 2.8.1
Scala code
First I created an entity class “Customer” with a first and a last name and of course an id.
package nl.paulhildebrand.jpa import javax.persistence._ import scala.reflect.BeanProperty @Entity @Table(name = "customer") class Customer(f: String, l: String) { @Id @GeneratedValue(strategy = GenerationType.AUTO) @BeanProperty var id: Int = _ @BeanProperty var firstName: String = f @BeanProperty var lastName: String = l def this() = this (null, null) override def toString = id + " = " + firstName + " " + lastName }
Because it is an example I kept the entity “Customer” simple. Nice to notice is the @BeanProperty annotation. It dynamically adds a getter and a setter for the given variable. This keeps the code clean and the JPA implementation working. The rest is similar to a Java implementation.
To access the database I created a DAO. But first I needed to define the interface for it. In scala an interface is called a Trait.
package nl.paulhildebrand.jpa trait CustomerDao { def save(customer: Customer): Unit def find(id: Int): Option[Customer] def getAll: List[Customer] def getByLastName(lastName : String): List[Customer] }
As you can see it has some simple method to save and find customers. Lets have a look at the implementation of the trait.
package nl.paulhildebrand.jpa import org.springframework.beans.factory.annotation._ import org.springframework.stereotype._ import org.springframework.transaction.annotation.{Propagation, Transactional} import javax.persistence._ import scala.collection.JavaConversions._ @Repository("customerDao") @Transactional(readOnly = false, propagation = Propagation.REQUIRED) class CustomerDaoImpl extends CustomerDao { @Autowired var entityManager: EntityManager = _ def save(customer: Customer): Unit = customer.id match { case 0 => entityManager.persist(customer) case _ => entityManager.merge(customer) } def find(id: Int): Option[Customer] = { Option(entityManager.find(classOf[Customer], id)) } def getAll: List[Customer] = { entityManager.createQuery("From Customer", classOf[Customer]).getResultList.toList } def getByLastName(lastName : String): List[Customer] = { entityManager.createQuery("From Customer Where lastName = :lastName", classOf[Customer]).setParameter("lastName", lastName).getResultList.toList } }
This is a quite standard DAO implementation but the real magic is in the implicit conversions of the java collections to scala collections. This can be achieved by adding “import scala.collection.JavaConversions._”. Now methods as toList are available on the collections. Keep in mind this is an example, doing “getAll” on a large dataset is not recommended.
Configuration
When using JPA you need a file called META-INF/persistence.xml. The one below is an example for JPA2.
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.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_2_0.xsd"> <persistence-unit name="JpaScala" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> </persistence-unit> </persistence>
To wire all the code and libraries together I use the Spring Framework. The application-context below defines the following:
– Transaction manager
– JPA2 Entity manager
– Data source (Hypersonic memory database)
– Context scanner so it is able to find the spring annotated classes.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd "> <tx:annotation-driven transaction-manager="transactionManager"/> <context:component-scan base-package="nl.paulhildebrand"/> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" p:driverClassName="org.hsqldb.jdbcDriver" p:url="jdbc:hsqldb:mem:JpaScala" p:username="sa" p:password=""/> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="persistenceUnitName" value="JpaScala"/> <property name="persistenceProviderClass" value="org.hibernate.ejb.HibernatePersistence"/> <property name="jpaDialect"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/> </property> <property name="dataSource" ref="dataSource"/> <property name="jpaPropertyMap"> <map> <entry key="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/> <entry key="hibernate.connection.charSet" value="UTF-8"/> <entry key="hibernate.hbm2ddl.auto" value="create"/> <entry key="hibernate.show.sql" value="true"/> </map> </property> </bean> <bean id="entityManager" class="org.springframework.orm.jpa.support.SharedEntityManagerBean"> <property name="entityManagerFactory" ref="entityManagerFactory"/> </bean> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory"/> <property name="dataSource" ref="dataSource"/> </bean> </beans>
Try it out
The code below adds 5 customers to the database and shows them using various methods on the CustomerDao.
val ctx = new ClassPathXmlApplicationContext("application-context.xml") val dao: CustomerDao = ctx.getBean(classOf[CustomerDao]) dao.save(new Customer("Paul", "Hildebrand")) dao.save(new Customer("Floor", "Hildebrand")) dao.save(new Customer("Storm", "Hildebrand")) dao.save(new Customer("Jan", "Jansen")) dao.save(new Customer("Peter", "Jansen")) println(dao.getAll) println(dao.getByLastName("Jansen")) println(dao.getByLastName("Hildebrand")) dao.find(3) match { case Some(x) => println(x) case None => println("No customer found with id 3") }
Thoughts
With this example you should be able to write a simple JPA application using Scala. I have not yet tried to use things like case classes or many to one relations using Scala collections. You might also be able to improve the code using annotations in constructor arguments so that some “vars” can be replaced by “vals”.
For the dependency injection I used the Spring Framework because I use that a lot, but a framework like Guice works just as good.
This code might not work with Scala 2.7.x because of possible nested annotations.
Source code:
You can download the source code here.
For SBT users:
– unpack JpaScala.tar.gz
– start SBT in the root directory of JpaScala
– create new project
– do “update”
– do “run JpaScala”
If you have some improvements, please let me know. I am still quite new in the Scala world but I am learning fast.
Print article |
about 7 years ago
Leuke blog, zeg… tijd voor een scala cursus bij bol.com ? 😉
about 7 years ago
This will hurt, you’ll end up with Some(null), which is an anathema:
def find(id: Int): Option[Customer] = {
Some(entityManager.find(classOf[Customer], id))
}
Try:
def find(id: Int): Option[Customer] = entityManager.find(classOf[Customer], id) match {
case null => None
case customer => Some(customer)
}
about 7 years ago
@Eric Bowman. Thanks for the tip. I have changed it. Somehow I thought Some(null) would be the same as None.
about 7 years ago
Also, better to use JavaConverters instead of JavaConversions, so you can grep later for asScala/asJava.
about 7 years ago
On http://www.infoq.com/articles/scala_and_spring Urs Peter has some similar approach. You may find it interesting.
about 7 years ago
You are looking for:
Option(entityManager.find(classOf[Customer], id))
about 4 years ago
Can you please give the Scala -Guice-JPA sample
about 4 years ago
I am not experienced in guice but I think the only thing you have to figure out is how to configure a entity manager in guice. The rest should then be pretty straight forward and not much different.
about 4 years ago
Thanks I will try
about 4 years ago
Thanks.The tips were very helpful.I don’t find any other Scala-Jpa tutorial which is easy to follow and the same time really useful when one wants to use JPA + a Dependency Injector in Scala
about 4 years ago
With thanks the code for Guice module and implementation class are furnished
import scala.reflect._
import com.google.inject._
import com.google._
import com.google.inject.AbstractModule;
import com.google.inject.persist.jpa._;
import com.google.inject.persist._;
import javax.persistence._
import scala.collection.JavaConversions._
class MyModule extends AbstractModule {
override def configure() {
bind(classOf[CustomerDao]).to(classOf[CustomerDaoImpl])
}
}
import javax.persistence.EntityManager;
import com.google.inject.Provider;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import javax.persistence._
import javax.persistence.criteria._;
import javax.persistence.criteria.CriteriaQuery;
import scala.collection.JavaConversions._
class CustomerDaoImpl @Inject()(var em: Provider[EntityManager] ) extends CustomerDao {
@Transactional
def save(customer: Customer)= {
em.get.persist(customer);
em.get.flush()
}
def find(id: Int): Option[Customer] = {
Option(em.get.find(classOf[Customer], id))
}
def getAll: List[Customer] = {
em.get.createQuery(“Select c From Customer c”, classOf[Customer]).getResultList.toList
}
def getByLastName(lastName : String): List[Customer] = {
em.get.createQuery(“SELECT c From Customer c Where c.lastName = :lastName”, classOf[Customer]).setParameter(“lastName”, lastName).getResultList.toList
}
}
Once again thanks for the Guidance on a subject for which good material is scarce
about 1 year ago
hi
very nice
can you source code with maven
for me
thanks