50
Persistence – Iteration 4 Vancouver Bootcamp Aaron Zeckoski [email protected]

Persistence – Iteration 4 Vancouver Bootcamp Aaron Zeckoski [email protected]

Embed Size (px)

Citation preview

Persistence – Iteration 4Vancouver Bootcamp

Aaron Zeckoski

[email protected]

2

What is persistence?

• Taking temporary data (like in-memory program data) and storing it in a more permanent form

• Putting program data somewhere so you can retrieve it later (e.g. after a restart)

• Writing the information in your Java objects somewhere so you can get to it again after they are destroyed

3

How do we persist data in Sakai?

• Pretty much always use the database

• Some file system storage for tools like resources so data is accessible via WebDav

• You should probably use the database

4

Databases and Sakai

• Settings to control the database that is used are stored in sakai.properties

• The sakai.properties file is located in your sakai home (this is configurable but is normally your tomcat home) in the sakai directory

• Look for the following line in the file

# DATABASE CONFIGURATION

5

Some DB config tips

• Always leave auto.dll=true

• HSQLDB is turned on by default, it only stores data in memory by default

• HSQLDB works well for development and for demos

• You cannot look at the HSQLDB database without some serious trickery

6

More DB config tips

• MySQL is easy to setup and use and runs pretty efficiently

• Allows you to look at the database to see if things are working

• Seems to have trouble working with Sakai in Windows sometimes

• If all else fails, switch to HSQLDB file storage

7

HSQLDB file storage

• To use HSQLDB in file mode (where it stores data on the filesystem), comment out this line:

[email protected]=jdbc:hsqldb:.

• and uncomment this [email protected]=

jdbc:hsqldb:${sakai.home}/db/sakai.db

8

MySQL config

• To use MySQL, uncomment the six lines under this line: ## MySQL settings

• Comment out the 7 lines under this one: ## HSQLDB settings

• Update the username and password lines

9

One last DB tip

• You can turn on verbose Hibernate logging in the sakai.properties file

• Change the following from false to true# enable hibernate SQL debugging output

hibernate.show_sql=false

10

3 ways to persist data to the DB

JDBChttp://java.sun.com/products/jdbc/

Spring JDBChttp://www.springframework.org/docs/reference/jdbc.html

Hibernatehttp://www.hibernate.org/

11

JDBC Info

• Java Database Connectivity

• Industry standard but has some issues:– The developer needs to deal with lot of plumbing and

infrastructure, such as endless try-catch-finally-try-catch blocks. – Applications need complex error handling to ensure that

connections are properly closed after they're used, which makes the code verbose, bloated, and repetitive.

– JDBC uses the rather uninformative SQLException.

– JDBC has no exception hierarchy

From: http://java.sun.com/products/jdbc/

12

Spring JDBC Info

• Abstraction framework for JDBC– i.e. It does lots of stuff for you!

• Some features of Spring JDBC– JdbcDaoSupport – super class, provides JdbcTemplate access– Spring provides an abstract exception layer, moving verbose

and error-prone exception handling out of application code into the framework. The framework takes care of all exception handling; application code can concentrate on extracting results by using appropriate SQL.

– Spring provides a significant exception hierarchy for your application code to work with in place of SQLException.

– For creating instances of oracle.sql.BLOB (binary large object) and oracle.sql.CLOB(character large object), Spring provides the class org.springframework.jdbc.support.lob.OracleLobHandler.

From: http://www.springframework.org/docs/reference/jdbc.html

13

Hibernate Info

• Object / Relational mapping (ORM) and persistence / query framework– i.e. It does even more stuff for you!

• Some features of Hibernate– HibernateDaoSupport – super class, easy HibernateTemplate access– Database independence - sits between the database and your java

code, easy database switch without changing any code– Object / Relational Mapping (ORM) - Allows a developer to treat a

database like a collection of Java objects– Object oriented query language (HQL) - Fully supports polymorphic

queries, native SQL, and Criteria queries – Hibernate Mapping - Uses HBM XML files to map value objects

(POJOs) to database tables – Transparent persistence - Allows easy saves/delete/retrieve for simple

value objects– High performance because it is such a lightweight framework

From: http://www.hibernate.org/

14

More Hibernate Info

• Hibernate basically sits between the DB and your code

• Can map persistent objects to tables

• In Sakai, the Hibernate configuration is set for you already

From: http://www.hibernate.org/hib_docs/v3/reference/en/html/architecture.html

15

Even more Hibernate Info

• Hibernate 2-tier web architecture

• Can send data to JDBC or XML files

• Best to just use it the way Sakai does (JDBC)

From: http://www.hibernate.org/354.html

16

Hibernate Development• 4 methods of development using Hibernate• Top down (good for existing code, we will use it for this exercise)

– implement a Java (JavaBeans) object model– write a mapping document by hand, or generate it from XDoclet tags– export the database tables using the hbm2ddl (SchemaExport) tool

• Bottom up (good for existing database or code conversion)– start with an existing data model– use the Hibernate Tools to generate the mapping documents– use the hbm2java (CodeGenerator) tool to generate skeletal Java code– fill in the business logic by hand

• Middle out (good for new development)– express your conceptual object model directly as a mapping document– use the hbm2java tool to generate skeletal Java code– fill in the business logic by hand– export the database tables using the hbm2ddl tool

• Meet in the middle (good for existing JDBC to Hibernate switch)– start with an existing data model and existing Java classes– write a mapping document to adapt between the two models

From: http://www.hibernate.org/355.html

17

Hibernate Tools

• Hibernate provides a set of Eclipse toolshttp://www.hibernate.org/255.html

– Mapping Editor: An editor for Hibernate XML mapping files, supporting auto-completion and syntax highlighting

– Console: a view in Eclipse. Provides a tree overview of console configurations and interactive view of persistent classes and relationships. Also allows the execution of HQL queries against your database and browsing of results in Eclipse.

– Development Wizards: Includes the Hibernate configuration (cfg.xml) files wizard and reverse engineering wizard for turning an existing database schema into POJO source files and HBM files.

• We will work without the tools for this exercise

From: http://www.hibernate.org/255.html

18

Other Hibernate Tools

• Hibernate Synchronizer

http://hibernatesynch.sourceforge.net/ – Actively developed plugin for Eclipse– Works with Hibernate 3 (so does Sakai 2.2!)– Can generate value objects, subclasses,

components, and DAOs from HBM files– Works well for new large scale code projects– Use Hibernate Tools for smaller projects

From: http://hibernatesynch.sourceforge.net/

19

Hibernate in Sakai

• 3 ways of using Hibernate in Sakai1. Create a SessionFactory using settings inside

your tool

2. Create a session factory from the global Sakai sessionFactoryBase

3. Add our HBMs to the global Sakai sessionFactory

• Sakai 2.2 uses Hibernate 3 Previous versions used Hibernate 2

From: http://bugs.sakaiproject.org/confluence/display/BOOT/Hibernate+in+Sakai

20

Method 1

• Create a Hibernate SessionFactory using config settings in your tool– You should use this when connecting to an

external database– Don’t use this method to connect to the

internal Sakai database!– More info on session configuration:

http://www.hibernate.org/hib_docs/reference/en/html/session-configuration.html

21

Method 2

• Create a session factory from the global Sakai SessionFactoryBase– We will use this method for the workshop

and the Task List Tool Exercise

• This method works well for simple tools– More complex tools should use method 3

• Making a connection this way does not always work on Windows with MySQL

From: http://bugs.sakaiproject.org/confluence/display/BOOT/Creating+sessions+from+the+Sakai+SessionFactoryBase

22

Method 3

• Add our HBMs to the global Sakai sessionFactory– This is the preferred method– Works best for more complex applications– Requires the tool to deploy portions to

shared and components so cannot be used for simple tools

• Demonstrated in tasklist-complex

From: http://bugs.sakaiproject.org/confluence/display/BOOT/Using+the+Sakai+global+sessionFactory

23

Let’s write some code!

• Should have iteration3 code complete

• If not, check out the iteration3 code:

https://source.sakaiproject.org/contrib/programmerscafe/tags/sakai-2.2/

tasklist-iteration3-CSS/

24

Update project.xml

• Add the Hibernate dependency to the tool/project.xml file

• Note that we use 3 property variables from master/project.properties

<dependency> <groupId>${sakai.hibernate.groupId}</groupId> <artifactId>${sakai.hibernate.artifactId}</artifactId> <version>${sakai.hibernate.version}</version></dependency>

25

Hibernate Mapping Files

• Hibernate can use an xml file to map objects to the database

• We will create our mapping file from a simple template attached to the persistence page

• For applications with many tables, use a tool to help generate the HBM files

26

Package for the HBM

• Create a new Java package for the HBM (mapping file)– org.sakaiproject.tool.tasklist.hibernate

• Create a new file in this package– Task.hbm.xml

27

Basic HBM template<?xml version="1.0"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping> <class name="org.sakaiproject.tool.MyTool.MyClassImpl” table="MYTOOL_MYCLASS"> <id name="id" type="long" column="MYCLASS_ID"> <generator class="native"> <param name="sequence">MYCLASS_ID_SEQ</param> </generator> </id> <property name="myProperty" type="string" length="255" not-null="true"> <column name="MY_PROPERTY"/> </property> </class>

</hibernate-mapping>

28

Template customization

• Change the class name and table– org.sakaiproject.tool.tasklist.TaskImpl

• Change the id column and generator param• Copy and paste the property block to add the

4 properties from the Task object– owner– siteId– creationDate– task

29

Updating the Task object

• We want our object to correctly compare to a copy which contains the same data

• You should at least implement equals and hashCode for Hibernate

• It is a good idea to also implement compareTo and toString– Note: Hibernate Synchronizer will completely

create these functions for you

30

Commons-lang comparison

• Advantages– Easy to implement– Reliable for comparisons

• Disadvantages– Somewhat inefficient (creates an object for

every comparison or uses reflection)– Slow for a large number of comparisons

31

Add commons-lang dependency to project.xml

<dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>${sakai.commons.lang.version}</version></dependency>

• Sakai deploys commons-lang to shared so you do not need to include it in your war bundle

32

Adding Task imports

• The builders make it easy to implement each of the 4 functions

import org.apache.commons.lang.builder.CompareToBuilder;import org.apache.commons.lang.builder.EqualsBuilder;import org.apache.commons.lang.builder.HashCodeBuilder;import org.apache.commons.lang.builder.ToStringBuilder;

33

EqualsBuilder equals

public boolean equals(Object obj) { if (null == obj) return false; if (obj instanceof org.sakaiproject.tool.mytool.MyObject == false) { return false; } if (this == obj) { return true; } org.sakaiproject.tool.mytool.MyObject castObj = (org.sakaiproject.tool.mytool.MyObject) obj; return new EqualsBuilder() .append(this.getId(), castObj.getId()) .isEquals(); }

34

HashCodeBuilder hashCode

public int hashCode() { // pick 2 hard-coded, odd, >0 ints as args return new HashCodeBuilder(1, 31) .append(this.id) .toHashCode(); }

35

CompareToBuilder compareTo

public int compareTo(Object obj) { org.sakaiproject.tool.mytool.MyObject castObj = (org.sakaiproject.tool.mytool.MyObject) obj; return new CompareToBuilder() .append(this.getId(), castObj.getId()) .toComparison(); }

36

ToStringBuilder toString

public String toString() { return new ToStringBuilder(this) .append(this.id) .toString();}

37

Implementing the Manager API for Hibernate

• Create a new class which implements the TaskListManager API– TaskListManagerDaoImpl

• Extend HibernateDaoSupport

• Add import for HibernateDaoSupport– Make sure you use the one for hibernate 3

38

Adding commons-logger

• Add the following to the top of the classprivate static Log log = LogFactory.getLog(MyClass.class);

• Change MyClass to this class name

• Add the commons logging imports

import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory;

39

Implement savetaskpublic boolean saveTask(Task t) { try { getHibernateTemplate().save(t); } catch (DataAccessException e) { log.error("Hibernate could not save: ” + e.toString()); e.printStackTrace(); return false; } return true; }

40

Implement deleteTaskpublic boolean deleteTask(Task t) { try { getHibernateTemplate().delete(t); } catch (DataAccessException e) { log.error("Hibernate could not delete: ” + e.toString()); e.printStackTrace(); return false; } return true; }

41

Implement findAllTasks

• Add some additional imports first– This will make it much easier to use the

Eclipse autocomplete

import org.hibernate.criterion.DetachedCriteria; import org.hibernate.criterion.Order; import org.hibernate.criterion.Restrictions;

42

Implement findAllTaskscontinued

public Collection findAllTasks(String siteId) { DetachedCriteria d = DetachedCriteria.forClass(Task.class) .add( Restrictions.eq("siteId", siteId) ) .addOrder( Order.desc("creationDate") ); return getHibernateTemplate().findByCriteria(d);}

43

Spring configuration

• Now we need to tie everything together with Spring

• First we will tell hibernate about our Task.hbm.xml mapping file

• Next we will give the hibernate stuff to our manager implementation

• Finally we will tie the new manager to the rest of the tool

44

Creating a SessionFactory using our HBM file

• Creates a SessionFactory based on the Sakai global SessionFactory

<bean id="org.sakaiproject.tasklist.SessionFactory" parent="org.sakaiproject.springframework.orm.hibernate.SessionFactoryBase"> <property name="mappingResources"> <list> <value>org/sakaiproject/tool/tasklist/ hibernate/Task.hbm.xml</value> </list> </property> </bean>

45

Define a HibernateTemplate

• HibernateTemplate gives us access to the nice Spring Hibernate functions

<bean id="org.sakaiproject.tool.tasklist.HibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate"> <property name="sessionFactory"> <ref bean="org.sakaiproject.tasklist.SessionFactory" /> </property></bean>

46

Define a HibernateTransactionManager

• Helps manage transactions and allows us to mix hibernate and direct JDBC

<bean id="org.sakaiproject.tool.tasklist. HibernateTransactionManager" class="org.springframework.orm.hibernate3. HibernateTransactionManager"> <property name="sessionFactory"> <ref bean="org.sakaiproject.tasklist.SessionFactory" /> </property> </bean>

47

Inject the HibernateTemplate into the new manager

• This connects the new manager to hibernate• The manager implementation should extend

HibernateDaoSupport

<bean id="org.sakaiproject.tool.tasklist.TasklistManagerDao” class="org.sakaiproject.tool.tasklist.impl.TaskListManagerDaoImpl" singleton="true"> <property name="hibernateTemplate"> <ref bean="org.sakaiproject.tool.tasklist.HibernateTemplate" /> </property> </bean>

48

Define a declarative transaction interceptor

• This does nice things for us like managing rollbacks and propagating changes

<bean id="org.sakaiproject.tool.mytool.TransactionProxyFactoryBean" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager"> <ref bean="org.sakaiproject.tool.mytool.HibernateTransactionManager" /> </property> <property name="target"> <ref bean="org.sakaiproject.tool.mytool.MyToolManagerHibernate" /> </property> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean>

49

Tie the new manager to the tool service

• Comment out memory impl, add in dao

<bean id="org_sakaiproject_tool_tasklist_api_TaskListService" class="org.sakaiproject.tool.tasklist.impl.TaskListServiceImpl" singleton="true"> <property name="taskListManager"> <ref bean="org.sakaiproject.tool.tasklist.api.TaskListManagerDao" /> <!--<ref bean="org.sakaiproject.tool.tasklist.api.TaskListManagerMemory" />--> </property> <property name="toolManager"> <ref bean="org.sakaiproject.tool.api.ToolManager" /> </property> <property name="userDirectoryService"> <ref bean="org.sakaiproject.user.api.UserDirectoryService" /> </property> </bean>

50

Build and Test

• Run maven sakai from your tasklist directory to build and deploy the tasklist– Should be no build errors

• Startup tomcat

• Test the tasklist tool– WARNING: Windows / MySQL users may have trouble

here, if you try to add a task and get no errors but also nothing happens, then you are experiencing a problem we do not know how to fix, switch to HSQLDB to test the tool