The beginnings of a new persistence framework which leverages the annotation

goodness of the new EJB3 persistence framework but is not "managed" (ie. is not
real ORM).
This commit is contained in:
Michael Bayne
2006-09-14 21:10:33 +00:00
parent 4a408fd3c9
commit e51e4c1e8c
3 changed files with 697 additions and 0 deletions
@@ -0,0 +1,174 @@
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001-2006 Michael Bayne
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.jdbc.depot;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import javax.persistence.Id;
/**
* Handles the marshalling and unmarshalling of persistent instances to JDBC
* primitives ({@link PreparedStatement} and {@link ResultSet}).
*/
public class DepotMarshaller<T>
{
/**
* Creates a marshaller for the specified persistent object class.
*/
public DepotMarshaller (Class<T> pclass)
{
_pclass = pclass;
// determine our table name
_tableName = _pclass.getName();
_tableName = _tableName.substring(_tableName.lastIndexOf(".")+1);
// introspect on the class and create field marshallers for all of its
// fields
ArrayList<FieldMarshaller> flist = new ArrayList<FieldMarshaller>();
for (Field field : _pclass.getFields()) {
// the field must be public, non-static and non-transient
int mods = field.getModifiers();
if (((mods & Modifier.PUBLIC) == 0) ||
((mods & Modifier.STATIC) != 0) ||
((mods & Modifier.TRANSIENT) != 0)) {
continue;
}
FieldMarshaller fm = FieldMarshaller.createMarshaller(field);
flist.add(fm);
// check to see if this is our primary key
for (Annotation ann : field.getDeclaredAnnotations()) {
System.err.println(
"Annotation " + ann + " (" + ann.getClass() + ")");
}
}
_fields = flist.toArray(new FieldMarshaller[flist.size()]);
// generate our full list of columns for use in queries
StringBuffer columns = new StringBuffer();
for (FieldMarshaller fm : _fields) {
if (columns.length() > 0) {
columns.append(",");
}
columns.append(fm.getColumnName());
}
_fullColumnList = columns.toString();
}
/**
* Returns the name of the table in which persistence instances of this
* class are stored. By default this is the classname of the persistent
* object without the package.
*/
public String getTableName ()
{
return _tableName;
}
/**
* Returns the field name of the primary key for this persistent object
* class or null if it did not declare a primary key.
*/
public String getPrimaryKey ()
{
return _primaryKey == null ? null : _primaryKey.getColumnName();
}
/**
* Creates a query for instances of this persistent object type using the
* supplied key.
*/
public PreparedStatement createQuery (
Connection conn, DepotRepository.Key key)
throws SQLException
{
String query = "select " + _fullColumnList + " from " + getTableName() +
" where " + key.toWhereClause();
PreparedStatement pstmt = conn.prepareStatement(query);
for (int ii = 0; ii < key.indices.length; ii++) {
pstmt.setObject(ii+1, key.values[ii]);
}
return pstmt;
}
/**
* Creates a persistent object from the supplied result set.
*/
public T createObject (ResultSet rs)
throws SQLException
{
try {
T po = (T)_pclass.newInstance();
for (int ii = 0; ii < _fields.length; ii++) {
_fields[ii].getValue(rs, ii+1, po);
}
return po;
} catch (Exception e) {
String errmsg = "Failed to unmarshall persistent object " +
"[pclass=" + _pclass.getName() + "]";
throw (SQLException)new SQLException(errmsg).initCause(e);
}
}
/**
* Creates a statement that will insert the supplied persistent object into
* the database.
*/
public PreparedStatement createInsert (Connection conn, Object po)
throws SQLException
{
return null;
}
/**
* Creates a statement that will update the supplied persistent object
* using the supplied key.
*/
public PreparedStatement createUpdate (
Connection conn, Object po, DepotRepository.Key key)
throws SQLException
{
return null;
}
/**
* Binds the specified object to the specified prepared statement.
*/
public void bindObject (PreparedStatement stmt, Object po)
{
}
protected Class<T> _pclass;
protected FieldMarshaller[] _fields;
protected String _tableName;
protected FieldMarshaller _primaryKey;
protected String _fullColumnList;
}
@@ -0,0 +1,313 @@
//
// samskivert library - useful routines for java programs
// Copyright (C) 2006 Michael Bayne
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.jdbc.depot;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import com.samskivert.io.PersistenceException;
import com.samskivert.jdbc.ConnectionProvider;
/**
* Provides a base for classes that provide access to persistent objects. Also
* defines the mechanism by which all persistent queries and updates are routed
* through the distributed cache.
*/
public class DepotRepository
{
protected DepotRepository (ConnectionProvider conprov)
{
_conprov = conprov;
}
protected <T> T load (Class<T> type, Comparable primaryKey)
throws PersistenceException
{
DepotMarshaller<T> marsh = getMarshaller(type);
Key key = new Key(marsh.getPrimaryKey(), primaryKey);
return invoke(new ObjectQuery<T>(marsh, key));
}
protected <T> T load (Class<T> type, Key key)
throws PersistenceException
{
DepotMarshaller<T> marsh = getMarshaller(type);
return invoke(new ObjectQuery<T>(marsh, key));
}
protected <T,C extends Collection<T>> Collection<T> findAll (
Class<T> type, Key key)
throws PersistenceException
{
DepotMarshaller<T> marsh = getMarshaller(type);
return invoke(new ObjectCollectionQuery<T>(marsh, key));
}
protected <T extends Collection> T findAll (
String index, Comparable key, CollectionQuery<T> query)
throws PersistenceException
{
return null;
}
protected void insert (Object record)
throws PersistenceException
{
}
protected int update (Object record)
throws PersistenceException
{
return 0;
}
protected int update (Object record, String ... modifiedFields)
throws PersistenceException
{
return 0;
}
protected <T> int updatePartial (Class<T> type,
Comparable primaryKey, Object ... fieldsValues)
throws PersistenceException
{
return 0;
}
protected <T> int updatePartial (Class<T> type,
Key key, Object ... fieldsValues)
throws PersistenceException
{
return 0;
}
protected <T> int updateLiteral (Class<T> type,
Comparable primaryKey, Object ... fieldsValues)
{
return 0;
}
protected <T> int updateLiteral (Class<T> type,
Key key, Object ... fieldsValues)
{
return 0;
}
protected int store (Object record)
throws PersistenceException
{
return 0;
}
protected void delete (Object record)
throws PersistenceException
{
}
protected <T> void delete (Class<T> type, Comparable primaryKey)
throws PersistenceException
{
}
protected <T> void deleteAll (Class<T> type, Key key)
throws PersistenceException
{
}
protected <T> DepotMarshaller<T> getMarshaller (Class<T> type)
{
@SuppressWarnings("unchecked")DepotMarshaller<T> marshaller =
(DepotMarshaller<T>)_marshallers.get(type);
if (marshaller == null) {
_marshallers.put(type, marshaller = new DepotMarshaller<T>(type));
}
return marshaller;
}
protected <T> T invoke (Query<T> query)
throws PersistenceException
{
// TODO: check the cache using query.getKey()
// TODO: retry query on transient failure
Connection conn = _conprov.getConnection(getIdent(), true);
try {
return query.invoke(conn);
} catch (SQLException sqe) {
throw new PersistenceException("Query failure " + query, sqe);
} finally {
_conprov.releaseConnection(getIdent(), true, conn);
}
}
/**
* Returns the identifier to be used when obtaining JDBC connections from
* our connection provider. The default implementation uses the class name
* of this repository.
*/
protected String getIdent ()
{
return getClass().getName();
}
protected static class Key
{
public String[] indices;
public Comparable[] values;
public Key (String[] indices, Comparable[] values)
{
this.indices = indices;
this.values = values;
}
public Key (String index, Comparable value)
{
this(new String[] { index }, new Comparable[] { value });
}
public String toWhereClause ()
{
StringBuffer where = new StringBuffer();
for (int ii = 0; ii < indices.length; ii++) {
if (ii > 0) {
where.append(" and ");
}
where.append(indices[ii]).append(" = ?");
}
return where.toString();
}
}
protected static class Key2 extends Key
{
public Key2 (String index1, Comparable value1,
String index2, Comparable value2)
{
super(new String[] { index1, index2 },
new Comparable[] { value1, value2 });
}
}
protected static abstract class Query<T>
{
public Key getKey ()
{
return _key;
}
public abstract T invoke (Connection conn)
throws SQLException, PersistenceException;
protected Query (Key key)
{
_key = key;
}
protected Key _key;
}
protected static abstract class CollectionQuery<T extends Collection>
{
public abstract T invoke (Connection conn)
throws SQLException, PersistenceException;
}
protected static class ObjectQuery<T> extends Query<T>
{
public ObjectQuery (DepotMarshaller<T> marsh, Key key) {
super(key);
_marsh = marsh;
}
public T invoke (Connection conn)
throws SQLException, PersistenceException
{
PreparedStatement stmt = _marsh.createQuery(conn, _key);
try {
T result = null;
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
result = _marsh.createObject(rs);
}
// TODO: if (rs.next()) issue warning?
rs.close();
return result;
} finally {
stmt.close();
}
}
protected DepotMarshaller<T> _marsh;
}
protected static class ObjectCollectionQuery<T>
extends Query<ArrayList<T>>
{
public ObjectCollectionQuery (DepotMarshaller<T> marsh, Key key) {
super(key);
_marsh = marsh;
}
public ArrayList<T> invoke (Connection conn)
throws SQLException, PersistenceException
{
PreparedStatement stmt = _marsh.createQuery(conn, _key);
try {
ArrayList<T> results = new ArrayList<T>();
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
results.add(_marsh.createObject(rs));
}
return results;
} finally {
stmt.close();
}
}
protected DepotMarshaller<T> _marsh;
}
// protected static abstract class InstanceUpdate<T>
// {
// public void invoke (T object)
// throws PersistenceException;
// }
// protected static abstract class CollectionUpdate<T>
// {
// public void invoke (Collection<T> collection)
// throws PersistenceException;
// }
protected ConnectionProvider _conprov;
protected HashMap<Class<?>, DepotMarshaller<?>> _marshallers =
new HashMap<Class<?>, DepotMarshaller<?>>();
}
@@ -0,0 +1,210 @@
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001-2006 Michael Bayne
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.jdbc.depot;
import java.lang.reflect.Field;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
/**
* Handles the marshalling and unmarshalling of a particular field of a
* persistent object.
*
* @see DepotMarshaller
*/
public abstract class FieldMarshaller
{
/**
* Creates and returns a field marshaller for the specified field. Throws
* an exception if the field in question cannot be marshalled.
*/
public static FieldMarshaller createMarshaller (Field field)
{
Class<?> ftype = field.getType();
FieldMarshaller marshaller;
// primitive types
if (ftype.equals(Boolean.TYPE)) {
marshaller = new PBooleanMarshaller();
} else if (ftype.equals(Byte.TYPE)) {
marshaller = new PByteMarshaller();
} else if (ftype.equals(Short.TYPE)) {
marshaller = new PShortMarshaller();
} else if (ftype.equals(Integer.TYPE)) {
marshaller = new PIntMarshaller();
} else if (ftype.equals(Long.TYPE)) {
marshaller = new PLongMarshaller();
} else if (ftype.equals(Float.TYPE)) {
marshaller = new PFloatMarshaller();
} else if (ftype.equals(Double.TYPE)) {
marshaller = new PDoubleMarshaller();
// "natural" types
} else if (ftype.equals(Byte.class) ||
ftype.equals(Short.class) ||
ftype.equals(Integer.class) ||
ftype.equals(Long.class) ||
ftype.equals(Float.class) ||
ftype.equals(Double.class) ||
ftype.equals(String.class)) {
marshaller = new ObjectMarshaller();
// TODO: byte array, (other array types?)
// SQL types
} else if (ftype.equals(Date.class) ||
ftype.equals(Time.class) ||
ftype.equals(Timestamp.class) ||
ftype.equals(Blob.class) ||
ftype.equals(Clob.class)) {
marshaller = new ObjectMarshaller();
} else {
throw new IllegalArgumentException(
"Cannot marshall field of type '" + ftype.getName() + "'.");
}
marshaller.init(field);
return marshaller;
}
/**
* Reads the value of our field from the persistent object and sets that
* value into the specified column of the supplied prepared statement.
*/
public abstract void setValue (Object po, PreparedStatement ps, int column)
throws SQLException, IllegalAccessException;
/**
* Reads the specified column from the supplied result set and writes it to
* the appropriate field of the persistent object.
*/
public abstract void getValue (ResultSet rset, int column, Object po)
throws SQLException, IllegalAccessException;
/**
* Returns the name of the table column used to store this field.
*/
public String getColumnName ()
{
return _field.getName();
}
protected void init (Field field)
{
_field = field;
}
protected static class PBooleanMarshaller extends FieldMarshaller {
public void setValue (Object po, PreparedStatement ps, int column)
throws SQLException, IllegalAccessException {
ps.setBoolean(column, _field.getBoolean(po));
}
public void getValue (ResultSet rs, int column, Object po)
throws SQLException, IllegalAccessException {
_field.setBoolean(po, rs.getBoolean(column));
}
}
protected static class PByteMarshaller extends FieldMarshaller {
public void setValue (Object po, PreparedStatement ps, int column)
throws SQLException, IllegalAccessException {
ps.setByte(column, _field.getByte(po));
}
public void getValue (ResultSet rs, int column, Object po)
throws SQLException, IllegalAccessException {
_field.setByte(po, rs.getByte(column));
}
}
protected static class PShortMarshaller extends FieldMarshaller {
public void setValue (Object po, PreparedStatement ps, int column)
throws SQLException, IllegalAccessException {
ps.setShort(column, _field.getShort(po));
}
public void getValue (ResultSet rs, int column, Object po)
throws SQLException, IllegalAccessException {
_field.setShort(po, rs.getShort(column));
}
}
protected static class PIntMarshaller extends FieldMarshaller {
public void setValue (Object po, PreparedStatement ps, int column)
throws SQLException, IllegalAccessException {
ps.setInt(column, _field.getInt(po));
}
public void getValue (ResultSet rs, int column, Object po)
throws SQLException, IllegalAccessException {
_field.setInt(po, rs.getInt(column));
}
}
protected static class PLongMarshaller extends FieldMarshaller {
public void setValue (Object po, PreparedStatement ps, int column)
throws SQLException, IllegalAccessException {
ps.setLong(column, _field.getLong(po));
}
public void getValue (ResultSet rs, int column, Object po)
throws SQLException, IllegalAccessException {
_field.setLong(po, rs.getLong(column));
}
}
protected static class PFloatMarshaller extends FieldMarshaller {
public void setValue (Object po, PreparedStatement ps, int column)
throws SQLException, IllegalAccessException {
ps.setFloat(column, _field.getFloat(po));
}
public void getValue (ResultSet rs, int column, Object po)
throws SQLException, IllegalAccessException {
_field.setFloat(po, rs.getFloat(column));
}
}
protected static class PDoubleMarshaller extends FieldMarshaller {
public void setValue (Object po, PreparedStatement ps, int column)
throws SQLException, IllegalAccessException {
ps.setDouble(column, _field.getDouble(po));
}
public void getValue (ResultSet rs, int column, Object po)
throws SQLException, IllegalAccessException {
_field.setDouble(po, rs.getDouble(column));
}
}
protected static class ObjectMarshaller extends FieldMarshaller {
public void setValue (Object po, PreparedStatement ps, int column)
throws SQLException, IllegalAccessException {
ps.setObject(column, _field.get(po));
}
public void getValue (ResultSet rs, int column, Object po)
throws SQLException, IllegalAccessException {
_field.set(po, rs.getObject(column));
}
}
protected Field _field;
}