diff --git a/jirm-orm/src/main/java/co/jirm/orm/OrmConfig.java b/jirm-orm/src/main/java/co/jirm/orm/OrmConfig.java index 436ad30..5a8db33 100644 --- a/jirm-orm/src/main/java/co/jirm/orm/OrmConfig.java +++ b/jirm-orm/src/main/java/co/jirm/orm/OrmConfig.java @@ -17,21 +17,29 @@ import co.jirm.core.execute.SqlExecutor; import co.jirm.mapper.SqlObjectConfig; +import co.jirm.orm.dao.DaoHooks; import co.jirm.orm.writer.SqlWriterStrategy; +import com.google.common.base.Optional; public class OrmConfig { private final SqlExecutor sqlExecutor; private final SqlObjectConfig sqlObjectConfig; private final SqlWriterStrategy sqlWriterStrategy; - - private OrmConfig(SqlExecutor sqlExecutor, SqlObjectConfig sqlObjectConfig, SqlWriterStrategy sqlWriterStrategy) { + private final Optional daoHooks; + + public OrmConfig(SqlExecutor sqlExecutor, SqlObjectConfig sqlObjectConfig, SqlWriterStrategy sqlWriterStrategy, Optional daoHooks) { super(); this.sqlExecutor = sqlExecutor; this.sqlObjectConfig = sqlObjectConfig; this.sqlWriterStrategy = sqlWriterStrategy; + this.daoHooks = daoHooks; } - + + public OrmConfig(SqlExecutor sqlExecutor, SqlObjectConfig sqlObjectConfig, SqlWriterStrategy sqlWriterStrategy) { + this(sqlExecutor, sqlObjectConfig, sqlWriterStrategy, Optional.absent()); + } + public static OrmConfig newInstance(SqlExecutor sqlExecutor, SqlObjectConfig objectConfig) { return new OrmConfig(sqlExecutor, SqlObjectConfig.DEFAULT, SqlWriterStrategy.newInstance("\n")); } @@ -51,4 +59,8 @@ public SqlObjectConfig getSqlObjectConfig() { public SqlExecutor getSqlExecutor() { return sqlExecutor; } + + public Optional getDaoHooks() { + return daoHooks; + } } diff --git a/jirm-orm/src/main/java/co/jirm/orm/dao/DaoHooks.java b/jirm-orm/src/main/java/co/jirm/orm/dao/DaoHooks.java new file mode 100644 index 0000000..e6c4bbf --- /dev/null +++ b/jirm-orm/src/main/java/co/jirm/orm/dao/DaoHooks.java @@ -0,0 +1,11 @@ +package co.jirm.orm.dao; + +import co.jirm.mapper.definition.*; + +import java.util.*; + +public interface DaoHooks { + public void beforeInsert(SqlObjectDefinition definition, Map values); + + // TODO to add beforeInsertMaps() method and its usage to the JirmDao. +} diff --git a/jirm-orm/src/main/java/co/jirm/orm/dao/JirmDao.java b/jirm-orm/src/main/java/co/jirm/orm/dao/JirmDao.java index 5707f0f..be3485e 100644 --- a/jirm-orm/src/main/java/co/jirm/orm/dao/JirmDao.java +++ b/jirm-orm/src/main/java/co/jirm/orm/dao/JirmDao.java @@ -34,6 +34,7 @@ import co.jirm.mapper.copy.CopyBuilder; import co.jirm.mapper.definition.SqlObjectDefinition; import co.jirm.mapper.definition.SqlParameterDefinition; +import co.jirm.orm.JirmFactory; import co.jirm.orm.OrmConfig; import co.jirm.orm.builder.delete.DeleteBuilderFactory; import co.jirm.orm.builder.delete.DeleteRootClauseBuilder; @@ -61,6 +62,8 @@ public final class JirmDao { private final UpdateBuilderFactory updateBuilderFactory; private final DeleteBuilderFactory deleteBuilderFactory; private final SqlWriterStrategy writerStrategy; + private final Optional daoHooks; + private final Optional jirmFactory; private JirmDao( SqlExecutor sqlExecutor, @@ -69,7 +72,9 @@ private JirmDao( SqlWriterStrategy writerStrategy, SelectBuilderFactory selectBuilderFactory, UpdateBuilderFactory updateBuilderFactory, - DeleteBuilderFactory deleteBuilderFactory) { + DeleteBuilderFactory deleteBuilderFactory, + Optional daoHooks, + Optional jirmFactory) { super(); this.sqlExecutor = sqlExecutor; this.config = config; @@ -78,9 +83,15 @@ private JirmDao( this.selectBuilderFactory = selectBuilderFactory; this.updateBuilderFactory = updateBuilderFactory; this.deleteBuilderFactory = deleteBuilderFactory; + this.daoHooks = daoHooks; + this.jirmFactory = jirmFactory; } - + public static JirmDao newInstance(Class type, OrmConfig config) { + return newInstance(type, config, Optional.absent()); + } + + public static JirmDao newInstance(Class type, OrmConfig config, Optional jirmFactory) { SqlObjectDefinition definition = config.getSqlObjectConfig().resolveObjectDefinition(type); SelectBuilderFactory selectBuilderFactory = SelectBuilderFactory.newInstance(definition, config); UpdateBuilderFactory updateBuilderFactory = UpdateBuilderFactory.newInstance(definition, config); @@ -90,10 +101,12 @@ public static JirmDao newInstance(Class type, OrmConfig config) { config.getSqlExecutor(), config.getSqlObjectConfig(), definition, config.getSqlWriterStrategy(), - selectBuilderFactory, updateBuilderFactory, deleteBuilderFactory); + selectBuilderFactory, updateBuilderFactory, deleteBuilderFactory, + config.getDaoHooks(), + jirmFactory); } - private LinkedHashMap toLinkedHashMap(T t, boolean bulkInsert) { + private LinkedHashMap toLinkedHashMap(T t, boolean bulkInsert, ForeignAct foreignAct) { LinkedHashMap m = config.getObjectMapper().convertObjectToSqlMap(t); /* * Replace the complex objects with there ids. @@ -108,6 +121,8 @@ private LinkedHashMap toLinkedHashMap(T t, boolean bulkInsert) { /* * TODO: We only set it if the object is actually present. ie do you really want to set null? */ + actForeign(pd.getParameterType(), m.get(pd.getParameterName()), foreignAct); + m.put(pd.getParameterName(), idDef.convertToSql(nkv.object)); } else if (bulkInsert) { @@ -139,7 +154,35 @@ else if (bulkInsert) { return m; } - + + private void actForeign(final Class clazz, final Object object, ForeignAct foreignAct) { + if (object != null && jirmFactory.isPresent()) { + @SuppressWarnings("unchecked") + final JirmDao foreignDao = ((JirmDao) jirmFactory.get().daoFor(clazz)); + + foreignAct.act(foreignDao, object); + } + } + + private static interface ForeignAct { + public void act(final JirmDao dao, final T object); + + public static final ForeignAct NO_ACT = new ForeignAct() { + @Override + public void act(final JirmDao dao, final T object) { + } + }; + } + + private static class ForeignInsert implements ForeignAct { + public static final ForeignInsert INSERT = new ForeignInsert(); + + @Override + public void act(final JirmDao dao, final T object) { + dao.insert(object); + } + } + public CopyBuilder copyBuilder() { return CopyBuilder.newInstance(definition.getObjectType(), config.getObjectMapper()); } @@ -181,7 +224,7 @@ public T findById(Object id) { } public void insert(T t) { - LinkedHashMap m = toLinkedHashMap(t, false); + LinkedHashMap m = toLinkedHashMap(t, false, ForeignInsert.INSERT); Iterator> it = m.entrySet().iterator(); /* * Remove the null values that are to be generated. @@ -207,12 +250,14 @@ public int deleteById(Object id) { } public UpdateObjectBuilder update(T t) { - LinkedHashMap m = toLinkedHashMap(t, false); + // todo to probably change the foreign act (probably with the ForeignAct interface). + LinkedHashMap m = toLinkedHashMap(t, false, ForeignAct.NO_ACT); return updateBuilderFactory.update(m); } public T reload(T t) { - LinkedHashMap m = toLinkedHashMap(t, false); + // todo to probably change the foreign act (probably with the ForeignAct interface). + LinkedHashMap m = toLinkedHashMap(t, false, ForeignAct.NO_ACT); Optional id = definition.idParameter(); check.state(id.isPresent(), "No id definition"); Optional o = id.get().valueFrom(m); @@ -220,6 +265,10 @@ public T reload(T t) { } public void insert(Map values) { + if (daoHooks.isPresent()) { + daoHooks.get().beforeInsert(definition, values); + } + StringBuilder qb = new StringBuilder(); writerStrategy.insertStatement(qb, definition, values); sqlExecutor.update(qb.toString(), writerStrategy.fillValues(definition, values).toArray()); @@ -229,7 +278,7 @@ public void insert(Iterator values, int batchSize) { Iterator> t = Iterators.transform(values, new Function>() { @Override public Map apply(T input) { - return toLinkedHashMap(input, true); + return toLinkedHashMap(input, true, ForeignInsert.INSERT); } }); insertMaps(t, batchSize); diff --git a/jirm-orm/src/main/java/co/jirm/orm/writer/SqlWriterStrategy.java b/jirm-orm/src/main/java/co/jirm/orm/writer/SqlWriterStrategy.java index 30b2c15..4cdb101 100644 --- a/jirm-orm/src/main/java/co/jirm/orm/writer/SqlWriterStrategy.java +++ b/jirm-orm/src/main/java/co/jirm/orm/writer/SqlWriterStrategy.java @@ -47,8 +47,12 @@ protected SqlWriterStrategy(Joiner commaJoiner, String clauseSpaceSeparator) { this.clauseSpaceSeparator = clauseSpaceSeparator; } + protected SqlWriterStrategy(String sep) { + this(Joiner.on("," + sep), sep); + } + public static SqlWriterStrategy newInstance(String sep) { - return new SqlWriterStrategy(Joiner.on("," + sep),sep); + return new SqlWriterStrategy(sep); } public StringBuilder insertStatement(StringBuilder qb, final SqlObjectDefinition definition, Map m) { diff --git a/jirm-spring/src/main/java/co/jirm/spring/CachingSpringJirmFactory.java b/jirm-spring/src/main/java/co/jirm/spring/CachingSpringJirmFactory.java new file mode 100644 index 0000000..1e7bfb7 --- /dev/null +++ b/jirm-spring/src/main/java/co/jirm/spring/CachingSpringJirmFactory.java @@ -0,0 +1,36 @@ +package co.jirm.spring; + +import co.jirm.orm.dao.*; + +import javax.sql.*; +import java.util.*; + +/** + * @author Denis Buzdalov + */ +public class CachingSpringJirmFactory extends SpringJirmFactory { + // informal invariant: forall k :: k in daoCache.keys ==> k.erasure == daoCache.get(k).erasure + protected final Map, JirmDao> daoCache = new HashMap, JirmDao>(); + + public CachingSpringJirmFactory(final DataSource dataSource) { + super(dataSource); + } + + public CachingSpringJirmFactory(final DataSource dataSource, final boolean recursive) { + super(dataSource, recursive); + } + + @SuppressWarnings("unchecked") // due to invariant of the daoCache + @Override + public JirmDao daoFor(final Class k) { + final JirmDao existingDao = daoCache.get(k); + if (existingDao == null) { + final JirmDao newDao = super.daoFor(k); + daoCache.put(k, newDao); + + return newDao; + } else { + return (JirmDao) existingDao; + } + } +} diff --git a/jirm-spring/src/main/java/co/jirm/spring/SpringJirmFactory.java b/jirm-spring/src/main/java/co/jirm/spring/SpringJirmFactory.java index 7e7fd03..6519228 100644 --- a/jirm-spring/src/main/java/co/jirm/spring/SpringJirmFactory.java +++ b/jirm-spring/src/main/java/co/jirm/spring/SpringJirmFactory.java @@ -20,6 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; +import com.google.common.base.Optional; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; @@ -35,9 +36,14 @@ public class SpringJirmFactory implements JirmFactory { private final JdbcTemplate jdbcTemplate; private final Supplier sqlExecutorSupplier; private final Supplier ormConfigSupplier; - + private final boolean recursive; + @Autowired public SpringJirmFactory(DataSource dataSource) { + this(dataSource, false); + } + + public SpringJirmFactory(DataSource dataSource, boolean recursive) { jdbcTemplate = new JdbcTemplate(dataSource); ormConfigSupplier = Suppliers.memoize(new Supplier() { @Override @@ -51,10 +57,11 @@ public SqlExecutor get() { return SpringJirmFactory.this.createSqlExecutor(); } }); + this.recursive = recursive; } public JirmDao daoFor(Class k) { - return JirmDao.newInstance(k, ormConfigSupplier.get()); + return JirmDao.newInstance(k, ormConfigSupplier.get(), recursive ? Optional.of(this) : Optional.absent()); } protected OrmConfig createOrmConfig() {