/*
* BaseLink - Generic object relational mapping
* Copyright (C) 2011 Ulrich Hilger, http://uhilger.de
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/
*/
package de.uhilger.baselink;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
/**
* Class PersistenceManager stores and retrieves Java objects to and from a
* relational database. Any object that implements interface Record can be
* read from or written to a database using this class.
*
*
In addition, any other class can be used directly as PersistenceManager
* wraps such other objects into GenericRecord objects implicitly.
*
*
Major aim of class PersistenceManager is to encapsulate the management
* of database related objects such as Connection, Statement and
* ResultSet to make sure proper opening and closing especially when used in
* conjunction with a connection pool.
*
*
In addition PersistenceManager provides methods that allow for an
* abundance of database read/write operations for convenience providing a base
* class to extend for individual needs.
*
* @author Copyright (c) Ulrich Hilger, http://uhilger.de
* @author Published under the terms and conditions of
* the GNU General Public License
* @version 3, March 22, 2011
*/
public class PersistenceManager {
private static final Logger logger = Logger.getLogger(PersistenceManager.class.getName());
public static final String NULL_STR = "null";
/** reference to data source to use for connections in production mode */
protected DataSource ds;
/** reference to JDBC url to use for connections in development mode */
protected String dburl;
/**
* Create a new object of class PersistenceManager
*/
public PersistenceManager() {
super();
}
/**
* Set the JDBC driver class name
* @param driverName name of JDBC driver class name
* @throws ClassNotFoundException when driver is not found
*/
public void setDriverName(String driverName) throws ClassNotFoundException {
Class.forName(driverName);
}
/**
* Set the database
*
*
Use either this method together with method setDriverName or
* method setDataSourceName to indicate the connection type
*
* @param url JDBC url
*/
public void setDatabase(String url) {
this.dburl = url;
}
/**
* Set the database
*
*
Use either this method or method setDataSourceName to indicate the connection type
*
* @param driverName class name of JDBC driver
* @param url JDBC url
* @throws ClassNotFoundException when driver is not found
*/
public void setDatabase(String driverName, String url) throws ClassNotFoundException {
Class.forName(driverName);
this.dburl = url;
}
/**
* Set name of DataSource
*
*
Use either this method or method setDatabase to indicate the connection type
*
* @param dataSourceName name of DataSource
* @throws NamingException when JNDI lookup fails
*/
public void setDataSourceName(String dataSourceName) throws NamingException {
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
ds = (DataSource) envCtx.lookup(dataSourceName);
}
/**
* Get a database connection
* @return a database connection
* @throws SQLException
*/
public Connection getConnection() {
Connection c = null;
try {
if (dburl != null) {
c = DriverManager.getConnection(dburl);
} else if(ds != null) {
c = ds.getConnection();
} else {
//throw new SQLException("Unable to get connection, DataSource and database URL are null.");
}
} catch(Exception ex) {
ex.printStackTrace();
}
return c;
}
/* -------------------- deletes ---------------------------- */
/**
* Delete a given object from the database
*
*
Use this method for single deletes. In cases where
* several subsequent deletes for objects of the same class
* are required the use of method delete(Object, Record)
* is recommended instead to minimise instantiation
* overhead.
*
* @param o object to delete
* @return the deleted object
*/
public Object delete(Object o) {
return delete(o, new GenericRecord(o.getClass()));
}
/**
* Delete a given object from the database
*
*
Use this method for single deletes. In cases where
* several subsequent deletes for objects of the same class
* are required the use of method delete(Connection, Object, Record)
* is recommended instead to minimise instantiation
* overhead.
*
* @param c the connection to use, expected to be open and established
* @param o object to delete
* @return the deleted object
*/
public Object delete(Connection c, Object o) {
return delete(c, o, new GenericRecord(o.getClass()));
}
/**
* Delete a given object from the database
* @param o object to delete
* @param record reference to object to use to map between object and database
* @return the deleted object
*/
public Object delete(Object o, Record record) {
Connection c = null;
try {
c = getConnection();
o = delete(c, o, record);
c.close();
c = null;
} catch (Exception ex) {
logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
} finally {
closeConnectionFinally(c);
}
return o;
}
/**
* Delete a given object from the database
* @param c the connection to use, expected to be open and established
* @param o object to delete
* @param record reference to object to use to map between object and database
* @return the deleted object
*/
public Object delete(Connection c, Object o, Record record) {
Object deletedObject = null;
PreparedStatement ps = null;
try {
ps = record.getDeleteStatment(c, o);
ps.executeUpdate();
ps.close();
ps = null;
deletedObject = o;
} catch (Exception ex) {
logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
} finally {
closeStatementFinally(ps);
}
return deletedObject;
}
/* ---------------------- inserts -------------------- */
/**
* Add an object to the database
*
*
Use this method for single inserts. In cases where
* several subsequent inserts for objects of the same class
* are required the use of method insert(Object, Record)
* is recommended instead to minimise instantiation
* overhead.
*
* @param o the object to add
* @return the added object
*/
public Object insert(Object o) {
return insert(o, new GenericRecord(o.getClass()));
}
/**
* Add an object to the database
*
*
Use this method for single inserts. In cases where
* several subsequent inserts for objects of the same class
* are required the use of method insert(Connection, Object, Record)
* is recommended instead to minimise instantiation
* overhead.
*
* @param c the connection to use, expected to be open and established
* @param o the object to add
* @return the object added to the database
*/
public Object insert(Connection c, Object o) {
return insert(c, o, new GenericRecord(o.getClass()));
}
/**
* Add an object to the database
* @param o object to add
* @param record reference to object to use to map between object and database
* @return the added object
*/
public Object insert(Object o, Record record) {
Connection c = null;
try {
c = getConnection();
o = insert(c, o, record);
c.close();
c = null;
} catch (Exception ex) {
logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
} finally {
closeConnectionFinally(c);
}
return o;
}
/**
* Add an object to the database
* @param c the connection to use, expected to be open and established
* @param o object to add
* @param record reference to object to use to map between object and database
* @return the object that was added
*/
public Object insert(Connection c, Object o, Record record) {
Object addedObject = null;
PreparedStatement ps = null;
try {
ps = record.getInsertStatment(c, o);
ps.executeUpdate();
ps.close();
ps = null;
addedObject = o;
} catch (Exception ex) {
logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
} finally {
closeStatementFinally(ps);
}
return addedObject;
}
/* --------------------------------- updates --------------------- */
/**
* update an object in the database
*
*
Use this method for single updates. In cases where
* several subsequent updates for objects of the same class
* are required the use of method update(Object, Record)
* is recommended instead to minimise instantiation
* overhead.
*
* @param o object to update
* @return the object that was updated
*/
public Object update(Object o) {
return update(o, new GenericRecord(o.getClass()));
}
/**
* update an object in the database
*
*
Use this method for single updates. In cases where
* several subsequent updates for objects of the same class
* are required the use of method update(Connection, Object, Record)
* is recommended instead to minimise instantiation
* overhead.
*
* @param c the connection to use, expected to be open and established
* @param o object to update
* @return the object that was updated
*/
public Object update(Connection c, Object o) {
return update(c, o, new GenericRecord(o.getClass()));
}
/**
* update an object in the database
* @param o object to update
* @param record reference to object to use to map between object and database
* @return the object that was updated
*/
public Object update(Object o, Record record) {
Connection c = null;
try {
c = getConnection();
o = update(c, o, record);
c.close();
c = null;
} catch (Exception ex) {
logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
} finally {
closeConnectionFinally(c);
}
return o;
}
/**
* update an object in the database
* @param c the connection to use, expected to be open and established
* @param o object to update
* @param record reference to object to use to map between object and database
* @return the object that was updated
*/
public Object update(Connection c, Object o, Record record) {
Object updatedObject = null;
PreparedStatement ps = null;
try {
ps = record.getUpdateStatment(c, o);
ps.executeUpdate();
ps.close();
ps = null;
updatedObject = o;
} catch (Exception ex) {
logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
} finally {
closeStatementFinally(ps);
}
return updatedObject;
}
/* --------------- selects ---------------- */
/**
* Select a list of objects through a given SQL statement
* @param sql sql query string that designates the requested objects
* @param record object to use to map db records to objects
* @return a list of objects that match the given query
*/
public List