/** * 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.dialect; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import com.jfinal.plugin.activerecord.Config; import com.jfinal.plugin.activerecord.Model; import com.jfinal.plugin.activerecord.ModelBuilder; import com.jfinal.plugin.activerecord.Page; import com.jfinal.plugin.activerecord.Record; import com.jfinal.plugin.activerecord.RecordBuilder; import com.jfinal.plugin.activerecord.Table; /** * Dialect. */ public abstract class Dialect { // Methods for common public abstract String forTableBuilderDoBuild(String tableName); public abstract String forPaginate(int pageNumber, int pageSize, StringBuilder findSql); // Methods for Model public abstract String forModelFindById(Table table, String columns); public abstract String forModelDeleteById(Table table); public abstract void forModelSave(Table table, Map<String, Object> attrs, StringBuilder sql, List<Object> paras); public abstract void forModelUpdate(Table table, Map<String, Object> attrs, Set<String> modifyFlag, StringBuilder sql, List<Object> paras); // Methods for DbPro. Do not delete the String[] pKeys parameter, the element of pKeys needs to trim() public abstract String forDbFindById(String tableName, String[] pKeys); public abstract String forDbDeleteById(String tableName, String[] pKeys); public abstract void forDbSave(String tableName, String[] pKeys, Record record, StringBuilder sql, List<Object> paras); public abstract void forDbUpdate(String tableName, String[] pKeys, Object[] ids, Record record, StringBuilder sql, List<Object> paras); /** * 覆盖此方法,可以对 JDBC 到 java 数据类型进行定制化转换 * 不同数据库从 JDBC 到 java 数据类型的映射关系有所不同 * * 此外,还可以通过改变 ModelBuilder.buildLabelNamesAndTypes() * 方法逻辑,实现下划线字段名转驼峰变量名的功能 */ @SuppressWarnings("rawtypes") public <T> List<T> buildModelList(ResultSet rs, Class<? extends Model> modelClass) throws SQLException, InstantiationException, IllegalAccessException { return ModelBuilder.me.build(rs, modelClass); } /** * 覆盖此方法,可以对 JDBC 到 java 数据类型进行定制化转换 * 不同数据库从 JDBC 到 java 数据类型的映射关系有所不同 * * 此外,还可以通过改变 RecordBuilder.buildLabelNamesAndTypes() * 方法逻辑,实现下划线字段名转驼峰变量名的功能 */ public List<Record> buildRecordList(Config config, ResultSet rs) throws SQLException { return RecordBuilder.me.build(config, rs); } public boolean isOracle() { return false; } public boolean isTakeOverDbPaginate() { return false; } public Page<Record> takeOverDbPaginate(Connection conn, int pageNumber, int pageSize, Boolean isGroupBySql, String totalRowSql, StringBuilder findSql, Object... paras) throws SQLException { throw new RuntimeException("You should implements this method in " + getClass().getName()); } public boolean isTakeOverModelPaginate() { return false; } @SuppressWarnings("rawtypes") public Page takeOverModelPaginate(Connection conn, Class<? extends Model> modelClass, int pageNumber, int pageSize, Boolean isGroupBySql, String totalRowSql, StringBuilder findSql, Object... paras) throws Exception { throw new RuntimeException("You should implements this method in " + getClass().getName()); } public void fillStatement(PreparedStatement pst, List<Object> paras) throws SQLException { for (int i=0, size=paras.size(); i<size; i++) { pst.setObject(i + 1, paras.get(i)); } } public void fillStatement(PreparedStatement pst, Object... paras) throws SQLException { for (int i=0; i<paras.length; i++) { pst.setObject(i + 1, paras[i]); } } public String getDefaultPrimaryKey() { return "id"; } public boolean isPrimaryKey(String colName, String[] pKeys) { for (String pKey : pKeys) { if (colName.equalsIgnoreCase(pKey)) { return true; } } return false; } /** * 一、forDbXxx 系列方法中若有如下两种情况之一,则需要调用此方法对 pKeys 数组进行 trim(): * 1:方法中调用了 isPrimaryKey(...):为了防止在主键相同情况下,由于前后空串造成 isPrimaryKey 返回 false * 2:为了防止 tableName、colName 与数据库保留字冲突的,添加了包裹字符的:为了防止串包裹区内存在空串 * 如 mysql 使用的 "`" 字符以及 PostgreSql 使用的 "\"" 字符 * 不满足以上两个条件之一的 forDbXxx 系列方法也可以使用 trimPrimaryKeys(...) 方法让 sql 更加美观,但不是必须 * * 二、forModelXxx 由于在映射时已经trim(),故不再需要调用此方法 */ public void trimPrimaryKeys(String[] pKeys) { for (int i=0; i<pKeys.length; i++) { pKeys[i] = pKeys[i].trim(); } } protected static class Holder { // "order\\s+by\\s+[^,\\s]+(\\s+asc|\\s+desc)?(\\s*,\\s*[^,\\s]+(\\s+asc|\\s+desc)?)*"; private static final Pattern ORDER_BY_PATTERN = Pattern.compile( "order\\s+by\\s+[^,\\s]+(\\s+asc|\\s+desc)?(\\s*,\\s*[^,\\s]+(\\s+asc|\\s+desc)?)*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); } public String replaceOrderBy(String sql) { return Holder.ORDER_BY_PATTERN.matcher(sql).replaceAll(""); } }