summaryrefslogtreecommitdiff
path: root/sql-persistence
diff options
context:
space:
mode:
authorTrygve Laugstøl <trygvis@inamo.no>2013-08-07 23:53:53 +0200
committerTrygve Laugstøl <trygvis@inamo.no>2013-08-07 23:53:53 +0200
commit26b01b500065634eb3133dc354a0ba71b13bff56 (patch)
tree2fed1b329f421b7da7bf6c223f17fad230d1b5bd /sql-persistence
parentdd150071369e825d4b4a59e15ad3291841c7ba13 (diff)
downloadcontainer-playground-26b01b500065634eb3133dc354a0ba71b13bff56.tar.gz
container-playground-26b01b500065634eb3133dc354a0ba71b13bff56.tar.bz2
container-playground-26b01b500065634eb3133dc354a0ba71b13bff56.tar.xz
container-playground-26b01b500065634eb3133dc354a0ba71b13bff56.zip
wip
o Start of JPA implementation.
Diffstat (limited to 'sql-persistence')
-rw-r--r--sql-persistence/src/main/java/io/trygvis/persistence/sql/AbstractTypedQuery.java12
-rw-r--r--sql-persistence/src/main/java/io/trygvis/persistence/sql/FromResultSet.java8
-rw-r--r--sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlDao.java25
-rw-r--r--sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlEntityDesc.java11
-rw-r--r--sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlEntityManager.java419
-rw-r--r--sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlEntityManagerFactory.java105
-rw-r--r--sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlEntityMeta.java15
-rw-r--r--sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlExecutor.java19
-rw-r--r--sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlQuery.java249
-rw-r--r--sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlUnit.java18
10 files changed, 863 insertions, 18 deletions
diff --git a/sql-persistence/src/main/java/io/trygvis/persistence/sql/AbstractTypedQuery.java b/sql-persistence/src/main/java/io/trygvis/persistence/sql/AbstractTypedQuery.java
index 1cb8405..ab92994 100644
--- a/sql-persistence/src/main/java/io/trygvis/persistence/sql/AbstractTypedQuery.java
+++ b/sql-persistence/src/main/java/io/trygvis/persistence/sql/AbstractTypedQuery.java
@@ -19,20 +19,18 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
-public abstract class AbstractTypedQuery<A> implements TypedQuery<A> {
+public abstract class AbstractTypedQuery<A> implements TypedQuery<A> , FromResultSet<A> {
private final Connection c;
- private final SqlEntityDesc sqlEntity;
+ private final SqlEntityMeta sqlEntity;
private int firstResult;
private int maxResults;
- protected AbstractTypedQuery(Connection c, SqlEntityDesc sqlEntity) {
+ protected AbstractTypedQuery(Connection c, SqlEntityMeta sqlEntity) {
this.c = c;
this.sqlEntity = sqlEntity;
}
- public abstract A fromResultSet(ResultSet rs) throws SQLException;
-
@Override
public TypedQuery<A> setMaxResults(int maxResult) {
this.maxResults = maxResult;
@@ -192,10 +190,10 @@ public abstract class AbstractTypedQuery<A> implements TypedQuery<A> {
public List<A> getResultList(Integer offset, Integer limit) {
String sql = "SELECT " + sqlEntity.defaultFields + " FROM " + sqlEntity.tableName;
- if(offset != null) {
+ if (offset != null) {
sql += " OFFSET " + offset;
}
- if(limit != null) {
+ if (limit != null) {
sql += " LIMIT " + limit;
}
diff --git a/sql-persistence/src/main/java/io/trygvis/persistence/sql/FromResultSet.java b/sql-persistence/src/main/java/io/trygvis/persistence/sql/FromResultSet.java
new file mode 100644
index 0000000..ec25f50
--- /dev/null
+++ b/sql-persistence/src/main/java/io/trygvis/persistence/sql/FromResultSet.java
@@ -0,0 +1,8 @@
+package io.trygvis.persistence.sql;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+public interface FromResultSet<T> {
+ T fromResultSet(ResultSet rs) throws SQLException;
+}
diff --git a/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlDao.java b/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlDao.java
new file mode 100644
index 0000000..7df3658
--- /dev/null
+++ b/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlDao.java
@@ -0,0 +1,25 @@
+package io.trygvis.persistence.sql;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+public abstract class SqlDao<Id, T> implements FromResultSet<T> {
+
+ protected final Connection c;
+
+ protected SqlDao(Connection c) {
+ this.c = c;
+ }
+
+ public abstract void insert(T o) throws SQLException;
+
+ public abstract void delete(T o) throws SQLException;
+
+ public abstract void deleteById(Id id) throws SQLException;
+
+// public abstract TypedQuery<T> query();
+
+ public abstract T selectById(Id id) throws SQLException;
+
+ public abstract void update(T entity) throws SQLException;
+}
diff --git a/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlEntityDesc.java b/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlEntityDesc.java
deleted file mode 100644
index dbbeed7..0000000
--- a/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlEntityDesc.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package io.trygvis.persistence.sql;
-
-public class SqlEntityDesc {
- public final String tableName;
- public final String defaultFields;
-
- public SqlEntityDesc(String tableName, String defaultFields) {
- this.tableName = tableName;
- this.defaultFields = defaultFields;
- }
-}
diff --git a/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlEntityManager.java b/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlEntityManager.java
new file mode 100644
index 0000000..adcd2e2
--- /dev/null
+++ b/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlEntityManager.java
@@ -0,0 +1,419 @@
+package io.trygvis.persistence.sql;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityTransaction;
+import javax.persistence.FlushModeType;
+import javax.persistence.LockModeType;
+import javax.persistence.PersistenceException;
+import javax.persistence.Query;
+import javax.persistence.RollbackException;
+import javax.persistence.TypedQuery;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.metamodel.Metamodel;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static javax.persistence.FlushModeType.AUTO;
+import static javax.persistence.LockModeType.NONE;
+
+public abstract class SqlEntityManager implements EntityManager {
+
+ private Map<String, Object> properties = new HashMap<>();
+
+ public abstract <Id, T> SqlDao<Id, T> getDao(Class<T> klass);
+
+ // -----------------------------------------------------------------------
+ //
+ // -----------------------------------------------------------------------
+
+ private final SqlEntityManagerFactory factory;
+
+ private final Connection c;
+
+ private boolean autoCommit;
+
+ private final Tx tx = new Tx();
+
+ protected Connection currentConnection() {
+ return c;
+ }
+
+ // -----------------------------------------------------------------------
+ // EntityManager Implementation
+ // -----------------------------------------------------------------------
+
+ protected SqlEntityManager(SqlEntityManagerFactory factory, Connection c) {
+ this.factory = factory;
+ this.c = c;
+
+ try {
+ autoCommit = c.getAutoCommit();
+ } catch (SQLException e) {
+ throw new PersistenceException(e);
+ }
+ }
+
+ @Override
+ public void persist(Object entity) {
+ if (entity == null) {
+ throw new NullPointerException("entity");
+ }
+
+ try {
+ @SuppressWarnings("unchecked")
+ Class<Object> klass = (Class<Object>) entity.getClass();
+ this.<Object, Object>getDao(klass).insert(entity);
+ } catch (SQLException e) {
+ throw new PersistenceException(e);
+ }
+ }
+
+ @Override
+ public <T> T merge(T entity) {
+ if (entity == null) {
+ throw new NullPointerException("entity");
+ }
+
+ try {
+ @SuppressWarnings("unchecked")
+ Class<T> klass = (Class<T>) entity.getClass();
+ SqlDao<Object, T> dao = getDao(klass);
+ T t = dao.selectById(entity);
+ if (t == null) {
+ dao.insert(entity);
+ } else {
+ dao.update(entity);
+ }
+
+ return t;
+ } catch (SQLException e) {
+ throw new PersistenceException(e);
+ }
+ }
+
+ @Override
+ public void remove(Object entity) {
+ if (entity == null) {
+ throw new NullPointerException("entity");
+ }
+
+ try {
+ @SuppressWarnings("unchecked")
+ Class<Object> klass = (Class<Object>) entity.getClass();
+ this.<Object, Object>getDao(klass).delete(entity);
+ } catch (SQLException e) {
+ throw new PersistenceException(e);
+ }
+ }
+
+ @Override
+ public <T> T find(Class<T> entityClass, Object primaryKey) {
+ try {
+ SqlDao<Object, T> dao = getDao(entityClass);
+ return dao.selectById(primaryKey);
+ } catch (SQLException e) {
+ throw new PersistenceException(e);
+ }
+ }
+
+ @Override
+ public <T> T find(Class<T> entityClass, Object primaryKey, Map<String, Object> properties) {
+ return find(entityClass, primaryKey);
+ }
+
+ @Override
+ public <T> T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode) {
+ if (lockMode != NONE) {
+ throw new IllegalArgumentException("Only lockMode = NONE is supported.");
+ }
+ return find(entityClass, primaryKey);
+ }
+
+ @Override
+ public <T> T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode, Map<String, Object> properties) {
+ return find(entityClass, primaryKey, lockMode);
+ }
+
+ @Override
+ public <T> T getReference(Class<T> entityClass, Object primaryKey) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void flush() {
+ }
+
+ @Override
+ public void setFlushMode(FlushModeType flushMode) {
+ if (flushMode != AUTO) {
+ throw new RuntimeException("Flush mode not supported: " + flushMode);
+ }
+ }
+
+ @Override
+ public FlushModeType getFlushMode() {
+ return AUTO;
+ }
+
+ @Override
+ public void lock(Object entity, LockModeType lockMode) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void lock(Object entity, LockModeType lockMode, Map<String, Object> properties) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void refresh(Object entity) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void refresh(Object entity, Map<String, Object> properties) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void refresh(Object entity, LockModeType lockMode) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void refresh(Object entity, LockModeType lockMode, Map<String, Object> properties) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void clear() {
+ }
+
+ @Override
+ public void detach(Object entity) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean contains(Object entity) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public LockModeType getLockMode(Object entity) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setProperty(String propertyName, Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Map<String, Object> getProperties() {
+ return properties;
+ }
+
+ @Override
+ public Query createQuery(String qlString) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> TypedQuery<T> createQuery(CriteriaQuery<T> criteriaQuery) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> TypedQuery<T> createQuery(String qlString, Class<T> resultClass) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Query createNamedQuery(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> TypedQuery<T> createNamedQuery(String name, Class<T> resultClass) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Query createNativeQuery(String sql) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Query createNativeQuery(String sqlString, Class resultClass) {
+ // What happens if the transaction is aborted and this query is executes? Can a Query outlive it's connection?
+ // Or even EntityManager? Should probably store a reference to the current connection and check that the
+ // current one is the same when executing.
+ @SuppressWarnings({"UnnecessaryLocalVariable", "unchecked"})
+ Class<Object> klass = resultClass;
+
+ SqlDao<?, Object> dao = this.<Object, Object>getDao(klass);
+ return new SqlQuery<>(dao, new SqlExecutorDelegate(), sqlString, true);
+ }
+
+ private class SqlExecutorDelegate implements SqlExecutor {
+
+ // For now an EntityManager wraps a Connection
+ private Connection getCurrentOrNewConnection() {
+ return c;
+ }
+
+ @Override
+ public int executeUpdate(UpdateCommand command) {
+ if (!isOpen()) {
+ throw new PersistenceException("This entity manager is closed.");
+ }
+
+ Connection c = getCurrentOrNewConnection();
+
+ try {
+ return command.run(c);
+ } catch (SQLException e) {
+ throw new PersistenceException(e);
+ }
+ }
+
+ @Override
+ public <T> List<T> executeQuery(QueryCommand<T> command) {
+ if (!isOpen()) {
+ throw new PersistenceException("This entity manager is closed.");
+ }
+
+ Connection c = getCurrentOrNewConnection();
+
+ try {
+ return command.run(c);
+ } catch (SQLException e) {
+ throw new PersistenceException(e);
+ }
+ }
+ }
+
+ @Override
+ public Query createNativeQuery(String sqlString, String resultSetMapping) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void joinTransaction() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> T unwrap(Class<T> cls) {
+ if(cls == SqlUnit.class) {
+ return cls.cast(this.factory.sqlUnit);
+ }
+ throw new PersistenceException();
+ }
+
+ @Override
+ public Object getDelegate() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public boolean isOpen() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public EntityTransaction getTransaction() {
+ return tx;
+ }
+
+ @Override
+ public SqlEntityManagerFactory getEntityManagerFactory() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public CriteriaBuilder getCriteriaBuilder() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Metamodel getMetamodel() {
+ throw new UnsupportedOperationException();
+ }
+
+ private class Tx implements EntityTransaction {
+ private boolean active;
+ private boolean rollbackOnly;
+
+ @Override
+ public void begin() {
+ try {
+ if (autoCommit) {
+ c.setAutoCommit(false);
+ autoCommit = false;
+ }
+ active = true;
+ rollbackOnly = false;
+ } catch (SQLException e) {
+ throw new PersistenceException(e);
+ }
+ }
+
+ @Override
+ public void commit() {
+ if (rollbackOnly) {
+ throw new RollbackException("Transaction marked for rollback only.");
+ }
+
+ if (autoCommit) {
+ return;
+ }
+
+ try {
+ c.commit();
+ } catch (SQLException e) {
+ throw new PersistenceException(e);
+ }
+
+ active = true;
+ }
+
+ @Override
+ public void rollback() {
+ active = false;
+ if (autoCommit) {
+ return;
+ }
+
+ try {
+ c.rollback();
+ } catch (SQLException e) {
+ throw new PersistenceException(e);
+ }
+ }
+
+ @Override
+ public void setRollbackOnly() {
+ this.rollbackOnly = active;
+ }
+
+ @Override
+ public boolean getRollbackOnly() {
+ return rollbackOnly;
+ }
+
+ @Override
+ public boolean isActive() {
+ return active;
+ }
+ }
+}
diff --git a/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlEntityManagerFactory.java b/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlEntityManagerFactory.java
new file mode 100644
index 0000000..d48f7e5
--- /dev/null
+++ b/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlEntityManagerFactory.java
@@ -0,0 +1,105 @@
+package io.trygvis.persistence.sql;
+
+import javax.persistence.Cache;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.PersistenceException;
+import javax.persistence.PersistenceUnitUtil;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.metamodel.Metamodel;
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Map;
+
+import static java.util.Collections.emptyMap;
+
+public abstract class SqlEntityManagerFactory implements EntityManagerFactory {
+
+ public final SqlUnit sqlUnit;
+
+ private final DataSource dataSource;
+
+ protected SqlEntityManagerFactory(SqlUnit sqlUnit, DataSource dataSource) {
+ this.sqlUnit = sqlUnit;
+ this.dataSource = dataSource;
+ }
+
+ // -----------------------------------------------------------------------
+ //
+ // -----------------------------------------------------------------------
+
+ protected abstract EntityManager createEntityManager(Connection c);
+
+ // -----------------------------------------------------------------------
+ //
+ // -----------------------------------------------------------------------
+
+ @Override
+ public EntityManager createEntityManager() {
+ return createEntityManager(emptyMap());
+ }
+
+ @Override
+ public EntityManager createEntityManager(Map map) {
+ try {
+ Connection c = dataSource.getConnection();
+ return createEntityManager(c);
+ } catch (SQLException e) {
+ throw new PersistenceException("Could not get connection", e);
+ }
+ }
+
+ @Override
+ public CriteriaBuilder getCriteriaBuilder() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Metamodel getMetamodel() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isOpen() {
+ return true;
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public Map<String, Object> getProperties() {
+ return emptyMap();
+ }
+
+ @Override
+ public Cache getCache() {
+ return new NoOpCache();
+ }
+
+ @Override
+ public PersistenceUnitUtil getPersistenceUnitUtil() {
+ throw new UnsupportedOperationException();
+ }
+
+ private static class NoOpCache implements Cache {
+ @Override
+ public boolean contains(Class cls, Object primaryKey) {
+ return false;
+ }
+
+ @Override
+ public void evict(Class cls, Object primaryKey) {
+ }
+
+ @Override
+ public void evict(Class cls) {
+ }
+
+ @Override
+ public void evictAll() {
+ }
+ }
+}
diff --git a/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlEntityMeta.java b/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlEntityMeta.java
new file mode 100644
index 0000000..c8cbcac
--- /dev/null
+++ b/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlEntityMeta.java
@@ -0,0 +1,15 @@
+package io.trygvis.persistence.sql;
+
+public class SqlEntityMeta {
+ public final String tableName;
+ public final String defaultFields;
+ public final String createTableSql;
+ public final String dropTableSql;
+
+ public SqlEntityMeta(String tableName, String defaultFields, String createTableSql, String dropTableSql) {
+ this.tableName = tableName;
+ this.defaultFields = defaultFields;
+ this.createTableSql = createTableSql;
+ this.dropTableSql = dropTableSql;
+ }
+}
diff --git a/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlExecutor.java b/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlExecutor.java
new file mode 100644
index 0000000..ad6b8af
--- /dev/null
+++ b/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlExecutor.java
@@ -0,0 +1,19 @@
+package io.trygvis.persistence.sql;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.List;
+
+public interface SqlExecutor {
+ int executeUpdate(UpdateCommand command);
+
+ <T> List<T> executeQuery(QueryCommand<T> command);
+
+ static interface UpdateCommand {
+ int run(Connection c) throws SQLException;
+ }
+
+ static interface QueryCommand<T> {
+ List<T> run(Connection c) throws SQLException;
+ }
+}
diff --git a/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlQuery.java b/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlQuery.java
new file mode 100644
index 0000000..b50b56e
--- /dev/null
+++ b/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlQuery.java
@@ -0,0 +1,249 @@
+package io.trygvis.persistence.sql;
+
+import javax.persistence.FlushModeType;
+import javax.persistence.LockModeType;
+import javax.persistence.NoResultException;
+import javax.persistence.NonUniqueResultException;
+import javax.persistence.Parameter;
+import javax.persistence.PersistenceException;
+import javax.persistence.TemporalType;
+import javax.persistence.TypedQuery;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static java.util.Collections.emptyMap;
+
+public class SqlQuery<T> implements TypedQuery<T> {
+ private final FromResultSet<T> fromResultSet;
+ private final SqlExecutor executor;
+ private final String sql;
+
+ private final boolean isSqlFinal;
+ private int maxResults = -1;
+ private int firstResult = -1;
+
+ public SqlQuery(FromResultSet<T> fromResultSet, SqlExecutor executor, String sql, boolean sqlFinal) {
+ this.fromResultSet = fromResultSet;
+ this.executor = executor;
+ this.sql = sql;
+ this.isSqlFinal = sqlFinal;
+ }
+
+ @Override
+ public List<T> getResultList() {
+ return getResultList(firstResult, maxResults);
+ }
+
+ @Override
+ public T getSingleResult() {
+ List<T> list = getResultList(0, 2);
+ if (list.size() == 1) {
+ return list.get(0);
+ }
+
+ if (list.size() == 0) {
+ throw new NoResultException();
+ }
+
+ throw new NonUniqueResultException();
+ }
+
+ public List<T> getResultList(int offset, int limit) {
+ final String sql = generateSql(this.sql, offset, limit);
+ return executor.executeQuery(new SqlExecutor.QueryCommand<T>() {
+ @Override
+ public List<T> run(Connection c) throws SQLException {
+ List<T> list = new ArrayList<>();
+ try (Statement stmt = c.createStatement()) {
+ ResultSet rs = stmt.executeQuery(sql);
+ while (rs.next()) {
+ T t = fromResultSet.fromResultSet(rs);
+ list.add(t);
+ }
+ }
+ return list;
+ }
+ });
+ }
+
+ private static String generateSql(String sql, int offset, int limit) {
+ if (offset > 0) {
+ sql += " OFFSET " + offset;
+ }
+ if (limit > 0) {
+ sql += " LIMIT " + limit;
+ }
+
+ return sql;
+ }
+
+ @Override
+ public int executeUpdate() {
+ return executor.executeUpdate(new SqlExecutor.UpdateCommand() {
+ @Override
+ public int run(Connection c) throws SQLException {
+ try (Statement stmt = c.createStatement()) {
+ return stmt.executeUpdate(sql);
+ }
+ }
+ });
+ }
+
+ @Override
+ public SqlQuery<T> setMaxResults(int maxResult) {
+ if (maxResult <= 0) {
+ throw new IllegalArgumentException("maxResult has to be positive: " + maxResult);
+ }
+ this.maxResults = maxResult;
+ return this;
+ }
+
+ @Override
+ public int getMaxResults() {
+ return maxResults;
+ }
+
+ @Override
+ public SqlQuery<T> setFirstResult(int startPosition) {
+ this.firstResult = startPosition;
+ return this;
+ }
+
+ @Override
+ public int getFirstResult() {
+ return firstResult;
+ }
+
+ @Override
+ public SqlQuery<T> setHint(String hintName, Object value) {
+ return null;
+ }
+
+ @Override
+ public Map<String, Object> getHints() {
+ return emptyMap();
+ }
+
+ @Override
+ public <A> SqlQuery<T> setParameter(Parameter<A> param, A value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public SqlQuery<T> setParameter(Parameter<Calendar> param, Calendar value, TemporalType temporalType) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public SqlQuery<T> setParameter(Parameter<Date> param, Date value, TemporalType temporalType) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public SqlQuery<T> setParameter(String name, Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public SqlQuery<T> setParameter(String name, Calendar value, TemporalType temporalType) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public SqlQuery<T> setParameter(String name, Date value, TemporalType temporalType) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public SqlQuery<T> setParameter(int position, Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public SqlQuery<T> setParameter(int position, Calendar value, TemporalType temporalType) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public SqlQuery<T> setParameter(int position, Date value, TemporalType temporalType) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set<Parameter<?>> getParameters() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Parameter<?> getParameter(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> Parameter<T> getParameter(String name, Class<T> type) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Parameter<?> getParameter(int position) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> Parameter<T> getParameter(int position, Class<T> type) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isBound(Parameter<?> param) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> T getParameterValue(Parameter<T> param) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Object getParameterValue(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Object getParameterValue(int position) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public SqlQuery<T> setFlushMode(FlushModeType flushMode) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public FlushModeType getFlushMode() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public SqlQuery<T> setLockMode(LockModeType lockMode) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public LockModeType getLockMode() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> T unwrap(Class<T> klass) {
+ throw new PersistenceException("Unsupported class: " + klass);
+ }
+}
diff --git a/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlUnit.java b/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlUnit.java
new file mode 100644
index 0000000..2879f5d
--- /dev/null
+++ b/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlUnit.java
@@ -0,0 +1,18 @@
+package io.trygvis.persistence.sql;
+
+import java.util.List;
+
+import static java.util.Arrays.asList;
+
+public class SqlUnit {
+
+ private final List<SqlEntityMeta> entities;
+
+ public SqlUnit(SqlEntityMeta... entities) {
+ this.entities = asList(entities);
+ }
+
+ public List<SqlEntityMeta> getEntities() {
+ return entities;
+ }
+}