- @IocBy(type=ComboIocLoader.class, args={
- "*js",
- "ioc/dao.js",
- "ioc/service.js",
- "*anno",
- "com.myapp.module",
- "com.myapp.service",
- "*tx",
- "*async" // @Async注解,异步执行.
- })
+ ComboIocLoader loader = new ComboIocLoader("*js",
+ "ioc/dao.js",
+ "ioc/service.js",
+ "*anno",
+ "com.myapp.module",
+ "com.myapp.service",
+ "*tx",
+ // @Async注解,异步执行.
+ "*async");
}}}
如上面的例子,组合加载器,组合了3个 Ioc 加载器,一个是 JsonLoader,一个是
AnnotationIocLoader, 一个是AOP事务的TransIocLoader(1.b.52新增)。
diff --git a/doc/manual/lang/mirror.man b/doc/manual/lang/mirror.man
index 9286f0dc2..3986fca73 100644
--- a/doc/manual/lang/mirror.man
+++ b/doc/manual/lang/mirror.man
@@ -203,3 +203,8 @@
mirror.setValue(obj, "name", "XiaoBai");
}}}
+ 获取静态字段
+ * 取静态属性
+ {{{
+ Field[] fields = mirror.getStaticField(nofinalOrAll);
+ }}}
\ No newline at end of file
diff --git a/src/org/nutz/dao/Chain.java b/src/org/nutz/dao/Chain.java
index c257351f6..2dbf70501 100644
--- a/src/org/nutz/dao/Chain.java
+++ b/src/org/nutz/dao/Chain.java
@@ -272,12 +272,15 @@ public void invoke(MappingField mf, Object val) {
//=============================================================
/**
- * 添加一个特殊节点, 如果value非空,则有3个情况:
+ * 添加一个特殊节点, 如果value非空而且是String类型,则有3个情况:
*
+1 效果如age=age+1
* -1 效果如count=count-1
* 支持的运算符有 + - *\/ % & ^ |
- * 其他值, 则对value.toString(),效果如 time=todate("XXXXX")
- *
+ * 其他值, 则对value.toString()
+ *
+ * Chain chain = Chain.makeSpecial("age", "+1");//输出的SQL会是 age=age+1
+ *
+ * Chain chain = Chain.makeSpecial("ct", "now()");//输出的SQL会是 ct=now(),但不建议用依赖特定数据库的now(),仅供演示.
* @since 1.b.44
*/
public abstract Chain addSpecial(String name, Object value);
diff --git a/src/org/nutz/dao/DaoInterceptor.java b/src/org/nutz/dao/DaoInterceptor.java
new file mode 100644
index 000000000..9f17e77e2
--- /dev/null
+++ b/src/org/nutz/dao/DaoInterceptor.java
@@ -0,0 +1,12 @@
+package org.nutz.dao;
+
+/**
+ * Dao操作拦截器
+ * @author wendal
+ * @see org.nutz.dao.impl.interceptor.DaoLogInterceptor
+ * @see org.nutz.dao.impl.interceptor.DaoTimeInterceptor
+ */
+public interface DaoInterceptor {
+
+ void filter(DaoInterceptorChain chain) throws DaoException;
+}
diff --git a/src/org/nutz/dao/DaoInterceptorChain.java b/src/org/nutz/dao/DaoInterceptorChain.java
new file mode 100644
index 000000000..d3a52ffb2
--- /dev/null
+++ b/src/org/nutz/dao/DaoInterceptorChain.java
@@ -0,0 +1,226 @@
+package org.nutz.dao;
+
+import java.sql.Connection;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.nutz.dao.impl.DaoExecutor;
+import org.nutz.dao.sql.DaoStatement;
+import org.nutz.dao.sql.SqlContext;
+import org.nutz.lang.random.R;
+import org.nutz.log.Log;
+import org.nutz.log.Logs;
+
+/**
+ * Dao执行拦截器链.
+ *
+ * @author wendal
+ * @see org.nutz.dao.impl.interceptor.DaoLogInterceptor
+ * @see org.nutz.dao.impl.interceptor.DaoTimeInterceptor
+ */
+public class DaoInterceptorChain implements ConnCallback {
+
+ private static final Log log = Logs.get();
+
+ protected int autoTransLevel;
+
+ protected Connection connection;
+
+ protected int current = 0;
+
+ protected DaoStatement daoStatement;
+
+ protected DaoExecutor executor;
+
+ protected List interceptors = new ArrayList();
+
+ protected int updateCount;
+
+ protected DaoStatement[] sts;
+
+ protected String id;
+
+ /**
+ * 新建一个DaoInterceptorChain.
+ *
+ * @param sts
+ * 将要进行的Dao操作(不一定是SQL操作,有可能是EL)
+ */
+ public DaoInterceptorChain(DaoStatement... sts) {
+ this.sts = sts;
+ id = R.UU32();
+ }
+
+ /**
+ * 继续下一个拦截器,如果已经是最后一个拦截器,那么执行executor.exec
+ *
+ * @return 本对象,用于链式操作
+ * @throws Exception
+ */
+ public DaoInterceptorChain doChain() throws DaoException {
+ if (hasNext()) {
+ DaoInterceptor interceptor = next();
+ current++;
+ interceptor.filter(this);
+ } else {
+ executor.exec(getConnection(), getDaoStatement());
+ updateCount += getDaoStatement().getUpdateCount();
+ }
+ return this;
+ }
+
+ /**
+ * 获取当前自动事务级别,DaoRunner中使用强制事务时会使用之.拦截器不能修改,即使修改也不会生效
+ *
+ * @return 当前自动(强制)事务级别
+ */
+ public int getAutoTransLevel() {
+ return autoTransLevel;
+ }
+
+ /**
+ * 当前执行的DaoStatement
+ *
+ * @return 当前执行的DaoStatement
+ */
+ public DaoStatement getDaoStatement() {
+ return daoStatement;
+ }
+
+ /**
+ * 全部DaoStatement,可能不止一条
+ *
+ * @return 全部DaoStatement
+ */
+ public DaoStatement[] getDaoStatements() {
+ return sts;
+ }
+
+ /**
+ * 拦截器列表(暂不开放修改)
+ *
+ * @return 全体拦截器列表
+ */
+ public List getInterceptors() {
+ return interceptors;
+ }
+
+ /**
+ * 更新总数,用于DaoSupport(NutDao)获取更新总数.
+ *
+ * @return 更新记录总数
+ */
+ public int getUpdateCount() {
+ return updateCount;
+ }
+
+ /**
+ * 是否还有下一个拦截器
+ *
+ * @return true,如果还有拦截器要执行
+ */
+ public boolean hasNext() {
+ return current < interceptors.size();
+ }
+
+ /**
+ * 这是DaoExecutor会执行的方法,拦截器内不要执行这个方法!! 这里也是拦截器开始生效的地方.
+ */
+ public void invoke(Connection conn) throws Exception {
+ for (DaoStatement st : sts) {
+ if (st == null) {
+ if (log.isInfoEnabled())
+ log.info("Found a null DaoStatement(SQL), ingore it ~~");
+ continue;
+ }
+ daoStatement = st;
+ this.connection = conn;
+ doChain();
+ }
+ }
+
+ /**
+ * 获取下一个拦截器. 调用前必须先调用hasNext进行判断
+ *
+ * @return 下一个拦截器
+ */
+ public DaoInterceptor next() {
+ return interceptors.get(current);
+ }
+
+ /**
+ * 设置强制事务的级别,对拦截器来说无意义.
+ *
+ * @param autoTransLevel
+ * 与DaoSupport(NutDao)内的值一致
+ */
+ public void setAutoTransLevel(int autoTransLevel) {
+ this.autoTransLevel = autoTransLevel;
+ }
+
+ /**
+ * 设置当前拦截器索引. 若设置值大于拦截器列表的大小,那么效果就等同于跳过剩余拦截器,直接执行DaoStatement
+ *
+ * @param current
+ */
+ public void setCurrent(int current) {
+ this.current = current;
+ }
+
+ /**
+ * 设置DaoExecutor. 典型应用是在拦截器中替换成daocache提供的DaoExecutor
+ *
+ * @param executor
+ * 新的DaoExecutor,不可以是null
+ */
+ public void setExecutor(DaoExecutor executor) {
+ this.executor = executor;
+ }
+
+ /**
+ * 设置新的拦截器列表.
+ *
+ * @param interceptors
+ * 新的拦截器列表
+ */
+ public void setInterceptors(List interceptors) {
+ this.interceptors = interceptors;
+ }
+
+ /**
+ * 设置当前使用的数据库连接
+ *
+ * @param connection
+ * 新的数据库连接,不可以是null
+ */
+ public void setConnection(Connection connection) {
+ this.connection = connection;
+ }
+
+ /**
+ * 获取当前数据库连接
+ *
+ * @return 当前数据库连接
+ */
+ public Connection getConnection() {
+ return connection;
+ }
+
+ /**
+ * 获取当前DaoStatement的上下文,注意,一个拦截器链可能包含多个DaoStatement
+ *
+ * @return 当前DaoStatement的上下文
+ */
+ public SqlContext getSqlContext() {
+ return getDaoStatement().getContext();
+ }
+
+ /**
+ * 拦截器链的id, 为一个uu32识别符.
+ *
+ * @return 本拦截器链的id
+ */
+ public String getId() {
+ return id;
+ }
+}
diff --git a/src/org/nutz/dao/impl/DaoSupport.java b/src/org/nutz/dao/impl/DaoSupport.java
index f72d283d7..fa94b88df 100644
--- a/src/org/nutz/dao/impl/DaoSupport.java
+++ b/src/org/nutz/dao/impl/DaoSupport.java
@@ -5,16 +5,23 @@
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import javax.sql.DataSource;
import org.nutz.dao.ConnCallback;
+import org.nutz.dao.DaoInterceptor;
+import org.nutz.dao.DaoInterceptorChain;
import org.nutz.dao.DatabaseMeta;
import org.nutz.dao.SqlManager;
import org.nutz.dao.entity.EntityMaker;
import org.nutz.dao.impl.entity.AnnotationEntityMaker;
+import org.nutz.dao.impl.interceptor.DaoLogInterceptor;
+import org.nutz.dao.impl.interceptor.DaoTimeInterceptor;
import org.nutz.dao.impl.sql.NutPojoMaker;
import org.nutz.dao.impl.sql.run.NutDaoExecutor;
import org.nutz.dao.impl.sql.run.NutDaoRunner;
@@ -25,12 +32,11 @@
import org.nutz.dao.sql.Sql;
import org.nutz.dao.sql.SqlContext;
import org.nutz.dao.util.Daos;
+import org.nutz.lang.Lang;
+import org.nutz.lang.Mirror;
import org.nutz.lang.Strings;
import org.nutz.log.Log;
import org.nutz.log.Logs;
-import org.nutz.trans.Atom;
-import org.nutz.trans.Trans;
-import org.nutz.trans.Transaction;
/**
* Dao 接口实现类的一些基础环境
@@ -82,10 +88,14 @@ public class DaoSupport {
private SqlManager sqlManager;
protected int autoTransLevel = Connection.TRANSACTION_READ_COMMITTED;
+
+ protected List _interceptors;
public DaoSupport() {
this.runner = new NutDaoRunner();
this.executor = new NutDaoExecutor();
+ this._interceptors = new ArrayList();
+ this.addInterceptor("log");
}
/**
@@ -258,44 +268,13 @@ public void run(ConnCallback callback) {
}
protected int _exec(final DaoStatement... sts) {
- // 看看是不是都是 SELECT 语句
- boolean isAllSelect = true;
- for (DaoStatement st : sts) {
- if (!st.isSelect()) {
- isAllSelect = false;
- break;
- }
- }
- // 这个是具体执行的逻辑,作为一个回调
- // 后面的逻辑是判断到底应不应该在一个事务里运行它
- DaoExec callback = new DaoExec(sts);
-
- // 如果强制没有事务或者都是 SELECT,没必要启动事务
- boolean useTrans = false;
- switch (meta.getType()) {
- case PSQL:
- useTrans = true;
- break;
- case SQLITE:
- Transaction t = Trans.get();
- useTrans = (t != null && (t.getLevel() == Connection.TRANSACTION_SERIALIZABLE
- || t.getLevel() == Connection.TRANSACTION_READ_UNCOMMITTED));
- break;
- default:
- useTrans = !(Trans.isTransactionNone() && (sts.length==1 || isAllSelect));
- break;
- }
- if (!useTrans) {
- run(callback);
- }
- // 否则启动事务
- // wendal: 还是很有必要的!!尤其是解决insert的@Prev/@Next不在同一个链接的问题
- else {
- Trans.exec(autoTransLevel, callback);
- }
-
+ final DaoInterceptorChain callback = new DaoInterceptorChain(sts);
+ callback.setExecutor(executor);
+ callback.setAutoTransLevel(autoTransLevel);
+ callback.setInterceptors(Collections.unmodifiableList(this._interceptors));
+ run(callback);
// 搞定,返回结果 ^_^
- return callback.re;
+ return callback.getUpdateCount();
}
/**
@@ -306,36 +285,6 @@ protected int _exec(final DaoStatement... sts) {
protected EntityMaker createEntityMaker() {
return new AnnotationEntityMaker(dataSource, expert, holder);
}
-
- /**
- *
- * @author wendal
- * @since 1.b.44
- */
- protected class DaoExec implements Atom, ConnCallback {
- private DaoStatement[] sts;
- private int re;
-
- public DaoExec(DaoStatement... sts) {
- this.sts = sts;
- }
-
- public void run() {
- DaoSupport.this.run(this);
- }
-
- public void invoke(Connection conn) throws Exception {
- for (DaoStatement st : sts) {
- if (st == null) {
- if (log.isInfoEnabled())
- log.info("Found a null DaoStatement(SQL), ingore it ~~");
- continue;
- }
- executor.exec(conn, st);
- re += st.getUpdateCount();
- }
- }
- }
public PojoMaker pojoMaker() {
return pojoMaker;
@@ -344,4 +293,38 @@ public PojoMaker pojoMaker() {
public void setAutoTransLevel(int autoTransLevel) {
this.autoTransLevel = autoTransLevel;
}
+
+ public void setInterceptors(List