All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sakaiproject.genericdao.springjdbc.SimpleDataMapper Maven / Gradle / Ivy

Go to download

Generic Dao is a Java package which allows a developer to skip writing DAOs for their persistence objects when they are using Spring and/or Hibernate. The package was originally created by Aaron Zeckoski for the Evaluation System project but was repackaged to make it distributable by request. It is used in the RSF framework (http://www2.caret.cam.ac.uk/rsfwiki/). Note about the BeanUtils provided dependency: BeanUtils is not required if you are not using it in your project. Note about the Hibernate provided dependency: Hibernate is not required if you are not using it in your project.

The newest version!
/**
 * $Id$
 * $URL$
 * SimpleDataMapper.java - genericdao - Apr 25, 2008 4:29:45 PM - azeckoski
 **************************************************************************
 * Copyright (c) 2008 Aaron Zeckoski
 * Licensed under the Apache License, Version 2
 * 
 * A copy of the Apache License, Version 2 has been included in this 
 * distribution and is available at: http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 * Aaron Zeckoski ([email protected]) ([email protected]) ([email protected])
 */

package org.sakaiproject.genericdao.springjdbc;

import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.azeckoski.reflectutils.ClassLoaderUtils;
import org.sakaiproject.genericdao.api.mappers.DataMapper;
import org.sakaiproject.genericdao.api.mappers.NamesRecord;
import org.sakaiproject.genericdao.api.translators.DatabaseTranslator;
import org.sakaiproject.genericdao.springjdbc.translators.BasicTranslator;


/**
 * This class allows us to generate a {@link DataMapper} using Spring or anything else that
 * can set/inject strings to create an object, most of the functions are assumed to
 * be handled automatically by generic DAO
 * 
 * @author Aaron Zeckoski ([email protected])
 */
public class SimpleDataMapper implements DataMapper {

    /**
     * Default constructor which is used by Spring mostly,
     * you will need to at least set the 
     */
    public SimpleDataMapper() {}

    /**
     * This is primarily for use when using all gendao conventions and annotations with a class,
     * otherwise this will not include enough information to complete the mapping,
     * the tablename will be the classname if no annotation is used,
     * the id column must be ID if no annotation is used and the property must also be id
     * 
     * @param persistentType any class type to map as a persistent type
     */
    public SimpleDataMapper(Class persistentType) {
        this.persistentType = persistentType;
    }

    /**
     * This is useful when adhering to some of the gendao conventions
     * but needing to specify your own id property and tableName
     * 
     * @param persistentType any class type to map as a persistent type
     * @param idPropertyName this is the property name matching the identifier for the table
     * @param tableName this is the name of the table that matches this persistent class
     */
    public SimpleDataMapper(Class persistentType, String idPropertyName, String tableName) {
        this.idPropertyName = idPropertyName;
        this.persistentType = persistentType;
        this.tableName = tableName;
    }

    private boolean usePropertyNamesForColumns = false;
    public boolean isUsePropertyNamesForColumns() {
        return usePropertyNamesForColumns;
    }
    /**
     * (OPTIONAL)
     * This will cause the mapper to use the property names as is (case and characters)
     * for the columns names instead of transforming them (e.g. property: myThing => column: myThing),
     * default is false (that means property: myThing => column: MY_THING)
     */
    public void setUsePropertyNamesForColumns(boolean usePropertyNamesForColumns) {
        this.usePropertyNamesForColumns = usePropertyNamesForColumns;
    }

    protected String idPropertyName = null;
    /**
     * (OPTIONAL)
     * This is the name of the property on the persistent object which defines the unique identifier,
     * defaults to null if unset but that will be replaced by the proper id or the default of "id"
     */
    public void setIdPropertyName(String idPropertyName) {
        this.idPropertyName = idPropertyName;
    }
    /* (non-Javadoc)
     * @see org.sakaiproject.genericdao.api.DataMapper#getIdPropertyName()
     */
    public String getIdPropertyName() {
        return idPropertyName;
    }

    private Class persistentType;
    /**
     * Allows setting the persistent type,
     * this should normally be done in the constructor and cannot be changed
     * once it has been set
     * @param persistentType this is the class that maps to the DB table
     * @throws IllegalArgumentException if the input is null or the value is already set
     */
    public void setPersistentType(Class persistentType) {
        if (persistentType == null) {
            throw new IllegalArgumentException("The persistentType cannot be null");
        }
        if (this.persistentType != null) {
            throw new IllegalArgumentException("The persistentType has already been set and cannot be reset");
        }
        this.persistentType = persistentType;
    }
    /**
     * (REQUIRED)
     * set this to the class name (e.g. org.project.MyClass) 
     * and it will be converted into the class object
     * @param persistentClassname this is the fully qualified classname of the persistent type
     */
    public void setPersistentClassname(String persistentClassname) {
        persistentType = ClassLoaderUtils.getClassFromString(persistentClassname);
        if (persistentType == null) {
            throw new IllegalArgumentException(
                    "Invalid class name for persistentClassname, could not create class from string: " + persistentClassname);
        }
    }
    /* (non-Javadoc)
     * @see org.sakaiproject.genericdao.api.DataMapper#getPersistentType()
     */
    public Class getPersistentType() {
        return persistentType;
    }

    protected String tableName;
    /**
     * (OPTIONAL)
     * set this to the name of the table,
* defaults to a name built from the simple class name of the persistent class */ public void setTableName(String tableName) { this.tableName = tableName; } /* (non-Javadoc) * @see org.sakaiproject.genericdao.api.DataMapper#getTableName() */ public String getTableName() { if (tableName == null) { if (persistentType != null) { tableName = BasicTranslator.makeTableNameFromClass(persistentType); } else { throw new IllegalStateException("tablename and persistentType are both null, invalid DataMapper"); } } return tableName; } private NamesRecord namesRecord = null; /** * (OPTIONAL) * Set this to a map of the persistent object properties to database column names, * String -> String,
* example: "id" -> "ID", "title" -> "ITEM_TITLE"
* defaults to autogenerated column names which are uppercased and underscored (as in example above) * @see SimpleDataMapper#setNamesUsed(String[]) * @see SimpleDataMapper#setNamesRecord(NamesRecord) */ public void setNamesMapping(Map namesMapping) { if (namesMapping != null && ! namesMapping.isEmpty()) { namesRecord = new NamesRecord(); for (Entry entry : namesMapping.entrySet()) { String key = entry.getKey(); String dbName = entry.getValue(); if (dbName != null && ! "".equals(dbName)) { namesRecord.setNameMapping(key, dbName); } } } } /** * (OPTIONAL) * Set this to an array of the persistent object properties which will be used for this mapping, * anything not listed will be ignored when forming queries, * this is the least work but depends on the more conventions and allows the least control
* NOTE: Be sure to set {@link #setUsePropertyNamesForColumns(boolean)} before calling this method
* Example: ["id", "title"],
* "id" -> "ID", "title" -> "ITEM_TITLE"
* defaults to autogenerated column names which are uppercased and underscored (as in example above) * depending on the setting for {@link #isUsePropertyNamesForColumns()} * @see SimpleDataMapper#setNamesMapping(Map) * @see SimpleDataMapper#setNamesRecord(NamesRecord) */ public void setNamesUsed(String[] namesUsed) { if (namesUsed == null) { throw new IllegalArgumentException("NamesRecord cannot be null"); } namesRecord = new NamesRecord(); for (int i = 0; i < namesUsed.length; i++) { String propertyName = namesUsed[i]; String columnName = propertyName; if (! isUsePropertyNamesForColumns()) { columnName = BasicTranslator.makeDBNameFromCamelCase(propertyName); } namesRecord.setNameMapping(propertyName, columnName); } } /** * (OPTIONAL) * Set the {@link NamesRecord} used for this mapping directly, * this controls the mapping of object properties to database columns, * this is more work than is probably needed but allows the most control, see the other methods * @see SimpleDataMapper#setNamesMapping(Map) * @see SimpleDataMapper#setNamesUsed(String[]) */ public void setNamesRecord(NamesRecord namesRecord) { if (namesRecord == null) { throw new IllegalArgumentException("NamesRecord cannot be null"); } this.namesRecord = namesRecord; } /* (non-Javadoc) * @see org.sakaiproject.genericdao.api.mappers.DataMapper#definePropertyToColumnMapping() */ public NamesRecord getPropertyToColumnNamesMapping() { return namesRecord; } private Map dbTypeToDDL = new HashMap(); public Map getDbTypeToDDL() { return dbTypeToDDL; } /** * Convenience method which allows setting the DDL for a database type, * this will be appended to existing entries
* Generally only useful if you only need to set a single DDL
* * @param databaseTypeConstant one of the database type constants from, e.g. {@link #DBTYPE_MYSQL}, * this indicates which database your DDL should work for * @param ddl the ddl to execute to create the table(s) for this persistent object */ public void addDBTypeAndDDL(String databaseTypeConstant, String ddl) { if (databaseTypeConstant == null || ddl == null) { throw new IllegalArgumentException("databaseTypeConstant and ddl cannot be null"); } if (dbTypeToDDL == null) { dbTypeToDDL = new HashMap(); } dbTypeToDDL.put(databaseTypeConstant, ddl); } private Map dbTypeToFilename = new HashMap(); public Map getDbTypeToFilename() { return dbTypeToFilename; } /** * (REQUIRED/OPTIONAL) * Sets the map of databaseTypeConstant -> file containing the DDL script, * the DDL will be looked up and stored in the class and executed on service init
* This must be set if the table does not already exist but if it does then this is optional
* The first non-comment ('--') line will be run, and if successful, * all other non-comment lines will be run. SQL statements may be on * multiple lines but must have ';' terminators.
* Can use the {@link #makeDDLTypeMap(String, String[])} method to make this more easily
* The following keys will be replaced automatically:
* {TABLENAME} - the value returned by {@link #getTableName()}
* {ID} - the column name of the unique identifier
* {IDSEQNAME} - (Oracle) a sequence name will be generated and inserted * based on the tablename for use in generating IDs, * if you want to specify your own sequence name then you will lose * the ability to have the ID inserted into newly created objects
* NOTE: the file must be in your jar/war/package so it can * be located in the classloader, * use something like this in your maven pom.xml resources tag: * <resource> <directory>${basedir}/src/sql</directory> <includes> <include>** /*.sql</include> </includes> </resource> * Remove the space between ** and /* from the above sample
* For example: src/sql/mysql.sql, src/sql/mysql/myproject.ddl
* @see #setDBTypeToDDL(Map) * @see #makeDDLTypeMap(String, String[]) */ public void setDBTypeToFile(Map dbTypeToFile) { if (dbTypeToFilename == null) { dbTypeToFilename = new HashMap(); } if (dbTypeToFile != null) { for (Entry entry : dbTypeToFile.entrySet()) { String dbtype = entry.getKey().toUpperCase(); String value = entry.getValue(); if (value != null) { this.dbTypeToFilename.put(dbtype, value); } } } } /** * (REQUIRED/OPTIONAL) * Sets the map of databaseTypeConstant -> DDL, * these will be appended to any existing values
* This must be set if the table does not already exist but if it does then this is optional
* The first non-comment ('--') line will be run, and if successful, * all other non-comment lines will be run. SQL statements may be on * multiple lines but must have ';' terminators.
* The following keys will be replaced automatically:
* {TABLENAME} - the value returned by {@link #getTableName()}
* {ID} - the column name of the unique identifier
* {IDSEQNAME} - (Oracle) a sequence name will be generated and inserted * based on the tablename for use in generating IDs, * if you want to specify your own sequence name then you will lose * the ability to have the ID inserted into newly created objects
* @see #setDBTypeToFile(Map) */ public void setDBTypeToDDL(Map dbTypeToDDL) { if (dbTypeToDDL == null) { dbTypeToDDL = new HashMap(); } if (dbTypeToDDL != null) { for (Entry entry : dbTypeToDDL.entrySet()) { String dbtype = entry.getKey().toUpperCase(); String value = entry.getValue(); if (value != null) { this.dbTypeToDDL.put(dbtype, value); } } } } /* (non-Javadoc) * @see org.sakaiproject.genericdao.api.DataMapper#generateDDL(java.lang.String) */ public String generateDDL(String databaseTypeConstant) { String ddl = null; if (dbTypeToDDL != null) { if (dbTypeToDDL.containsKey(databaseTypeConstant)) { ddl = dbTypeToDDL.get(databaseTypeConstant); } } return ddl; //throw new UnsupportedOperationException("No support for the database type: " + databaseTypeConstant); } /** * This is a convenience method which assists in creating the ddl type map, * it basically allows you to easily create the map if you have followed the convention * of placing all your ddl files in folders which are equivalent to the name * of the database type they are created for and named them all identically with * a name which is similar to the * @param fileName the name of the file (e.g. users.sql) * @param types the types to include in the map, use the constants from {@link DatabaseTranslator} (e.g. "derby") * @return the map which can be given to the {@link #setDBTypeToFile(Map)} method */ public static Map makeDDLTypeMap(String fileName, String[] types) { if (fileName == null || "".equals(fileName)) { throw new IllegalArgumentException("fileName must not be null"); } if (types == null || types.length == 0) { throw new IllegalArgumentException("Must include at least one type in the types array"); } HashMap dbTypeToFile = new HashMap(); // As this will be used on the classpath, and may be used to reference packages inside JARs, // we do NOT use File.separator, as that will only work for unpackaged resources. // Always using "/" will work for both packaged and unpackaged content - on any platform for (String type : types) { dbTypeToFile.put(type, type.toLowerCase() + "/" + fileName); } return dbTypeToFile; } /** * Convenience method which will take a filename and an array of DBTYPE String constants * (from {@link DatabaseTranslator}, example {@link DatabaseTranslator#DBTYPE_MYSQL}) * and produce a map of standard DDL types to file paths
* Will create a map like so: dbType -> [prefixPath/]dbType/fileName * * @param fileName the name of the sql file (e.g. myTableDDL.sql) * @param dbTypes an array with 1 or more dbTypes which you have DDL files for * @param prefixPath (optional) the prefix to append before the dbType and fileName (should not have a leading "/"), * can be null or "" if npt used * @return the map of dbType to file paths * @throws IllegalArgumentException if the filename or dbTYpes are null or empty */ public static Map makeDDLMap(String fileName, String[] dbTypes, String prefixPath) { if (fileName == null || "".equals(fileName)) { throw new IllegalArgumentException("filename must be set and cannot be null"); } if (dbTypes == null || dbTypes.length == 0) { throw new IllegalArgumentException("at least one dbType must be set and included, cannot be null or empty"); } HashMap dbTypeToFile = new HashMap(); if (prefixPath == null) { prefixPath = ""; } else { // As this will be used on the classpath, and may be used to reference packages inside JARs, // we do NOT use File.separator, as that will only work for unpackaged resources. // Always using "/" will work for both packaged and unpackaged content - on any platform if (!"/".equals(File.separator) && prefixPath.endsWith(File.separator)) { prefixPath = prefixPath.substring(0, prefixPath.length() - 1); } if (! prefixPath.endsWith("/")) { prefixPath += "/"; } } for (int i = 0; i < dbTypes.length; i++) { String type = dbTypes[i]; dbTypeToFile.put(type, prefixPath + type.toLowerCase() + "/" + fileName); } return dbTypeToFile; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy