package net.sf.jeasyorm; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import net.sf.jeasyorm.annotation.Column; import net.sf.jeasyorm.annotation.Table; import net.sf.jeasyorm.annotation.Transient; public class Mapping { private static Map<String, Mapping> mappingsByClassName = new HashMap<String, Mapping>(); protected Class<?> entityClass; protected Constructor<?> constructor; protected Map<String, FieldInfo> fieldsByName = new HashMap<String, FieldInfo>(); protected Map<String, FieldInfo> fieldsByLcColumnName = new HashMap<String, FieldInfo>(); protected String catalogName; protected String schemaName; protected String tableName; protected List<ColumnInfo> columns = new ArrayList<ColumnInfo>(); protected Map<String, ColumnInfo> columnsByLcColumnName = new HashMap<String, ColumnInfo>(); public static Mapping getMapping(EntityManager manager, Class<?> entityClass) { String key = (manager.getCache() != null ? manager.getCache() + ":" : "") + entityClass.getName(); Mapping mapping = mappingsByClassName.get(key); if (mapping == null && !mappingsByClassName.containsKey(key)) { try { mapping = manager.newMapping(entityClass); mappingsByClassName.put(key, mapping); } catch (SQLException e) { mappingsByClassName.put(key, null); } } return mapping; } public Mapping(EntityManager manager, Class<?> entityClass) throws SQLException { this.entityClass = entityClass; NameGuesser nameGuesser = manager.getNameGuesser(); // transient fields Set<String> transientFieldNames = new HashSet<String>(); // column names as specified with @Column annotation Map<String, String> columnNamesByFieldName = new HashMap<String, String>(); // get fields for (Field field : entityClass.getDeclaredFields()) { Transient t = field.getAnnotation(Transient.class); if (t != null) { transientFieldNames.add(field.getName()); } else if (isAccessible(field)) { this.fieldsByName.put(field.getName(), new FieldInfo(field.getName(), field.getType(), field, null, null)); } Column c = field.getAnnotation(Column.class); if (c != null && !"".equals(c.name())) columnNamesByFieldName.put(field.getName(), c.name()); } // get methods (getters and setters) for (Method m : entityClass.getDeclaredMethods()) { String name = m.getName(); boolean getter = false; if (name.length() > 3 && ((getter = name.startsWith("get")) || name.startsWith("set")) && Character.isUpperCase(name.charAt(3))) { name = name.substring(3,4).toLowerCase() + name.substring(4); Transient t = m.getAnnotation(Transient.class); if (t != null) { transientFieldNames.add(name); this.fieldsByName.remove(name); } else if (!transientFieldNames.contains(name) && isAccessible(m)) { if (!this.fieldsByName.containsKey(name)) { this.fieldsByName.put(name, new FieldInfo(name, m.getReturnType(), null, getter ? m : null, getter ? null : m)); } else if (getter) { this.fieldsByName.get(name).getter = m; } else { this.fieldsByName.get(name).setter = m; } } Column c = m.getAnnotation(Column.class); if (c != null && !"".equals(c.name())) columnNamesByFieldName.put(name, c.name()); } } try { constructor = entityClass.getConstructor(EntityManager.class); } catch (Exception e) { // ignore } for (FieldInfo fi : this.fieldsByName.values()) { if (columnNamesByFieldName.get(fi.name) != null) { this.fieldsByLcColumnName.put(columnNamesByFieldName.get(fi.name).toLowerCase(), fi); } else { for (String columnName : nameGuesser.guessColumnName(entityClass, fi.name)) { this.fieldsByLcColumnName.put(columnName.toLowerCase(), fi); } } } Transient t = entityClass.getAnnotation(Transient.class); if (t == null) { String schemaName = "%"; String[] tableNames; Table ta = entityClass.getAnnotation(Table.class); if (ta != null && !"".equals(ta.schema())) { schemaName = ta.schema(); } if (ta != null && !"".equals(ta.name())) { tableNames = new String[] { ta.name() }; } else { tableNames = nameGuesser.guessTableName(entityClass); } DatabaseMetaData metadata = manager.getConnection().getMetaData(); String[] name = getTableName(metadata, schemaName, tableNames); this.catalogName = name[0]; this.schemaName = name[1]; this.tableName = name[2]; if (this.tableName == null) { throw new RuntimeException("Class '" + entityClass.getName() + "': Table '" + Utils.join(tableNames, "'/'") + "' does not exist!"); } ResultSet rs = metadata.getColumns(this.catalogName, this.schemaName, this.tableName, null); int numColumns = rs.getMetaData().getColumnCount(); while (rs.next()) { String columnName = rs.getString(4); int columnType = rs.getInt(5); String nullable = rs.getString(18); String autoIncrement = numColumns >= 23 ? rs.getString(23) : null; ColumnInfo column = new ColumnInfo(columnName, columnType, false, nullable == null || "YES".equals(nullable), "YES".equals(autoIncrement)); this.columns.add(column); this.columnsByLcColumnName.put(columnName.toLowerCase(), column); if (fieldsByLcColumnName.get(columnName.toLowerCase()) == null) { throw new RuntimeException("Class '" + entityClass.getName() + "': No field for column '" + this.tableName + "." + columnName + "'!"); } } rs.close(); rs = metadata.getPrimaryKeys(this.catalogName, this.schemaName, this.tableName); while (rs.next()) { String columnName = rs.getString(4); for (ColumnInfo ci : this.columns) { if (columnName.equals(ci.name)) ci.primaryKey = true; } } rs.close(); } } protected String[] getTableName(DatabaseMetaData metadata, String schemaName, String tableNames[]) throws SQLException { String[] name = null; for (String tableName : tableNames) { ResultSet rs = metadata.getTables(null, schemaName, tableName.toLowerCase(), null); if (rs.next()) name = new String[] { rs.getString(1), rs.getString(2), rs.getString(3) }; rs.close(); if (name != null) break; rs = metadata.getTables(null, schemaName, tableName.toUpperCase(), null); if (rs.next()) name = new String[] { rs.getString(1), rs.getString(2), rs.getString(3) }; rs.close(); if (name != null) break; rs = metadata.getTables(null, schemaName, "%", null); while (rs.next()) { String dbTableName = rs.getString(3); if (tableName.equalsIgnoreCase(dbTableName)) { name = new String[] { rs.getString(1), rs.getString(2), dbTableName }; break; } } rs.close(); } return name; } protected boolean isAccessible(Field field) { if ((field.getModifiers() & Modifier.PUBLIC) > 0) return true; try { field.setAccessible(true); return true; } catch (SecurityException e) { return false; } } protected boolean isAccessible(Method m) { if ((m.getModifiers() & Modifier.PUBLIC) > 0) return true; try { m.setAccessible(true); return true; } catch (SecurityException e) { return false; } } public Class<?> getEntityClass() { return entityClass; } public Constructor<?> getConstructor() { return constructor; } public String getCatalogName() { return catalogName; } public String getSchemaName() { return schemaName; } public String getTableName() { return tableName; } public Collection<FieldInfo> getFields() { return fieldsByName.values(); } public FieldInfo getFieldForColumn(String columnName) { return fieldsByLcColumnName.get(columnName.toLowerCase()); } public FieldInfo getFieldForColumn(ColumnInfo column) { return fieldsByLcColumnName.get(column.name.toLowerCase()); } public List<ColumnInfo> getColumns() { return columns; } public ColumnInfo getColumnForColumn(String columnName) { return columnsByLcColumnName.get(columnName.toLowerCase()); } public static class FieldInfo { protected String name; protected Class<?> type; protected Field field; protected Method getter; protected Method setter; protected FieldInfo(String name, Class<?> type, Field field, Method getter, Method setter) { this.name = name; this.type = type; this.field = field; this.getter = getter; this.setter = setter; } public String getName() { return name; } public Class<?> getType() { return type; } public Field getField() { return field; } public Method getGetter() { return getter; } public Method getSetter() { return setter; } } public static class ColumnInfo { protected String name; protected int type; protected boolean primaryKey; protected boolean nullable; protected boolean autoIncrement; protected ColumnInfo(String name, int type, boolean primaryKey, boolean nullable, boolean autoIncrement) { this.name = name; this.type = type; this.nullable = nullable; this.autoIncrement = autoIncrement; } public String getName() { return name; } public int getType() { return type; } public boolean isPrimaryKey() { return primaryKey; } public boolean isAutoIncrement() { return autoIncrement; } public boolean isNullable() { return nullable; } } }