Browse Source

jfinal 3.4

James 8 years ago
parent
commit
e00cd99f4e

+ 6 - 0
src/main/java/com/jfinal/captcha/Captcha.java

@@ -65,6 +65,12 @@ public class Captcha implements Serializable {
 		this(key, value, DEFAULT_EXPIRE_TIME);
 		this(key, value, DEFAULT_EXPIRE_TIME);
 	}
 	}
 	
 	
+	/**
+	 * redis 反序列化需要默认构造方法
+	 */
+	public Captcha() {
+	}
+	
 	public String getKey() {
 	public String getKey() {
 		return key;
 		return key;
 	}
 	}

+ 1 - 1
src/main/java/com/jfinal/core/JFinal.java

@@ -155,7 +155,7 @@ public final class JFinal {
 	}
 	}
 	
 	
 	/**
 	/**
-	 * 用于在 Eclipse 中,通过创建 main 方法的方式启动项目,支持加载
+	 * 用于在 Eclipse 中,通过创建 main 方法的方式启动项目,支持加载
 	 */
 	 */
 	public static void start(String webAppDir, int port, String context, int scanIntervalSeconds) {
 	public static void start(String webAppDir, int port, String context, int scanIntervalSeconds) {
 		server = ServerFactory.getServer(webAppDir, port, context, scanIntervalSeconds);
 		server = ServerFactory.getServer(webAppDir, port, context, scanIntervalSeconds);

+ 1 - 1
src/main/java/com/jfinal/kit/PathKit.java

@@ -110,7 +110,7 @@ public class PathKit {
 		}
 		}
 	}
 	}
 	
 	
-	public static boolean isAbsolutelyPath(String path) {
+	public static boolean isAbsolutePath(String path) {
 		return path.startsWith("/") || path.indexOf(':') == 1;
 		return path.startsWith("/") || path.indexOf(':') == 1;
 	}
 	}
 	
 	

+ 11 - 0
src/main/java/com/jfinal/plugin/activerecord/TableBuilder.java

@@ -71,6 +71,7 @@ class TableBuilder {
 		ResultSet rs = stm.executeQuery(sql);
 		ResultSet rs = stm.executeQuery(sql);
 		ResultSetMetaData rsmd = rs.getMetaData();
 		ResultSetMetaData rsmd = rs.getMetaData();
 		
 		
+		// setColumnType(...) 置入的 java 类型主要用于 core 包下面的 parameter 转成正确的 java 类型
 		for (int i=1; i<=rsmd.getColumnCount(); i++) {
 		for (int i=1; i<=rsmd.getColumnCount(); i++) {
 			String colName = rsmd.getColumnName(i);
 			String colName = rsmd.getColumnName(i);
 			String colClassName = rsmd.getColumnClassName(i);
 			String colClassName = rsmd.getColumnClassName(i);
@@ -87,6 +88,16 @@ class TableBuilder {
 				else if (type == Types.CLOB || type == Types.NCLOB) {
 				else if (type == Types.CLOB || type == Types.NCLOB) {
 					table.setColumnType(colName, String.class);
 					table.setColumnType(colName, String.class);
 				}
 				}
+				// 支持 oracle.sql.TIMESTAMP
+				else if (type == Types.TIMESTAMP) {
+					table.setColumnType(colName, java.sql.Timestamp.class);
+				}
+				// 支持 oracle.sql.DATE
+				// 实际情况是 oracle DATE 字段仍然返回的是 Types.TIMESTAMP,而且 oralce 的 DATE 字段上的 getColumnClassName(i) 方法返回的是 java.sql.Timestamp 可以被正确处理
+				// 所以,此处的 if 判断一是为了逻辑上的正确性、完备性,二是其它类型的数据库可能用得着
+				else if (type == Types.DATE) {
+					table.setColumnType(colName, java.sql.Date.class);
+				}
 				else {
 				else {
 					table.setColumnType(colName, String.class);
 					table.setColumnType(colName, String.class);
 				}
 				}

+ 76 - 0
src/main/java/com/jfinal/plugin/activerecord/builder/TimestampProcessedModelBuilder.java

@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.jfinal.plugin.activerecord.builder;
+
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import com.jfinal.plugin.activerecord.CPI;
+import com.jfinal.plugin.activerecord.Model;
+import com.jfinal.plugin.activerecord.ModelBuilder;
+
+/**
+ * TimestampProcessedModelBuilder
+ * 时间戳被处理过的 ModelBuilder
+ * oracle 从 Connection 中取值时需要调用具体的 getTimestamp(int) 来取值
+ */
+public class TimestampProcessedModelBuilder extends ModelBuilder {
+	
+	public static final TimestampProcessedModelBuilder me = new TimestampProcessedModelBuilder();
+	
+	@SuppressWarnings({"rawtypes", "unchecked"})
+	public <T> List<T> build(ResultSet rs, Class<? extends Model> modelClass) throws SQLException, InstantiationException, IllegalAccessException {
+		List<T> result = new ArrayList<T>();
+		ResultSetMetaData rsmd = rs.getMetaData();
+		int columnCount = rsmd.getColumnCount();
+		String[] labelNames = new String[columnCount + 1];
+		int[] types = new int[columnCount + 1];
+		buildLabelNamesAndTypes(rsmd, labelNames, types);
+		while (rs.next()) {
+			Model<?> ar = modelClass.newInstance();
+			Map<String, Object> attrs = CPI.getAttrs(ar);
+			for (int i=1; i<=columnCount; i++) {
+				Object value;
+				if (types[i] < Types.DATE)
+					value = rs.getObject(i);
+				else if (types[i] == Types.TIMESTAMP)
+					value = rs.getTimestamp(i);
+				else if (types[i] == Types.DATE)
+					value = rs.getDate(i);
+				else if (types[i] == Types.CLOB)
+					value = handleClob(rs.getClob(i));
+				else if (types[i] == Types.NCLOB)
+					value = handleClob(rs.getNClob(i));
+				else if (types[i] == Types.BLOB)
+					value = handleBlob(rs.getBlob(i));
+				else
+					value = rs.getObject(i);
+				
+				attrs.put(labelNames[i], value);
+			}
+			result.add((T)ar);
+		}
+		return result;
+	}
+}
+
+
+

+ 80 - 0
src/main/java/com/jfinal/plugin/activerecord/builder/TimestampProcessedRecordBuilder.java

@@ -0,0 +1,80 @@
+/**
+ * Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.jfinal.plugin.activerecord.builder;
+
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import com.jfinal.plugin.activerecord.CPI;
+import com.jfinal.plugin.activerecord.Config;
+import com.jfinal.plugin.activerecord.ModelBuilder;
+import com.jfinal.plugin.activerecord.Record;
+import com.jfinal.plugin.activerecord.RecordBuilder;
+
+/**
+ * TimestampProcessedRecordBuilder
+ * 时间戳被处理过的 RecordBuilder
+ * oracle 从 Connection 中取值时需要调用具体的 getTimestamp(int) 来取值
+ */
+public class TimestampProcessedRecordBuilder extends RecordBuilder {
+	
+	public static final TimestampProcessedRecordBuilder me = new TimestampProcessedRecordBuilder();
+	
+	@SuppressWarnings("unchecked")
+	public List<Record> build(Config config, ResultSet rs) throws SQLException {
+		List<Record> result = new ArrayList<Record>();
+		ResultSetMetaData rsmd = rs.getMetaData();
+		int columnCount = rsmd.getColumnCount();
+		String[] labelNames = new String[columnCount + 1];
+		int[] types = new int[columnCount + 1];
+		buildLabelNamesAndTypes(rsmd, labelNames, types);
+		while (rs.next()) {
+			Record record = new Record();
+			CPI.setColumnsMap(record, config.getContainerFactory().getColumnsMap());
+			Map<String, Object> columns = record.getColumns();
+			for (int i=1; i<=columnCount; i++) {
+				Object value;
+				if (types[i] < Types.DATE)
+					value = rs.getObject(i);
+				else if (types[i] == Types.TIMESTAMP)
+					value = rs.getTimestamp(i);
+				else if (types[i] == Types.DATE)
+					value = rs.getDate(i);
+				else if (types[i] == Types.CLOB)
+					value = ModelBuilder.me.handleClob(rs.getClob(i));
+				else if (types[i] == Types.NCLOB)
+					value = ModelBuilder.me.handleClob(rs.getNClob(i));
+				else if (types[i] == Types.BLOB)
+					value = ModelBuilder.me.handleBlob(rs.getBlob(i));
+				else
+					value = rs.getObject(i);
+				
+				columns.put(labelNames[i], value);
+			}
+			result.add(record);
+		}
+		return result;
+	}
+}
+
+
+
+

+ 7 - 0
src/main/java/com/jfinal/plugin/activerecord/dialect/OracleDialect.java

@@ -24,12 +24,19 @@ import java.util.Map.Entry;
 import java.util.Set;
 import java.util.Set;
 import com.jfinal.plugin.activerecord.Record;
 import com.jfinal.plugin.activerecord.Record;
 import com.jfinal.plugin.activerecord.Table;
 import com.jfinal.plugin.activerecord.Table;
+import com.jfinal.plugin.activerecord.builder.TimestampProcessedModelBuilder;
+import com.jfinal.plugin.activerecord.builder.TimestampProcessedRecordBuilder;
 
 
 /**
 /**
  * OracleDialect.
  * OracleDialect.
  */
  */
 public class OracleDialect extends Dialect {
 public class OracleDialect extends Dialect {
 	
 	
+	public OracleDialect() {
+		this.modelBuilder = TimestampProcessedModelBuilder.me;
+		this.recordBuilder = TimestampProcessedRecordBuilder.me;
+	}
+	
 	public String forTableBuilderDoBuild(String tableName) {
 	public String forTableBuilderDoBuild(String tableName) {
 		return "select * from " + tableName + " where rownum < 1";
 		return "select * from " + tableName + " where rownum < 1";
 	}
 	}

+ 30 - 2
src/main/java/com/jfinal/plugin/activerecord/dialect/SqlServerDialect.java

@@ -199,11 +199,39 @@ public class SqlServerDialect extends Dialect {
 	}
 	}
 	
 	
 	public void fillStatement(PreparedStatement pst, List<Object> paras) throws SQLException {
 	public void fillStatement(PreparedStatement pst, List<Object> paras) throws SQLException {
-		fillStatementHandleDateType(pst, paras);
+		for (int i=0, size=paras.size(); i<size; i++) {
+			Object value = paras.get(i);
+			if (value instanceof java.sql.Date) {
+				pst.setDate(i + 1, (java.sql.Date)value);
+			} else if (value instanceof java.sql.Timestamp) {
+				pst.setTimestamp(i + 1, (java.sql.Timestamp)value);
+			} else if (value instanceof java.util.Date) {
+				// 相对于 Dialect 中多出来的一个 if 分支
+				// issue: http://www.jfinal.com/feedback/2667#replyContent
+				java.util.Date d = (java.util.Date)value;
+				pst.setTimestamp(i + 1, new java.sql.Timestamp(d.getTime()));
+			} else {
+				pst.setObject(i + 1, value);
+			}
+		}
 	}
 	}
 	
 	
 	public void fillStatement(PreparedStatement pst, Object... paras) throws SQLException {
 	public void fillStatement(PreparedStatement pst, Object... paras) throws SQLException {
-		fillStatementHandleDateType(pst, paras);
+		for (int i=0; i<paras.length; i++) {
+			Object value = paras[i];
+			if (value instanceof java.sql.Date) {
+				pst.setDate(i + 1, (java.sql.Date)value);
+			} else if (value instanceof java.sql.Timestamp) {
+				pst.setTimestamp(i + 1, (java.sql.Timestamp)value);
+			} else if (value instanceof java.util.Date) {
+				// 相对于 Dialect 中多出来的一个 if 分支
+				// issue: http://www.jfinal.com/feedback/2667#replyContent
+				java.util.Date d = (java.util.Date)value;
+				pst.setTimestamp(i + 1, new java.sql.Timestamp(d.getTime()));
+			} else {
+				pst.setObject(i + 1, value);
+			}
+		}
 	}
 	}
 }
 }
 
 

+ 5 - 0
src/main/java/com/jfinal/plugin/activerecord/generator/MetaBuilder.java

@@ -247,6 +247,11 @@ public class MetaBuilder {
 					typeStr = "byte[]";
 					typeStr = "byte[]";
 				} else if (type == Types.CLOB || type == Types.NCLOB) {
 				} else if (type == Types.CLOB || type == Types.NCLOB) {
 					typeStr = "java.lang.String";
 					typeStr = "java.lang.String";
+				}
+				// 支持 oracle 的 TIMESTAMP、DATE 字段类型,其中 Types.DATE 值并不会出现
+				// 保留对 Types.DATE 的判断,一是为了逻辑上的正确性、完备性,二是其它类型的数据库可能用得着
+				else if (type == Types.TIMESTAMP || type == Types.DATE) {
+					typeStr = "java.util.Date";
 				} else {
 				} else {
 					typeStr = "java.lang.String";
 					typeStr = "java.lang.String";
 				}
 				}

+ 1 - 1
src/main/java/com/jfinal/render/RenderManager.java

@@ -122,7 +122,7 @@ public class RenderManager {
 		
 		
 		String baseDownloadPath;
 		String baseDownloadPath;
 		// 如果为绝对路径则直接使用,否则把 downloadPath 参数作为项目根路径的相对路径
 		// 如果为绝对路径则直接使用,否则把 downloadPath 参数作为项目根路径的相对路径
-		if (PathKit.isAbsolutelyPath(downloadPath)) {
+		if (PathKit.isAbsolutePath(downloadPath)) {
 			baseDownloadPath = downloadPath;
 			baseDownloadPath = downloadPath;
 		} else {
 		} else {
 			baseDownloadPath = PathKit.getWebRootPath() + File.separator + downloadPath;
 			baseDownloadPath = PathKit.getWebRootPath() + File.separator + downloadPath;

+ 1 - 1
src/main/java/com/jfinal/template/expr/ast/Id.java

@@ -23,7 +23,7 @@ import com.jfinal.template.stat.Scope;
  */
  */
 public class Id extends Expr {
 public class Id extends Expr {
 	
 	
-	private String id;
+	private final String id;
 	
 	
 	public Id(String id) {
 	public Id(String id) {
 		this.id = id;
 		this.id = id;

+ 9 - 2
src/main/java/com/jfinal/template/ext/directive/RandomDirective.java

@@ -16,22 +16,29 @@
 
 
 package com.jfinal.template.ext.directive;
 package com.jfinal.template.ext.directive;
 
 
+import java.io.IOException;
 import com.jfinal.template.Directive;
 import com.jfinal.template.Directive;
 import com.jfinal.template.Env;
 import com.jfinal.template.Env;
+import com.jfinal.template.TemplateException;
 import com.jfinal.template.io.Writer;
 import com.jfinal.template.io.Writer;
 import com.jfinal.template.stat.Scope;
 import com.jfinal.template.stat.Scope;
 
 
 /**
 /**
- * 输出随机数
+ * 输出 int 型随机数
  */
  */
 public class RandomDirective extends Directive {
 public class RandomDirective extends Directive {
 	
 	
 	private java.util.Random random = new java.util.Random();
 	private java.util.Random random = new java.util.Random();
 	
 	
 	public void exec(Env env, Scope scope, Writer writer) {
 	public void exec(Env env, Scope scope, Writer writer) {
-		write(writer, String.valueOf(random.nextInt()));
+		try {
+			writer.write(random.nextInt());
+		} catch (IOException e) {
+			throw new TemplateException(e.getMessage(), location, e);
+		}
 	}
 	}
 }
 }
 
 
 
 
 
 
+

+ 1 - 1
src/main/java/com/jfinal/upload/OreillyCos.java

@@ -51,7 +51,7 @@ public class OreillyCos {
 		uploadPath = uploadPath.replaceAll("\\\\", "/");
 		uploadPath = uploadPath.replaceAll("\\\\", "/");
 		
 		
 		String baseUploadPath;
 		String baseUploadPath;
-		if (PathKit.isAbsolutelyPath(uploadPath)) {
+		if (PathKit.isAbsolutePath(uploadPath)) {
 			baseUploadPath = uploadPath;
 			baseUploadPath = uploadPath;
 		} else {
 		} else {
 			baseUploadPath = PathKit.getWebRootPath() + File.separator + uploadPath;
 			baseUploadPath = PathKit.getWebRootPath() + File.separator + uploadPath;