Initial upload of DBBean class may contain bugs
This commit is contained in:
parent
186d8d50a3
commit
497f7807f3
2 changed files with 413 additions and 0 deletions
307
src/zutil/db/bean/DBBean.java
Normal file
307
src/zutil/db/bean/DBBean.java
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
package zutil.db.bean;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import zutil.db.DBConnection;
|
||||
|
||||
/**
|
||||
* The class that extends this will be able to save its state to a DB.
|
||||
* Fields that are transient will be ignored, and fields that extend
|
||||
* DBBean will be replaced by the id field of that class.
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public abstract class DBBean {
|
||||
|
||||
/**
|
||||
* Sets the name of the table in the database
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface DBTable {
|
||||
String value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the field as the id column in the table
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface DBTableID {}
|
||||
|
||||
/**
|
||||
* A Class that contains information about a bean
|
||||
*/
|
||||
protected static class DBBeanConfig{
|
||||
/** The name of the table in the DB */
|
||||
protected String tableName;
|
||||
/** The id field */
|
||||
protected Field id_field;
|
||||
/** All the fields in the bean */
|
||||
protected ArrayList<Field> fields;
|
||||
|
||||
protected DBBeanConfig(){
|
||||
fields = new ArrayList<Field>();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This is a cache of all the initialized beans
|
||||
*/
|
||||
private static HashMap<Class<? extends DBBean>,DBBeanConfig> beanConfigs = new HashMap<Class<? extends DBBean>,DBBeanConfig>();
|
||||
|
||||
|
||||
protected DBBean(){
|
||||
if( !beanConfigs.containsKey( this.getClass() ) )
|
||||
initBeanConfig( this.getClass() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the ID field of the bean or null if there is non
|
||||
*/
|
||||
public static Field getIDField(Class<? extends DBBean> c){
|
||||
if( !beanConfigs.containsKey( c ) )
|
||||
initBeanConfig( c );
|
||||
return beanConfigs.get( c ).id_field;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return all the fields except the ID field
|
||||
*/
|
||||
public static ArrayList<Field> getFields(Class<? extends DBBean> c){
|
||||
if( !beanConfigs.containsKey( c ) )
|
||||
initBeanConfig( c );
|
||||
return beanConfigs.get( c ).fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the configuration object for the specified class
|
||||
*/
|
||||
protected static DBBeanConfig getBeanConfig(Class<? extends DBBean> c){
|
||||
if( !beanConfigs.containsKey( c ) )
|
||||
initBeanConfig( c );
|
||||
return beanConfigs.get( c );
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches the fields
|
||||
*/
|
||||
private static void initBeanConfig(Class<? extends DBBean> c){
|
||||
Field[] fields = c.getDeclaredFields();
|
||||
DBBeanConfig config = new DBBeanConfig();
|
||||
// Find the table name
|
||||
if( c.getAnnotation(DBTable.class) != null )
|
||||
config.tableName = c.getAnnotation(DBTable.class).value().replace('\"', ' ');
|
||||
// Add the fields in the bean
|
||||
for( Field field : fields ){
|
||||
if( !Modifier.isTransient(field.getModifiers()) ){
|
||||
if(field.getAnnotation(DBBean.DBTableID.class) != null)
|
||||
config.id_field = field;
|
||||
config.fields.add( field );
|
||||
}
|
||||
}
|
||||
|
||||
beanConfigs.put(c, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the Object to the DB
|
||||
*/
|
||||
public void save(DBConnection sql) throws SQLException{
|
||||
Class<? extends DBBean> c = this.getClass();
|
||||
DBBeanConfig config = beanConfigs.get(c);
|
||||
try {
|
||||
// Generate the SQL
|
||||
StringBuilder query = new StringBuilder();
|
||||
query.append("REPLACE INTO ? ");
|
||||
|
||||
for( Field field : config.fields ){
|
||||
query.append(" ");
|
||||
query.append(field.getName());
|
||||
query.append("=?, ");
|
||||
}
|
||||
query.delete( query.length()-2, query.length());
|
||||
PreparedStatement stmt = sql.getPreparedStatement( sql.toString() );
|
||||
// add the table name
|
||||
stmt.setObject(1, config.tableName);
|
||||
// Put in the variables in the SQL
|
||||
for(int i=0; i<config.fields.size() ;++i ){
|
||||
Field field = config.fields.get(i);
|
||||
|
||||
// Another DBBean class
|
||||
if( DBBean.class.isAssignableFrom( field.getDeclaringClass() )){
|
||||
DBBean subobj = (DBBean)field.get(this);
|
||||
subobj.save(sql);
|
||||
stmt.setObject(i+2, getBeanConfig(subobj.getClass()) );
|
||||
}
|
||||
// Normal field
|
||||
else
|
||||
stmt.setObject(i+2, field.get(this));
|
||||
}
|
||||
|
||||
// Execute the SQL
|
||||
DBConnection.exec(stmt);
|
||||
} catch (SQLException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new SQLException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the object from the DB
|
||||
*/
|
||||
public void delete(DBConnection sql){
|
||||
Class<? extends DBBean> c = this.getClass();
|
||||
DBBeanConfig config = beanConfigs.get(c);
|
||||
if( config.id_field == null )
|
||||
throw new NoSuchElementException("DBTableID annotation missing in bean!");
|
||||
try {
|
||||
PreparedStatement stmt = sql.getPreparedStatement(
|
||||
"DELETE FROM ? WHERE "+ config.id_field +"=?");
|
||||
// Put in the variables in the SQL
|
||||
stmt.setObject(1, config.tableName );
|
||||
stmt.setObject(2, config.id_field.get(this) );
|
||||
|
||||
// Execute the SQL
|
||||
DBConnection.exec(stmt);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all the rows in the table into a LinkedList
|
||||
*
|
||||
* @param <T> is the class of the bean
|
||||
* @param c is the class of the bean
|
||||
* @return a LinkedList with all the Beans in the DB
|
||||
*/
|
||||
public static <T extends DBBean> List<T> load(DBConnection sql, Class<T> c) throws SQLException {
|
||||
// Initiate a BeanConfig if there is non
|
||||
if( !beanConfigs.containsKey( c ) )
|
||||
initBeanConfig( c );
|
||||
DBBeanConfig config = beanConfigs.get(c);
|
||||
// Generate query
|
||||
PreparedStatement stmt = sql.getPreparedStatement( "SELECT * FROM ?" );
|
||||
stmt.setString(1, config.tableName);
|
||||
// Run query
|
||||
List<T> list = DBConnection.exec(stmt, DBBeanResultHandler.createList(c) );
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all the rows in the table into a LinkedList
|
||||
*
|
||||
* @param <T> is the class of the bean
|
||||
* @param c is the class of the bean
|
||||
* @param id is the id value of the bean
|
||||
* @return a DBBean Object with the specific id or null
|
||||
*/
|
||||
public static <T extends DBBean> T load(DBConnection sql, Class<T> c, Object id) throws SQLException {
|
||||
// Initiate a BeanConfig if there is non
|
||||
if( !beanConfigs.containsKey( c ) )
|
||||
initBeanConfig( c );
|
||||
DBBeanConfig config = beanConfigs.get(c);
|
||||
// Generate query
|
||||
PreparedStatement stmt = sql.getPreparedStatement( "SELECT * FROM ? WHERE ?=? LIMIT 1" );
|
||||
stmt.setString(1, config.tableName);
|
||||
stmt.setString(2, config.id_field.getName());
|
||||
stmt.setObject(3, id );
|
||||
// Run query
|
||||
T obj = DBConnection.exec(stmt, DBBeanResultHandler.create(c) );
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a specific table for the given Bean
|
||||
*/
|
||||
public static void create(DBConnection sql, Class<? extends DBBean> c) throws SQLException{
|
||||
if( !beanConfigs.containsKey( c ) )
|
||||
initBeanConfig( c );
|
||||
DBBeanConfig config = beanConfigs.get(c);
|
||||
|
||||
// Generate the SQL
|
||||
StringBuilder query = new StringBuilder();
|
||||
query.append("CREATE TABLE ? ( ");
|
||||
|
||||
for( Field field : config.fields ){
|
||||
query.append(" ");
|
||||
query.append( field.getName() );
|
||||
query.append( classToDBName(c) );
|
||||
if( config.id_field.equals( field ) )
|
||||
query.append(" PRIMARY KEY");
|
||||
query.append(", ");
|
||||
}
|
||||
query.delete( query.length()-2, query.length());
|
||||
query.append(")");
|
||||
PreparedStatement stmt = sql.getPreparedStatement( sql.toString() );
|
||||
// add the table name
|
||||
stmt.setObject(1, config.tableName);
|
||||
|
||||
// Execute the SQL
|
||||
DBConnection.exec(stmt);
|
||||
}
|
||||
|
||||
private static String classToDBName(Class<?> c){
|
||||
if( c == String.class) return "CLOB"; // TEXT
|
||||
else if(c == Short.class) return "SMALLINT";
|
||||
else if(c == short.class) return "SMALLINT";
|
||||
else if(c == Integer.class) return "INTEGER";
|
||||
else if(c == int.class) return "INTEGER";
|
||||
else if(c == BigInteger.class) return "BIGINT";
|
||||
else if(c == Long.class) return "DECIMAL";
|
||||
else if(c == long.class) return "DECIMAL";
|
||||
else if(c == Float.class) return "DOUBLE";
|
||||
else if(c == float.class) return "DOUBLE";
|
||||
else if(c == Double.class) return "DOUBLE";
|
||||
else if(c == double.class) return "DOUBLE";
|
||||
else if(c == BigDecimal.class) return "DECIMAL";
|
||||
else if(c == Boolean.class) return "BOOLEAN";
|
||||
else if(c == boolean.class) return "BOOLEAN";
|
||||
else if(c == Byte.class) return "BINARY(1)";
|
||||
else if(c == byte.class) return "BINARY(1)";
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a workaround if the field is not visible to other classes
|
||||
*
|
||||
* @param field is the field
|
||||
* @return the value of the field
|
||||
*/
|
||||
protected Object getFieldValue(Field field){
|
||||
try {
|
||||
return field.get(this);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a workaround if the field is not visible to other classes
|
||||
*
|
||||
* @param field is the field
|
||||
* @return the value of the field
|
||||
*/
|
||||
protected void setFieldValue(Field field, Object o){
|
||||
try {
|
||||
field.set(this, o);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
106
src/zutil/db/bean/DBBeanResultHandler.java
Normal file
106
src/zutil/db/bean/DBBeanResultHandler.java
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
package zutil.db.bean;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import zutil.db.SQLResultHandler;
|
||||
import zutil.db.bean.DBBean.DBBeanConfig;
|
||||
|
||||
public class DBBeanResultHandler<T> implements SQLResultHandler<T>{
|
||||
|
||||
private Class<? extends DBBean> bean_class;
|
||||
private DBBeanConfig bean_config;
|
||||
private boolean list;
|
||||
|
||||
/**
|
||||
* Creates a new instance of this class that returns only one bean
|
||||
*
|
||||
* @param cl is the DBBean class that will be parsed from the SQL result
|
||||
* @return a new instance of this class
|
||||
*/
|
||||
public static <C extends DBBean> DBBeanResultHandler<C> create(Class<C> cl){
|
||||
return new DBBeanResultHandler<C>(cl, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of this class that returns a list of beans
|
||||
*
|
||||
* @param cl is the DBBean class that will be parsed from the SQL result
|
||||
* @return a new instance of this class
|
||||
*/
|
||||
public static <C extends DBBean> DBBeanResultHandler<List<C>> createList(Class<C> cl){
|
||||
return new DBBeanResultHandler<List<C>>(cl, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of this class
|
||||
*
|
||||
* @param cl is the DBBean class that will be parsed from the SQL result
|
||||
*/
|
||||
protected DBBeanResultHandler(Class<? extends DBBean> cl, boolean list) {
|
||||
this.bean_class = cl;
|
||||
this.list = list;
|
||||
this.bean_config = DBBean.getBeanConfig( cl );
|
||||
}
|
||||
|
||||
/**
|
||||
* Is called to handle an result from an query.
|
||||
*
|
||||
* @param stmt is the query
|
||||
* @param result is the ResultSet
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T handle(Statement stmt, ResultSet result) throws SQLException{
|
||||
if( list ){
|
||||
LinkedList<DBBean> bean_list = new LinkedList<DBBean>();
|
||||
while( result.next() ){
|
||||
DBBean obj = createBean(result);
|
||||
bean_list.add( obj );
|
||||
}
|
||||
return (T) bean_list;
|
||||
}
|
||||
else{
|
||||
if( result.next() )
|
||||
return (T) createBean(result);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new bean and assigns field values from the ResultSet
|
||||
*
|
||||
* @param result is where the field values for the bean will bee read from, the cursor should be in front of the data
|
||||
* @return a new instance of the bean
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private DBBean createBean(ResultSet result) throws SQLException{
|
||||
try {
|
||||
DBBean obj = bean_class.newInstance();
|
||||
|
||||
for( Field field : bean_config.fields ){
|
||||
String name = field.getName();
|
||||
|
||||
// Another DBBean class
|
||||
if( DBBean.class.isAssignableFrom( field.getDeclaringClass() )){
|
||||
DBBean subobj = DBBean.load(null, (Class<? extends DBBean>)field.getDeclaringClass(), result.getObject(name));
|
||||
obj.setFieldValue(field, subobj);
|
||||
}
|
||||
// Normal field
|
||||
else
|
||||
obj.setFieldValue(field, result.getObject(name));
|
||||
}
|
||||
return obj;
|
||||
|
||||
} catch (InstantiationException e) {
|
||||
throw new SQLException(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new SQLException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue