This commit is contained in:
Miguel Ferreira 2023-05-11 12:15:13 +01:00
commit e561389192
20 changed files with 1587 additions and 0 deletions

16
.gitignore vendored Normal file
View file

@ -0,0 +1,16 @@
# NetBeans specific #
nbproject/private/
build/
nbbuild/
dist/
nbdist/
nbactions.xml
nb-configuration.xml
# Class Files #
*.class
# Package Files #
*.jar
*.war
*.ear

13
pom.xml Executable file
View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.sf</groupId>
<artifactId>jeasyorm</artifactId>
<version>0.9.1</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
</project>

View file

@ -0,0 +1,217 @@
package net.sf.jeasyorm;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import net.sf.jeasyorm.Mapping.FieldInfo;
public abstract class AbstractEntityManager extends EntityManager {
private SqlTranslator sqlTranslator;
protected AbstractEntityManager(Connection connection) {
super(connection);
}
public void setSqlTranslator(SqlTranslator sqlTranslator) {
this.sqlTranslator = sqlTranslator;
}
protected PreparedStatement prepareStatement(String sql, String[] columnNames) {
try {
String finalSql = sqlTranslator != null ? sqlTranslator.getTranslatedSql(sql) : sql;
if (columnNames == null || columnNames.length <= 0) {
return getConnection().prepareStatement(finalSql);
} else {
return getConnection().prepareStatement(finalSql, columnNames);
}
} catch (SQLException e) {
throw new RuntimeSQLException("Error preparing statement [" + sql + "]", e);
}
}
protected void close(ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (Exception e) {
/* ignore */ }
}
}
protected void close(PreparedStatement stmt) {
if (stmt != null) {
try {
stmt.close();
} catch (Exception e) {
/* ignore */ }
}
}
protected Object getObject(ResultSet rs, Mapping mapping) {
Object o;
try {
o = mapping.getConstructor() != null ? mapping.getConstructor().newInstance(this) :
mapping.getEntityClass().newInstance();
} catch (Exception e) {
throw new RuntimeSQLException("Can't create object of type '" + mapping.getEntityClass().getName() + "'", e);
}
try {
ResultSetMetaData metadata = rs.getMetaData();
for (int index=1; index<=metadata.getColumnCount(); index++) {
int sqlType = metadata.getColumnType(index);
String columnName = metadata.getColumnName(index);
FieldInfo fi = mapping.getFieldForColumn(columnName);
if (fi == null) {
columnName = metadata.getColumnLabel(index);
fi = mapping.getFieldForColumn(columnName);
}
if (fi == null) {
if (columnName.contains("__")) continue; // ignore
throw new RuntimeSQLException("Can't find field for column '" + columnName + "'");
}
try {
Object v = getValue(rs, index, fi.getType(), sqlType);
set(o, fi, v);
} catch (RuntimeSQLException e) {
throw new RuntimeSQLException("Can't read column '" + columnName + "'", e);
}
}
} catch (SQLException e) {
throw new RuntimeSQLException(e);
}
return o;
}
protected Object get(Object o, FieldInfo fi) {
try {
if (fi.getGetter() != null) {
return fi.getGetter().invoke(o);
} else if (fi.getField() != null) {
return fi.getField().get(o);
} else {
throw new RuntimeSQLException("Neither field nor getter found for field '" + fi.getName() + "'");
}
} catch (IllegalArgumentException e) {
throw new RuntimeSQLException("Error getting field '" + fi.getName() + "'");
} catch (IllegalAccessException e) {
throw new RuntimeSQLException("Error getting field '" + fi.getName() + "'");
} catch (InvocationTargetException e) {
throw new RuntimeSQLException("Error getting field '" + fi.getName() + "'");
}
}
protected void set(Object o, FieldInfo fi, Object v) {
try {
if (fi.getSetter() != null) {
fi.getSetter().invoke(o, v);
} else if (fi.getField() != null) {
fi.getField().set(o, v);
} else {
throw new RuntimeSQLException("Neither field nor setter found for field '" + fi.getName() + "'");
}
} catch (IllegalArgumentException e) {
throw new RuntimeSQLException("Error setting field '" + fi.getName() + "'");
} catch (IllegalAccessException e) {
throw new RuntimeSQLException("Error setting field '" + fi.getName() + "'");
} catch (InvocationTargetException e) {
throw new RuntimeSQLException("Error setting field '" + fi.getName() + "'");
}
}
protected void setParameter(PreparedStatement stmt, int index, Object value, int sqlType) {
try {
if (value == null) {
stmt.setNull(index, sqlType);
} else if (value instanceof Boolean) {
stmt.setBoolean(index, (Boolean) value);
} else if (value instanceof Byte) {
stmt.setByte(index, (Byte) value);
} else if (value instanceof Short) {
stmt.setShort(index, (Short) value);
} else if (value instanceof Integer) {
stmt.setInt(index, (Integer) value);
} else if (value instanceof Long) {
stmt.setLong(index, (Long) value);
} else if (value instanceof Float) {
stmt.setFloat(index, (Float) value);
} else if (value instanceof Double) {
stmt.setDouble(index, (Double) value);
} else if (value instanceof byte[]) {
stmt.setBytes(index, (byte[]) value);
} else if (value instanceof char[]) {
stmt.setString(index, new String((char[]) value));
} else if (value instanceof String) {
stmt.setString(index, (String) value);
} else if (value instanceof BigDecimal) {
stmt.setBigDecimal(index, (BigDecimal) value);
} else if (value instanceof java.util.Date) {
stmt.setTimestamp(index, new java.sql.Timestamp(((java.util.Date) value).getTime()));
} else if (value instanceof java.sql.Date) {
stmt.setDate(index, (java.sql.Date) value);
} else if (value instanceof java.sql.Time) {
stmt.setTime(index, (java.sql.Time) value);
} else if (value instanceof java.sql.Timestamp) {
stmt.setTimestamp(index, (java.sql.Timestamp) value);
} else if (value instanceof java.sql.Clob) {
stmt.setClob(index, (java.sql.Clob) value);
} else if (value instanceof java.sql.Blob) {
stmt.setBlob(index, (java.sql.Blob) value);
} else {
throw new RuntimeSQLException("Unsupported type '" + value.getClass().getName() + "'");
}
} catch (SQLException e) {
throw new RuntimeSQLException(e);
}
}
protected Object getValue(ResultSet rs, int index, Class<?> type, int sqlType) {
try {
if (type == Boolean.class) {
return rs.getBoolean(index);
} else if (type == Byte.class) {
return rs.getByte(index);
} else if (type == Short.class) {
return rs.getShort(index);
} else if (type == Integer.class) {
return rs.getInt(index);
} else if (type == Long.class) {
return rs.getLong(index);
} else if (type == Float.class) {
return rs.getFloat(index);
} else if (type == Double.class) {
return rs.getDouble(index);
} else if (type == byte[].class) {
return rs.getBytes(index);
} else if (type == char[].class) {
return rs.getString(index).toCharArray();
} else if (type == String.class) {
return rs.getString(index);
} else if (type == BigDecimal.class) {
return rs.getBigDecimal(index);
} else if (type == java.util.Date.class) {
return new java.util.Date(rs.getTimestamp(index).getTime());
} else if (type == java.sql.Date.class) {
return rs.getDate(index);
} else if (type == java.sql.Time.class) {
return rs.getTime(index);
} else if (type == java.sql.Timestamp.class) {
return rs.getTimestamp(index);
} else if (type == java.sql.Clob.class) {
return rs.getClob(index);
} else if (type == java.sql.Blob.class) {
return rs.getBlob(index);
} else {
throw new RuntimeSQLException("Unsupported type '" + type.getName() + "'");
}
} catch (SQLException e) {
throw new RuntimeSQLException(e);
}
}
}

View file

@ -0,0 +1,456 @@
package net.sf.jeasyorm;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import net.sf.jeasyorm.Mapping.ColumnInfo;
import net.sf.jeasyorm.Mapping.FieldInfo;
public class BasicEntityManager extends AbstractEntityManager {
public BasicEntityManager(Connection connection) {
super(connection);
}
/**
*
* @param entityClass
* @return
*/
@Override
protected SqlInfo newSqlInfo(Class<?> entityClass) {
Mapping mapping = Mapping.getMapping(this, entityClass);
SqlInfo info = new SqlInfo(mapping);
StringBuffer selectColumns = new StringBuffer();
StringBuffer insertColumns = new StringBuffer();
StringBuffer insertPlaceholders = new StringBuffer();
StringBuffer updateSet = new StringBuffer();
StringBuffer where = new StringBuffer();
for (ColumnInfo ci : mapping.getColumns()) {
if (selectColumns.length() > 0) {
selectColumns.append(", ");
}
selectColumns.append(ci.getName());
if (!ci.isAutoIncrement()) {
if (insertColumns.length() > 0) {
insertColumns.append(", ");
}
insertColumns.append(ci.getName());
if (insertPlaceholders.length() > 0) {
insertPlaceholders.append(", ");
}
insertPlaceholders.append("?");
}
if (ci.isPrimaryKey()) {
if (where.length() > 0) {
where.append(" and ");
}
where.append(ci.getName());
where.append(" = ?");
} else {
if (updateSet.length() > 0) {
updateSet.append(", ");
}
updateSet.append(ci.getName());
updateSet.append(" = ?");
}
}
String tableName = (mapping.getSchemaName() != null ? mapping.getSchemaName() + "." : "")
+ mapping.getTableName();
info.loadSql = "select " + selectColumns + " from " + tableName + " where " + where;
info.selectSql = "select " + selectColumns + " from " + tableName;
info.insertSql = "insert into " + tableName + " (" + insertColumns + ") values (" + insertPlaceholders + ")";
info.updateSql = "update " + tableName + " set " + updateSet + " where " + where;
info.deleteSql = "delete from " + tableName + " where " + where;
return info;
}
@SuppressWarnings("unchecked")
@Override
public <T> T load(Class<T> entityClass, Object... pk) {
SqlInfo info = getSqlInfo(this, entityClass);
PreparedStatement stmt = null;
try {
stmt = prepareStatement(info.loadSql, null);
int i = 0;
for (ColumnInfo ci : info.mapping.getColumns()) {
if (ci.isPrimaryKey()) {
setParameter(stmt, i + 1, pk[i], ci.getType());
i++;
}
}
ResultSet rs = null;
try {
rs = stmt.executeQuery();
T entity = null;
if (rs.next()) {
entity = (T) getObject(rs, info.mapping);
}
if (rs.next()) {
throw new RuntimeSQLException("Multiple rows returned for statement [" + info.loadSql + "]");
}
return entity;
} catch (RuntimeSQLException se) {
throw se;
} catch (Exception e) {
throw new RuntimeSQLException("Error executing statement [" + info.loadSql + "]", e);
} finally {
close(rs);
}
} finally {
close(stmt);
}
}
protected String getSql(SqlInfo info, String query) {
String lcQuery = query.trim().toLowerCase();
if (lcQuery.startsWith("from")) {
int pos = info.selectSql.indexOf(" from ");
return info.selectSql.substring(0, pos + 1) + query;
} else if (lcQuery.startsWith("where") || lcQuery.startsWith("order")) {
return info.selectSql + " " + query;
} else {
return query;
}
}
@SuppressWarnings("unchecked")
public <T> T findUnique(Class<T> entityClass, String query, Object... params) {
boolean isNative = Utils.isNativeType(entityClass);
SqlInfo info = isNative ? null : getSqlInfo(this, entityClass);
String sql = isNative ? query : getSql(info, query);
PreparedStatement stmt = null;
try {
stmt = prepareStatement(sql, null);
for (int i = 0; i < params.length; i++) {
setParameter(stmt, i + 1, params[i], Types.VARCHAR);
}
ResultSet rs = null;
try {
rs = stmt.executeQuery();
T entity = null;
if (rs.next()) {
entity = !isNative ? (T) getObject(rs, info.mapping) : (T) getValue(rs, 1, entityClass, Types.VARCHAR);
}
if (rs.next()) {
throw new RuntimeSQLException("Multiple rows returned for statement [" + sql + "]");
}
return entity;
} catch (RuntimeSQLException se) {
throw se;
} catch (Exception e) {
throw new RuntimeSQLException("Error executing statement [" + sql + "]", e);
} finally {
close(rs);
}
} finally {
close(stmt);
}
}
@SuppressWarnings("unchecked")
public <T> List<T> find(Class<T> entityClass, String query, Object... params) {
boolean isNative = Utils.isNativeType(entityClass);
SqlInfo info = isNative ? null : getSqlInfo(this, entityClass);
String sql = isNative ? query : getSql(info, query);
List<T> entities = new ArrayList<T>();
PreparedStatement stmt = null;
try {
stmt = prepareStatement(sql, null);
for (int i = 0; i < params.length; i++) {
setParameter(stmt, i + 1, params[i], Types.VARCHAR);
}
ResultSet rs = null;
try {
rs = stmt.executeQuery();
while (rs.next()) {
T entity = !isNative ? (T) getObject(rs, info.mapping) : (T) getValue(rs, 1, entityClass, Types.VARCHAR);
entities.add(entity);
}
return entities;
} catch (RuntimeSQLException se) {
throw se;
} catch (Exception e) {
throw new RuntimeSQLException("Error executing statement [" + sql + "]", e);
} finally {
close(rs);
}
} finally {
close(stmt);
}
}
public int count(String sql, Object... params) {
PreparedStatement stmt = null;
try {
int pos1 = sql.indexOf(" from ");
int pos2 = sql.lastIndexOf(" order by ");
stmt = prepareStatement("select count(*)" + sql.substring(pos1, pos2 > pos1 ? pos2 : sql.length()), null);
for (int i = 0; i < params.length; i++) {
setParameter(stmt, i + 1, params[i], Types.VARCHAR);
}
ResultSet rs = null;
try {
rs = stmt.executeQuery();
if (rs.next()) {
return rs.getInt(1);
}
} catch (RuntimeSQLException se) {
throw se;
} catch (Exception e) {
throw new RuntimeSQLException("Error executing statement [" + sql + "]", e);
} finally {
close(rs);
}
} finally {
close(stmt);
}
return 0;
}
@SuppressWarnings("unchecked")
public <T> Page<T> find(Class<T> entityClass, int pageNum, int pageSize, String query, Object... params) {
boolean isNative = Utils.isNativeType(entityClass);
SqlInfo info = isNative ? null : getSqlInfo(this, entityClass);
String sql = isNative ? query : getSql(info, query);
List<T> entities = new ArrayList<T>();
PreparedStatement stmt = null;
try {
stmt = prepareStatement(sql + " limit " + pageSize + " offset " + (pageNum * pageSize), null);
for (int i = 0; i < params.length; i++) {
setParameter(stmt, i + 1, params[i], Types.VARCHAR);
}
ResultSet rs = null;
try {
rs = stmt.executeQuery();
while (rs.next()) {
T entity = !isNative ? (T) getObject(rs, info.mapping) : (T) getValue(rs, 1, entityClass, Types.VARCHAR);
entities.add(entity);
}
int totalSize = (pageNum * pageSize) + entities.size();
if (entities.size() >= pageSize) {
totalSize = Math.max(totalSize, count(sql, params));
}
return new Page<T>(entities, pageNum, pageSize, totalSize);
} catch (RuntimeSQLException se) {
throw se;
} catch (Exception e) {
throw new RuntimeSQLException("Error executing statement [" + sql + "]", e);
} finally {
close(rs);
}
} finally {
close(stmt);
}
}
public <T> Iterator<T> iterator(Class<T> entityClass, String query, Object... params) {
SqlInfo info = getSqlInfo(this, entityClass);
String sql = getSql(info, query);
PreparedStatement stmt = null;
ResultSet rs = null;
try {
stmt = prepareStatement(sql, null);
for (int i = 0; i < params.length; i++) {
setParameter(stmt, i + 1, params[i], Types.VARCHAR);
}
rs = stmt.executeQuery();
return new ResultSetIterator<T>(stmt, rs, entityClass);
} catch (RuntimeSQLException se) {
close(rs);
close(stmt);
throw se;
} catch (Exception e) {
close(rs);
close(stmt);
throw new RuntimeSQLException("Error executing statement [" + sql + "]", e);
}
}
public int execute(String sql, Object... params) {
PreparedStatement stmt = null;
try {
stmt = prepareStatement(sql, null);
for (int i = 0; i < params.length; i++) {
setParameter(stmt, i + 1, params[i], Types.VARCHAR);
}
return stmt.executeUpdate();
} catch (Exception e) {
throw new RuntimeSQLException("Error executing statement [" + sql + "]", e);
} finally {
close(stmt);
}
}
public <T> T insert(T entity) {
SqlInfo info = getSqlInfo(this, entity.getClass());
PreparedStatement stmt = null;
try {
List<String> columnNames = new ArrayList<String>();
for (ColumnInfo ci : info.mapping.getColumns()) {
if (ci.isAutoIncrement()) {
columnNames.add(ci.getName());
}
}
stmt = prepareStatement(info.insertSql, columnNames.toArray(new String[columnNames.size()]));
int i = 0;
for (ColumnInfo ci : info.mapping.getColumns()) {
if (!ci.isAutoIncrement()) {
FieldInfo fi = info.mapping.getFieldForColumn(ci);
Object value = get(entity, fi);
if (ci.isPrimaryKey() && value == null) {
value = getPrimaryKey(info.mapping, ci);
set(entity, fi, Utils.convertTo(value, fi.getType()));
}
setParameter(stmt, i + 1, value, ci.getType());
i++;
}
}
int num = stmt.executeUpdate();
if (num < 1) {
throw new RuntimeSQLException("Error inserting entity");
}
if (columnNames.size() > 0) {
ResultSet rs = null;
try {
rs = stmt.getGeneratedKeys();
for (String columnName : columnNames) {
FieldInfo fi = info.mapping.getFieldForColumn(columnName);
ColumnInfo ci = info.mapping.getColumnForColumn(columnName);
rs.next();
set(entity, fi, getValue(rs, 1, fi.getType(), ci.getType()));
}
} catch (SQLException e) {
throw new RuntimeSQLException("Error updating generated keys", e);
} finally {
close(rs);
}
}
} catch (SQLException e) {
throw new RuntimeSQLException("Error inserting entity", e);
} finally {
close(stmt);
}
return null;
}
protected Object getPrimaryKey(Mapping mapping, ColumnInfo ci) {
return null;
}
public <T> void update(T entity) {
SqlInfo info = getSqlInfo(this, entity.getClass());
PreparedStatement stmt = null;
try {
stmt = prepareStatement(info.updateSql, null);
int i = 0;
for (ColumnInfo ci : info.mapping.getColumns()) {
if (!ci.isPrimaryKey()) {
FieldInfo fi = info.mapping.getFieldForColumn(ci);
Object value = get(entity, fi);
setParameter(stmt, i + 1, value, ci.getType());
i++;
}
}
for (ColumnInfo ci : info.mapping.getColumns()) {
if (ci.isPrimaryKey()) {
FieldInfo fi = info.mapping.getFieldForColumn(ci);
Object value = get(entity, fi);
setParameter(stmt, i + 1, value, ci.getType());
i++;
}
}
int num = stmt.executeUpdate();
if (num < 1) {
throw new RuntimeSQLException("Error updating entity");
}
} catch (SQLException e) {
throw new RuntimeSQLException("Error updating entity", e);
} finally {
close(stmt);
}
}
public <T> void delete(T entity) {
SqlInfo info = getSqlInfo(this, entity.getClass());
PreparedStatement stmt = null;
try {
stmt = prepareStatement(info.deleteSql, null);
int i = 0;
for (ColumnInfo ci : info.mapping.getColumns()) {
FieldInfo fi = info.mapping.getFieldForColumn(ci);
if (ci.isPrimaryKey()) {
Object value = get(entity, fi);
setParameter(stmt, i + 1, value, ci.getType());
i++;
}
}
int num = stmt.executeUpdate();
if (num < 1) {
throw new RuntimeSQLException("Error deleting entity");
}
} catch (SQLException e) {
throw new RuntimeSQLException("Error deleting entity", e);
} finally {
close(stmt);
}
}
public class ResultSetIterator<T> implements Iterator<T> {
private PreparedStatement stmt;
private ResultSet rs;
boolean isNative;
private Class<T> entityClass;
private Mapping mapping;
boolean hasNext;
protected ResultSetIterator(PreparedStatement stmt, ResultSet rs, Class<T> entityClass) {
this.stmt = stmt;
this.rs = rs;
this.isNative = Utils.isNativeType(entityClass);
this.entityClass = entityClass;
this.mapping = isNative ? null : Mapping.getMapping(BasicEntityManager.this, entityClass);
try {
this.hasNext = rs.next();
} catch (SQLException e) {
this.hasNext = false;
}
}
@Override
public boolean hasNext() {
return hasNext;
}
@Override
@SuppressWarnings("unchecked")
public T next() {
T entity = !isNative ? (T) getObject(rs, mapping) : (T) getValue(rs, 1, entityClass, Types.VARCHAR);
try {
this.hasNext = rs.next();
} catch (SQLException e) {
this.hasNext = false;
}
return entity;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
public void close() {
BasicEntityManager.this.close(rs);
BasicEntityManager.this.close(stmt);
}
}
}

View file

@ -0,0 +1,5 @@
package net.sf.jeasyorm;
public class Configuration {
}

View file

@ -0,0 +1,65 @@
package net.sf.jeasyorm;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
public class DerbyEntityManager extends BasicEntityManager {
private boolean pagingSupported = false;
public DerbyEntityManager(Connection connection) throws SQLException {
super(connection);
DatabaseMetaData metadata = connection.getMetaData();
String prodName = metadata.getDatabaseProductName();
if (prodName.indexOf("Derby") < 0) throw new RuntimeException("Database not supported");
int majorVersion = metadata.getDatabaseMajorVersion();
int minorVersion = metadata.getDatabaseMinorVersion();
if (majorVersion > 10 || (majorVersion == 10 && minorVersion >= 5)) pagingSupported = true;
}
@SuppressWarnings("unchecked")
public <T> Page<T> find(Class<T> entityClass, int pageNum, int pageSize, String query, Object... params) {
if (!pagingSupported) throw new UnsupportedOperationException();
boolean isNative = Utils.isNativeType(entityClass);
SqlInfo info = isNative ? null : getSqlInfo(this, entityClass);
String sql = isNative ? query : getSql(info, query);
List<T> entities = new ArrayList<T>();
PreparedStatement stmt = null;
try {
String pagingSql = sql + " offset " + (pageNum*pageSize) + " rows fetch next " + pageSize + " rows only";
stmt = prepareStatement(pagingSql, null);
for (int i=0; i<params.length; i++) {
setParameter(stmt, i+1, params[i], Types.VARCHAR);
}
ResultSet rs = null;
try {
rs = stmt.executeQuery();
while (rs.next()) {
T entity = !isNative ? (T) getObject(rs, info.mapping) : (T) getValue(rs, 1, entityClass, Types.VARCHAR);
entities.add(entity);
}
int totalSize = (pageNum*pageSize) + entities.size();
if (entities.size() >= pageSize) {
totalSize = Math.max(totalSize, count(sql, params));
}
return new Page<T>(entities, pageNum, pageSize, totalSize);
} catch (RuntimeSQLException se) {
throw se;
} catch (Exception e) {
throw new RuntimeSQLException("Error executing statement [" + sql + "]", e);
} finally {
close(rs);
}
} finally {
close(stmt);
}
}
}

View file

@ -0,0 +1,144 @@
package net.sf.jeasyorm;
import java.lang.reflect.Constructor;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
public abstract class EntityManager {
private static final Logger log = Logger.getLogger(EntityManager.class.getName());
private static Map<String, SqlInfo> infosByClassName = new HashMap<String, SqlInfo>();
private static List<Class<? extends EntityManager>> entityManagerClasses =
new ArrayList<Class<? extends EntityManager>>();
static {
register(BasicEntityManager.class);
register(HSQLDBEntityManager.class);
register(OracleEntityManager.class);
register(PostgreSQLEntityManager.class);
register(DerbyEntityManager.class);
// MySQL (5.1) supported by BasicEntityManager
// H2 supported by BasicEntityManager
}
private String cache;
private Connection connection;
private NameGuesser nameGuesser;
public static EntityManager getInstance(Connection connection) {
return getInstance(null, connection);
}
public static EntityManager getInstance(String cache, Connection connection) {
for (Class<? extends EntityManager> emClass : entityManagerClasses) {
try {
Constructor<? extends EntityManager> c = emClass.getConstructor(Connection.class);
EntityManager em = c.newInstance(connection);
em.setCache(cache);
return em;
} catch (Exception e) {
// ignore
}
}
return null;
}
public static void clear() {
infosByClassName.clear();
}
@SuppressWarnings("unchecked")
public static void register(String entityManagerClassName) {
try {
entityManagerClasses.add(0, (Class<? extends EntityManager>) Class.forName(entityManagerClassName));
} catch (Exception e) {
throw new RuntimeException("Class '" + entityManagerClassName + "' not found!");
}
}
public static void register(Class<? extends EntityManager> entityManagerClass) {
entityManagerClasses.add(0, entityManagerClass);
}
protected static SqlInfo getSqlInfo(EntityManager manager, Class<?> entityClass) {
String key = (manager.getCache() != null ? manager.getCache() + ":" : "") + entityClass.getName();
SqlInfo info = infosByClassName.get(key);
if (info == null && !infosByClassName.containsKey(key)) {
info = manager.newSqlInfo(entityClass);
infosByClassName.put(key, info);
}
return info;
}
/**
* Constructors of child classes should throw an exception if they do not
* support the database type of the connection.
* (e.g. by checking connection.getMetaData().getDatabaseProductName())
*/
protected EntityManager(Connection connection) {
this.connection = connection;
}
public Connection getConnection() { return connection; }
public String getCache() { return cache; }
public void setCache(String cache) { this.cache = cache; }
public NameGuesser getNameGuesser() {
if (nameGuesser == null) nameGuesser = new SimpleNameGuesser();
return nameGuesser;
}
public void setNameGuesser(NameGuesser nameGuesser) { this.nameGuesser = nameGuesser; }
protected abstract SqlInfo newSqlInfo(Class<?> entityClass);
protected Mapping newMapping(Class<?> entityClass) throws SQLException {
return new Mapping(this, entityClass);
}
public abstract <T> T load(Class<T> entityClass, Object... pk);
public abstract <T> T findUnique(Class<T> entityClass, String query, Object... params);
public abstract <T> List<T> find(Class<T> entityClass, String query, Object... params);
public abstract <T> Page<T> find(Class<T> entityClass, int pageNum, int pageSize, String query, Object... params);
public abstract <T> Iterator<T> iterator(Class<T> entityClass, String query, Object... params);
public abstract int count(String query, Object... params);
public abstract <T> T insert(T entity);
public abstract <T> void update(T entity);
public abstract <T> void delete(T entity);
protected void log(Level level, String message, Object... params) {
log.log(level, message, params);
}
protected void log(Level level, String message, Throwable t) {
log.log(level, message, t);
}
protected static class SqlInfo {
protected Mapping mapping;
protected String loadSql;
protected String selectSql;
protected String insertSql;
protected String updateSql;
protected String deleteSql;
protected SqlInfo(Mapping mapping) {
this.mapping = mapping;
}
}
}

View file

@ -0,0 +1,37 @@
package net.sf.jeasyorm;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import net.sf.jeasyorm.Mapping.ColumnInfo;
public class HSQLDBEntityManager extends BasicEntityManager {
public HSQLDBEntityManager(Connection connection) throws SQLException {
super(connection);
String prodName = connection.getMetaData().getDatabaseProductName();
if (prodName.indexOf("HSQL") < 0) throw new RuntimeException("Database not supported");
}
protected Object getPrimaryKey(Mapping mapping, ColumnInfo ci) {
PreparedStatement stmt = null;
ResultSet rs = null;
try {
stmt = prepareStatement("call next value for JEASYORM_SEQUENCE", null);
rs = stmt.executeQuery();
if (rs.next()) {
return rs.getLong(1);
}
} catch (SQLException e) {
throw new RuntimeSQLException(e);
} finally {
close(rs);
close(stmt);
}
return null;
}
}

View file

@ -0,0 +1,271 @@
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; }
}
}

View file

@ -0,0 +1,9 @@
package net.sf.jeasyorm;
public interface NameGuesser {
public String[] guessTableName(Class<?> entityClass);
public String[] guessColumnName(Class<?> entityClass, String field);
}

View file

@ -0,0 +1,77 @@
package net.sf.jeasyorm;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
import net.sf.jeasyorm.Mapping.ColumnInfo;
public class OracleEntityManager extends BasicEntityManager {
public OracleEntityManager(Connection connection) throws SQLException {
super(connection);
String prodName = connection.getMetaData().getDatabaseProductName();
if (prodName.indexOf("Oracle") < 0) throw new RuntimeException("Database not supported");
}
protected Object getPrimaryKey(Mapping mapping, ColumnInfo ci) {
PreparedStatement stmt = null;
ResultSet rs = null;
try {
stmt = prepareStatement("select jeasyorm_sequence.nextval from dual", null);
rs = stmt.executeQuery();
if (rs.next()) {
return rs.getLong(1);
}
} catch (SQLException e) {
throw new RuntimeSQLException(e);
} finally {
close(rs);
close(stmt);
}
return null;
}
@SuppressWarnings("unchecked")
public <T> Page<T> find(Class<T> entityClass, int pageNum, int pageSize, String query, Object... params) {
boolean isNative = Utils.isNativeType(entityClass);
SqlInfo info = isNative ? null : getSqlInfo(this, entityClass);
String sql = isNative ? query : getSql(info, query);
List<T> entities = new ArrayList<T>();
PreparedStatement stmt = null;
try {
String pagingSql = "select * from (select t__.*, rownum as rownum__ from (" + sql +
") t__ where rownum <= " + (pageNum*pageSize+pageSize) + ") where rownum__ > " + (pageNum*pageSize);
stmt = prepareStatement(pagingSql, null);
for (int i=0; i<params.length; i++) {
setParameter(stmt, i+1, params[i], Types.VARCHAR);
}
ResultSet rs = null;
try {
rs = stmt.executeQuery();
while (rs.next()) {
T entity = !isNative ? (T) getObject(rs, info.mapping) : (T) getValue(rs, 1, entityClass, Types.VARCHAR);
entities.add(entity);
}
int totalSize = (pageNum*pageSize) + entities.size();
if (entities.size() >= pageSize) {
totalSize = Math.max(totalSize, count(sql, params));
}
return new Page<T>(entities, pageNum, pageSize, totalSize);
} catch (RuntimeSQLException se) {
throw se;
} catch (Exception e) {
throw new RuntimeSQLException("Error executing statement [" + sql + "]", e);
} finally {
close(rs);
}
} finally {
close(stmt);
}
}
}

View file

@ -0,0 +1,25 @@
package net.sf.jeasyorm;
import java.util.ArrayList;
import java.util.Collection;
public class Page<T> extends ArrayList<T> {
private static final long serialVersionUID = 1L;
private int pageNum;
private int pageSize;
private int totalSize;
public Page(Collection<T> elements, int pageNum, int pageSize, int totalSize) {
super(elements);
this.pageNum = pageNum;
this.pageSize = pageSize;
this.totalSize = totalSize;
}
public int pageNumber() { return pageNum; }
public int pageSize() { return pageSize; }
public int totalSize() { return totalSize; }
}

View file

@ -0,0 +1,36 @@
package net.sf.jeasyorm;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import net.sf.jeasyorm.Mapping.ColumnInfo;
public class PostgreSQLEntityManager extends BasicEntityManager {
public PostgreSQLEntityManager(Connection connection) throws SQLException {
super(connection);
String prodName = connection.getMetaData().getDatabaseProductName();
if (prodName.indexOf("PostgreSQL") < 0) throw new RuntimeException("Database not supported");
}
protected Object getPrimaryKey(Mapping mapping, ColumnInfo ci) {
PreparedStatement stmt = null;
ResultSet rs = null;
try {
stmt = prepareStatement("select nextval('jeasyorm_sequence')", null);
rs = stmt.executeQuery();
if (rs.next()) {
return rs.getLong(1);
}
} catch (SQLException e) {
throw new RuntimeSQLException(e);
} finally {
close(rs);
close(stmt);
}
return null;
}
}

View file

@ -0,0 +1,19 @@
package net.sf.jeasyorm;
public final class RuntimeSQLException extends RuntimeException {
private static final long serialVersionUID = 1L;
public RuntimeSQLException(final Throwable cause) {
super(cause);
}
public RuntimeSQLException(final String message) {
super(message);
}
public RuntimeSQLException(final String message, final Throwable cause) {
super(message, cause);
}
}

View file

@ -0,0 +1,40 @@
package net.sf.jeasyorm;
public class SimpleNameGuesser implements NameGuesser {
@Override
public String[] guessTableName(Class<?> entityClass) {
String name1 = entityClass.getSimpleName();
String name2 = name1.replaceAll("([a-z])([A-Z])", "$1_$2");
if (name1.equals(name2)) {
return new String[] {
name1.toUpperCase(),
"T" + name1.toUpperCase(),
};
} else {
return new String[] {
name1.toUpperCase(),
name2.toUpperCase(),
"T" + name1.toUpperCase(),
"T" + name2.toUpperCase()
};
}
}
@Override
public String[] guessColumnName(Class<?> entityClass, String field) {
String name1 = field;
String name2 = name1.replaceAll("([a-z])([A-Z])", "$1_$2");
if (name1.equals(name2)) {
return new String[] {
name1.toUpperCase()
};
} else {
return new String[] {
name1.toUpperCase(),
name2.toUpperCase()
};
}
}
}

View file

@ -0,0 +1,7 @@
package net.sf.jeasyorm;
public interface SqlTranslator {
public String getTranslatedSql(String sql);
}

View file

@ -0,0 +1,109 @@
package net.sf.jeasyorm;
import java.io.BufferedReader;
import java.io.IOException;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.SQLException;
public abstract class Utils {
public static String join(String[] arr, String sep) {
StringBuffer sb = new StringBuffer();
for (String s : arr) {
if (sb.length() > 0) sb.append(sep);
sb.append(s);
}
return sb.toString();
}
/**
* Returns true if the provided class is a type supported natively (as
* opposed to a bean).
*
* @param type {@link java.lang.Class} type to be tested
* @since 1.0
*/
public static boolean isNativeType(final Class<?> type) {
return type == boolean.class || type == Boolean.class ||
type == byte.class || type == Byte.class ||
type == short.class || type == Short.class ||
type == int.class || type == Integer.class ||
type == long.class || type == Long.class ||
type == float.class || type == Float.class ||
type == double.class || type == Double.class ||
type == char.class || type == Character.class ||
type == byte[].class ||
type == char[].class ||
type == String.class ||
type == BigDecimal.class ||
type == java.util.Date.class || type == java.sql.Date.class ||
type == java.sql.Time.class || type == java.sql.Timestamp.class ||
java.sql.Clob.class.isAssignableFrom(type) ||
java.sql.Blob.class.isAssignableFrom(type) ||
type == Object.class;
}
public static Object convertTo(Object value, final Class<?> type) {
if (value == null || value.getClass() == type) {
return value;
} else if (value instanceof Number && Number.class.isAssignableFrom(type)) {
Number n = (Number) value;
if (type == Byte.class) {
return n.byteValue();
} else if (type == Short.class) {
return n.shortValue();
} else if (type == Integer.class) {
return n.intValue();
} else if (type == Long.class) {
return n.longValue();
} else if (type == Float.class) {
return n.floatValue();
} else if (type == Double.class) {
return n.doubleValue();
}
} else if (value instanceof java.util.Date && java.util.Date.class.isAssignableFrom(type)) {
java.util.Date d = (java.util.Date) value;
if (type == java.util.Date.class) {
return new java.util.Date(d.getTime());
} else if (type == java.sql.Date.class) {
return new java.sql.Date(d.getTime());
} else if (type == java.sql.Time.class) {
return new java.sql.Time(d.getTime());
} else if (type == java.sql.Timestamp.class) {
return new java.sql.Timestamp(d.getTime());
}
}
return value;
}
public static int executeScript(Connection connection, BufferedReader reader, boolean ignoreException)
throws IOException, SQLException {
int num = 0;
StringBuffer sb = new StringBuffer();
String line;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (!"".equals(line)) {
if (sb.length() > 0) sb.append(" ");
if (line.endsWith(";")) {
sb.append(line.substring(0, line.length()-1));
if (!ignoreException) {
num += connection.createStatement().execute(sb.toString()) ? 1 : 0;
} else {
try {
num += connection.createStatement().execute(sb.toString()) ? 1 : 0;
} catch (SQLException e) {
e = e;
}
}
sb.delete(0, sb.length());
} else {
sb.append(line);
}
}
}
return num;
}
}

View file

@ -0,0 +1,14 @@
package net.sf.jeasyorm.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.FIELD })
public @interface Column {
String name() default "";
}

View file

@ -0,0 +1,15 @@
package net.sf.jeasyorm.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Table {
String schema() default "";
String name() default "";
}

View file

@ -0,0 +1,12 @@
package net.sf.jeasyorm.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
public @interface Transient {
}