/*
' * JEF - Copyright 2009-2010 Jiyi (mr.jiyi@gmail.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 jef.database;
import java.io.IOException;
import java.io.Serializable;
import java.io.StringReader;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.sql.SQLTimeoutException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;
import java.util.concurrent.TimeUnit;
import javax.persistence.EntityExistsException;
import javax.persistence.FetchType;
import javax.persistence.PersistenceException;
import javax.persistence.QueryTimeoutException;
import javax.sql.DataSource;
import jef.accelerator.bean.BeanAccessor;
import jef.accelerator.bean.FastBeanWrapperImpl;
import jef.common.log.LogUtil;
import jef.database.Session.UpdateContext;
import jef.database.annotation.Cascade;
import jef.database.annotation.JoinType;
import jef.database.datasource.DataSourceInfo;
import jef.database.datasource.DataSourceWrapper;
import jef.database.datasource.DataSources;
import jef.database.datasource.IRoutingDataSource;
import jef.database.datasource.SimpleDataSource;
import jef.database.dialect.DatabaseDialect;
import jef.database.dialect.type.ColumnMapping;
import jef.database.innerpool.PartitionSupport;
import jef.database.jsqlparser.parser.JpqlParser;
import jef.database.jsqlparser.parser.ParseException;
import jef.database.jsqlparser.parser.StSqlParser;
import jef.database.jsqlparser.parser.TokenMgrError;
import jef.database.jsqlparser.statement.create.ColumnDefinition;
import jef.database.jsqlparser.statement.create.CreateTable;
import jef.database.jsqlparser.statement.select.OrderBy;
import jef.database.jsqlparser.statement.select.Select;
import jef.database.jsqlparser.visitor.Expression;
import jef.database.jsqlparser.visitor.SelectItem;
import jef.database.meta.AbstractMetadata;
import jef.database.meta.AbstractRefField;
import jef.database.meta.DbProperty;
import jef.database.meta.ITableMetadata;
import jef.database.meta.JoinKey;
import jef.database.meta.JoinPath;
import jef.database.meta.MetaHolder;
import jef.database.meta.Reference;
import jef.database.query.AbstractEntityMappingProvider;
import jef.database.query.AbstractJoinImpl;
import jef.database.query.ConditionQuery;
import jef.database.query.DefaultPartitionCalculator;
import jef.database.query.EntityMappingProvider;
import jef.database.query.JoinElement;
import jef.database.query.JoinUtil;
import jef.database.query.JpqlExpression;
import jef.database.query.OrderField;
import jef.database.query.PartitionCalculator;
import jef.database.query.Query;
import jef.database.query.RefField;
import jef.database.query.ReferenceType;
import jef.database.query.SelectsImpl;
import jef.database.query.SqlContext;
import jef.database.query.SqlExpression;
import jef.database.routing.PartitionResult;
import jef.database.wrapper.executor.DbTask;
import jef.tools.ArrayUtils;
import jef.tools.Assert;
import jef.tools.JefConfiguration;
import jef.tools.StringUtils;
import jef.tools.reflect.BeanWrapper;
import jef.tools.reflect.GenericUtils;
import jef.tools.security.cplus.TripleDES;
import jef.tools.string.CharUtils;
import org.apache.commons.lang.ObjectUtils;
import com.google.common.base.Objects;
public final class DbUtils {
// 在db rac的场景,在初始化数据源时需要把dbKey和racId的对应关系注册到该全局属性中,orm需要根据映射关系合并同racId的连接。
// private static Map<String, String> dbKey2RacIds = new HashMap<String,
// String>();
// private static Map<String, List<String>> racId2DbKeys = new
// HashMap<String, List<String>>();
// private static ThreadLocal<Boolean> isRouted = new
// ThreadLocal<Boolean>();
public static final int NO_RAC_ID = -1;
public static PartitionCalculator partitionUtil = new DefaultPartitionCalculator();
/**
* 线程池。线程池有以下作用 1、在分库分表时使用线程 2、在JTA事务管理模式下,为了避免在JTA中执行DDL,因此不得不将代码在新的线程中执行。
*
* 线程池策略: 1、平时最大线程数为CPU个数乘以2. 2、任务堆积最大256个
* 3、超出256个堆积任务后,如果线程数还不到128个,则开启新线程消除堆积。如果已经达到,则让请求线程自行完成任务
*/
public static ExecutorService es;
static {
int processorCount = Runtime.getRuntime().availableProcessors();
es = new ThreadPoolExecutor(processorCount * 2, processorCount * 4, 60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(processorCount * 4), Executors.defaultThreadFactory(),
new CallerRunsPolicy());
}
/**
* 获取数据库加密的密钥,目前使用固定密钥
*
* @return
* @throws IOException
*/
private static byte[] getEncryptKey() {
String s = JefConfiguration.get(DbCfg.DB_ENCRYPTION_KEY);
if (StringUtils.isEmpty(s)) {
s = "781296-5e32-89122";
}
return s.getBytes();
}
/**
* 并行执行多个数据库任务
*
* @param tasks
* @throws SQLException
*/
public static void parallelExecute(List<DbTask> tasks) throws SQLException {
CountDownLatch latch = new CountDownLatch(tasks.size());
Queue<SQLException> exceptions = new ConcurrentLinkedQueue<SQLException>();
Queue<Throwable> throwables = new ConcurrentLinkedQueue<Throwable>();
for (DbTask task : tasks) {
task.prepare(latch, exceptions, throwables);
DbUtils.es.execute(task);
}
try {
latch.await();
} catch (InterruptedException e) {
throw new SQLException(e);
}
if (!exceptions.isEmpty()) {
throw DbUtils.wrapExceptions(exceptions);
}
if (!throwables.isEmpty()) {
throw DbUtils.toRuntimeException(throwables.peek());
}
}
/*
* 处理SQL执行错误 <strong>注意,这个方法执行期间会调用连接,因此必须在这个方法执行完后才能释放连接</strong>
*
* @param e
*
* @param tablename
*
* @param conn
*/
public static void processError(SQLException e, String tablename, OperateTarget conn) {
if (conn.getProfile().isIOError(e)) {
conn.notifyDisconnect(e);
}
DebugUtil.setSqlState(e, tablename);
}
/**
* 用于查找两个表之间的外键
*
* @param class1
* @return 外键关系
*/
public static Reference findPath(ITableMetadata from, ITableMetadata target) {
for (Reference r : from.getRefFieldsByRef().keySet()) {
if (r.getTargetType() == target) {
return r;
}
}
return null;
}
/**
* 查找到目标类型的引用关系。只允许查找到一个到目标类型的关系。如果找到0个或者多个,那么会抛出异常。
*
* @param class1
* @return 外键关系
* @throws IllegalArgumentException
*/
public static Reference findDistinctPath(ITableMetadata from, ITableMetadata target) {
Reference ref = null;
for (Reference reference : from.getRefFieldsByRef().keySet()) {
if (reference.getTargetType() == target) {
if (ref != null) {
throw new IllegalArgumentException("There's more than one reference to [" + target.getSimpleName()
+ "] in type [" + from.getSimpleName() + "],please assign the reference field name.");
}
ref = reference;
}
}
if (ref == null) {
throw new IllegalArgumentException("Target class " + target.getSimpleName()
+ "of fileter-condition is not referenced by " + from.getSimpleName());
}
return ref;
}
/**
* 如果列名或表名碰到了数据库的关键字,那么就要增加引号一类字符进行转义
*
* @param profile
* @param name
* @return
*/
public static final String escapeColumn(DatabaseDialect profile, String name) {
if (name == null)
return name;
String w = profile.getProperty(DbProperty.WRAP_FOR_KEYWORD);
if (w != null && profile.containKeyword(name)) {
StringBuilder sb = new StringBuilder(name.length() + 2);
sb.append(w.charAt(0)).append(name).append(w.charAt(1));
return sb.toString();
}
return name;
}
/**
* 根据datasource解析连接信息
*
* @param ds
* @param updateDataSourceProperties
* 在能解析出ds的情况下,向datasource的连接属性执行注入
* @return
* @throws SQLException
*/
public static ConnectInfo tryAnalyzeInfo(DataSource ds, boolean updateDataSourceProperties) {
if (ds instanceof IRoutingDataSource) {
IRoutingDataSource rds = (IRoutingDataSource) ds;
Entry<String, DataSource> e = rds.getDefaultDatasource();
if (e == null) {// 更见鬼了,没法获得缺省的DataSource。
Collection<String> names = rds.getDataSourceNames();
if (!names.isEmpty()) {
String name = names.iterator().next();
LogUtil.warn(
"Can not determine default datasource name. choose [" + name + "] as default datasource.");
return tryAnalyzeInfo(rds.getDataSource(name), updateDataSourceProperties);
}
} else {
return tryAnalyzeInfo(e.getValue(), updateDataSourceProperties);
}
}
DataSourceWrapper dsw = DataSources.wrapFor(ds);
if (dsw != null) {
ConnectInfo info = new ConnectInfo();
DbUtils.processDataSourceOfEnCrypted(dsw);
info.url = dsw.getUrl();
info.user = dsw.getUser();
info.password = dsw.getPassword();
DatabaseDialect profile = info.parse();// 解析,获得profile, 解析出数据库名等信息
if (updateDataSourceProperties)
profile.processConnectProperties(dsw);
return info;// 理想情况
}
return null;
}
/**
* 根据已有的连接解析连接信息
*
* @param conn
* @return
* @throws SQLException
*/
public static ConnectInfo tryAnalyzeInfo(Connection conn) throws SQLException {
DatabaseMetaData meta = conn.getMetaData();
ConnectInfo info = new ConnectInfo();
info.user = meta.getUserName();
info.url = meta.getURL();
info.parse();// 解析,获得profile, 解析出数据库名等信息
return info;
}
/**
* Close the given JDBC Connection and ignore any thrown exception. This is
* useful for typical finally blocks in manual JDBC code.
*
* @param con
* the JDBC Connection to close (may be {@code null})
*/
public static void closeConnection(Connection con) {
if (con != null) {
try {
con.close();
} catch (SQLException ex) {
LogUtil.exception("Could not close JDBC Connection", ex);
} catch (Throwable ex) {
LogUtil.exception("Unexpected exception on closing JDBC Connection", ex);
}
}
}
/**
* 将SQL异常构成链表
*
* @param errors
* @return
*/
public static final SQLException wrapExceptions(Collection<SQLException> errors) {
if (errors == null || errors.isEmpty())
return null;
Iterator<SQLException> iter = errors.iterator();
SQLException root = iter.next();
SQLException last = root;
while (iter.hasNext()) {
SQLException current = iter.next();
last.setNextException(current);
last = current;
}
return root;
}
/**
* 数据库密码解密
*/
public static String decrypt(String pass) {
TripleDES t = new TripleDES();
String text = t.decipher2(getEncryptKey(), pass);
return text;
}
/**
* 数据库密码解密
*/
public static String ecrypt(String pass) throws IOException {
TripleDES t = new TripleDES();
String text = t.cipher2(getEncryptKey(), pass);
return text;
}
/**
* 处理DataSource中的密码
*
* @param ds
*/
public static void processDataSourceOfEnCrypted(DataSourceInfo ds) {
boolean flag = JefConfiguration.getBoolean(DbCfg.DB_PASSWORD_ENCRYPTED, false);
if (!flag) {
return;
}
String old = ds.getPassword();
if (old != null && old.matches("[a-fA-F0-9]{16,}")) {
ds.setPassword(decrypt(old));
}
}
/**
* 解析select后的语句
*
* @param sql
* @return
* @throws ParseException
*/
@SuppressWarnings("unchecked")
public static List<SelectItem> parseSelectItems(String sql) throws ParseException {
JpqlParser parser = new JpqlParser(new StringReader(sql));
return parser.SelectItemsList();
}
public static ColumnDefinition parseColumnDef(String def) throws ParseException {
String sql = StringUtils.concat("create table A (B ", def, ")");
StSqlParser parser = new StSqlParser(new StringReader(sql));
CreateTable ct = parser.CreateTable();
return ct.getColumnDefinitions().get(0);
}
/**
* 解析select语句
*
* @param sql
* @return
* @throws ParseException
*/
public static Select parseSelect(String sql) throws ParseException {
JpqlParser parser = new JpqlParser(new StringReader(sql));
return parser.Select();
}
/**
* 解析Select语句(原生SQL)
*
* @param sql
* @return
* @throws ParseException
*/
public static Select parseNativeSelect(String sql) throws ParseException {
StSqlParser parser = new StSqlParser(new StringReader(sql));
return parser.Select();
}
/**
* 解析表达式
*
* @param sql
* @return
* @throws ParseException
*/
public static Expression parseExpression(String sql) throws ParseException {
JpqlParser parser = new JpqlParser(new StringReader(sql));
return parser.SimpleExpression();
}
/**
* 解析OrderBy元素
*
* @param sql
* @return
* @throws ParseException
*/
public static OrderBy parseOrderBy(String sql) {
StSqlParser parser = new StSqlParser(new StringReader("ORDER BY " + sql));
try {
return parser.OrderByElements();
} catch (ParseException e) {
throw new PersistenceException(sql, e);
}
}
/**
* 解析二元表达式
*
* @param sql
* @return
* @throws ParseException
*/
public static Expression parseBinaryExpression(String sql) throws ParseException {
JpqlParser parser = new JpqlParser(new StringReader(sql));
return parser.Expression();
}
/**
* 解析语句
*
* @param sql
* @return
* @throws ParseException
*/
public static jef.database.jsqlparser.visitor.Statement parseStatement(String sql) throws ParseException {
JpqlParser parser = new JpqlParser(new StringReader(sql));
try {
return parser.Statement();
} catch (ParseException e) {
LogUtil.error("ErrorSQL:" + sql);
throw e;
} catch (TokenMgrError e) {
LogUtil.error("ErrorSQL:" + sql);
throw e;
}
}
/**
* 对于检测基本查询是否需要展开成连接查询,如果需要就展开
*
* @param <T>
* @param queryObj
* @return
*/
@SuppressWarnings("unchecked")
public static <T extends IQueryableEntity> JoinElement toReferenceJoinQuery(Query<T> queryObj,
List<Reference> excludeRef) {
// 得到可以合并查询的引用关系
Map<Reference, List<AbstractRefField>> map = queryObj.isCascadeViaOuterJoin()
? DbUtils.getMergeAsOuterJoinRef(queryObj) : Collections.EMPTY_MAP;
Query<?>[] otherQuery = queryObj.getOtherQueryProvider();
if (otherQuery.length == 0 && map.isEmpty()) {
return queryObj;
}
// 拼装出带连接的查询请求
AbstractJoinImpl j = DbUtils.getJoin(queryObj, map, ArrayUtils.asList(otherQuery), excludeRef);
if (j != null) {
j.setFetchSize(queryObj.getFetchSize());
j.setMaxResult(queryObj.getMaxResult());
j.setQueryTimeout(queryObj.getQueryTimeout());
if (queryObj.getSelectItems() != null) {
List<QueryAlias> qs = j.allElements();
for (int i = 0; i < qs.size(); i++) {
qs.get(i).setAlias("T" + (i + 1));
}
SelectsImpl select = new jef.database.query.SelectsImpl(qs);
select.merge((AbstractEntityMappingProvider) queryObj.getSelectItems());
j.setSelectItems(select);
}
// TODO 其实cacheable, Transformer,
// attribute都是Query在转换时需要保持不变的属性,可以设法抽取到公共类中。
j.setResultTransformer(queryObj.getResultTransformer());
j.setCacheable(queryObj.isCacheable());
// FilterCondition合并
if (queryObj.getFilterCondition() != null) {
for (QueryAlias qa : j.allElements()) {
if (qa.getStaticRef() != null) {
List<Condition> con = queryObj.getFilterCondition().get(qa.getStaticRef());
if (con != null) {
j.addRefConditions(qa.getQuery(), con);
}
}
}
}
return j;
} else {
return queryObj;
}
}
/**
* 将带下划线的名称,转为不带下划线的名称<br>
* 例如: you_are_boy -> YouAreBoy
*
* @param name
* 待转的名称
* @param capitalize
* 首字母是否要大写
* @return 转换后的名称
*/
public static String underlineToUpper(String name, boolean capitalize) {
char[] r = new char[name.length()];
int n = 0;
boolean nextUpper = capitalize;
for (char c : name.toCharArray()) {
if (c == '_') {
nextUpper = true;
} else {
if (nextUpper) {
r[n] = Character.toUpperCase(c);
nextUpper = false;
} else {
r[n] = Character.toLowerCase(c);
}
n++;
}
}
return new String(r, 0, n);
}
/**
* 将带大小写的名称,转换为全小写但带下划线的名称 例如: iLoveYou -> i_love_you
*
* @param name
* 待转换为名称
* @return 转换后的名称
*/
public static String upperToUnderline(String name) {
if (name == null)
return null;
boolean skipUpper = true;
StringBuilder sb = new StringBuilder();
char[] chars = name.toCharArray();
sb.append(chars[0]);
for (int i = 1; i < chars.length; i++) {
if (CharUtils.isUpperAlpha(chars[i])) {
if (!skipUpper) {
sb.append('_').append(chars[i]);
skipUpper = true;
} else {
if (i + 2 < chars.length && CharUtils.isLowerAlpha(chars[i + 1])) {
sb.append('_').append(chars[i]);
} else {
sb.append(chars[i]);
}
}
} else {
sb.append(chars[i]);
skipUpper = false;
}
}
return sb.toString().toUpperCase();
}
/**
* 设置指定的值到主键
*
* @param data
* 对象
* @param pk
* 主键,可以是Map或单值
*/
public static void setPrimaryKeyValue(IQueryableEntity data, Object pk) throws PersistenceException {
List<ColumnMapping> fields = MetaHolder.getMeta(data).getPKFields();
if (fields.isEmpty())
return;
Assert.notNull(pk);
// if (pk instanceof Map) {
// Map<String, Object> pkMap = (Map<String, Object>) pk;
// BeanWrapper wrapper = BeanWrapper.wrap(data, BeanWrapper.FAST);
// for (Field f : fields) {
// if (wrapper.isWritableProperty(f.name())) {
// wrapper.setPropertyValue(f.name(), pkMap.get(f.name()));
// }
// }
// } else
if (pk.getClass().isArray()) {
int length = Array.getLength(pk);
int n = 0;
Assert.isTrue(length == fields.size());
for (ColumnMapping f : fields) {
f.getFieldAccessor().set(data, Array.get(pk, n++));
}
} else {
if (fields.size() != 1) {
throw new PersistenceException("No Proper PK fields!");
}
fields.get(0).getFieldAccessor().set(data, pk);
}
}
/**
* 提供主键的值
*/
public static Map<String, Object> getPrimaryKeyValueMap(IQueryableEntity data) {
ITableMetadata meta = MetaHolder.getMeta(data);
int len = meta.getPKFields().size();
if (len == 0)
return null;
Map<String, Object> keyValMap = new HashMap<String, Object>();
for (int i = 0; i < len; i++) {
ColumnMapping field = meta.getPKFields().get(i);
if (!isValidPKValue(data, meta, field)) {
return null;
}
keyValMap.put(field.fieldName(), field.getFieldAccessor().get(data));
}
return keyValMap;
}
/**
* 提供主键的值
*/
public static List<Object> getPrimaryKeyValue(IQueryableEntity data) {
ITableMetadata meta = MetaHolder.getMeta(data);
if (meta.getPKFields().isEmpty())
return null;
int len = meta.getPKFields().size();
Object[] result = new Object[len];
for (int i = 0; i < len; i++) {
ColumnMapping field = meta.getPKFields().get(i);
if (!isValidPKValue(data, meta, field)) {
return null;
}
result[i] = field.getFieldAccessor().get(data);
}
return Arrays.asList(result);
}
// 从实体中获取主键的值,这里的实体都必须是已经从数据库中选择出来的,因此无需校验主键值是否合法
public static List<Serializable> getPKValueSafe(IQueryableEntity data) {
ITableMetadata meta = MetaHolder.getMeta(data);
if (meta.getPKFields().isEmpty())
return null;
int len = meta.getPKFields().size();
Serializable[] result = new Serializable[len];
for (int i = 0; i < len; i++) {
ColumnMapping field = meta.getPKFields().get(i);
result[i] = (Serializable) field.getFieldAccessor().get(data);
}
return Arrays.asList(result);
}
/**
* 将field转换为列名(包含表的别名)
*
* @param field
* 字段
* @param feature
* 数据库方言
* @param tableAlias
* 所在表的别名
* @return 可以在SQL中使用的列名
* @deprecated use
* {@linkplain #toColumnName(ColumnMapping, DatabaseDialect, String)}
* instead
*/
public static String toColumnName(Field field, DatabaseDialect feature, String tableAlias) {
if (field instanceof MetadataContainer) {
ITableMetadata meta = ((MetadataContainer) field).getMeta();
return getColumnName(meta, field, tableAlias, feature);
}
ITableMetadata meta = getTableMeta(field);
if (field instanceof JpqlExpression) {
return ((JpqlExpression) field).toSqlAndBindAttribs(null, feature);
} else {
return getColumnName(meta, field, tableAlias, feature);
}
}
/**
* 代替上面的方法,性能更好,也更安全
*
* @param column
* 列定义
* @param profile
* 数据库方言
* @param tableAlias
* 列所在的表的别名
* @return 使用在SQL中的列名称
*/
public static String toColumnName(ColumnMapping column, DatabaseDialect profile, String alias) {
if (alias != null) {
StringBuilder sb = new StringBuilder();
sb.append(alias).append('.').append(column.getColumnName(profile, true));
return sb.toString();
} else {
return column.getColumnName(profile, true);
}
}
/**
* 返回某个field的数据库列名称
*
* @param field
* field
* @param alias
* 数据库表别名
* @param profile
* 当前数据库方言
* @return 数据库列名称
*
* @deprecated 不够安全
*/
public static String getColumnName(ITableMetadata meta, Field fld, String alias, DatabaseDialect profile) {
if (alias == null) {
return meta.getColumnName(fld, profile, true);
} else {
if (fld instanceof JpqlExpression) {
throw new UnsupportedOperationException();
} else {
StringBuilder sb = new StringBuilder();
sb.append(alias).append('.').append(meta.getColumnName(fld, profile, true));
return sb.toString();
}
}
}
/*
* 当存在引用列时的连接创建方式。由于连接中的表名需要转换后重映射到字段上,因此需要
*
* @param d 查询实体
*
* @param map 通过元数据配置的表关联
*
* @param queryProvider : 额外的外表关联
*
* @return
*/
protected static AbstractJoinImpl getJoin(Query<?> d, Map<Reference, List<AbstractRefField>> map,
List<Query<?>> queryProvider, List<Reference> exclude) {
AbstractJoinImpl join = null;
// 处理默认需要的连接查询:该种关联只关联一级,不会递归关联。
for (Reference r : map.keySet()) {
if (exclude != null && exclude.contains(r))
continue;
Query<?> tQuery = null;
for (Query<?> t : queryProvider) {
if (t.getInstance().getClass() == r.getTargetType().getThisType()) {
queryProvider.remove(t);
tQuery = t;
break;
}
}
if (join == null) {
join = JoinUtil.create(d, r, tQuery);
Assert.notNull(join, "Invalid Reference:" + r.toString());
} else {
join = JoinUtil.create(join, r, tQuery);
Assert.notNull(join, "Invalid Reference:" + r.toString());
}
}
// 还有一些REF条件,可能隐式地指定了若干的外部查询实例,此时需要将这些隐式查询实例添加到Join上
List<QueryAlias> qs = join == null ? Arrays.asList(new QueryAlias(null, d)) : join.allElements();
AbstractEntityMappingProvider tmpContext = new SqlContext(-1, qs, null);
for (Condition c : d.getConditions()) {
checkIfThereIsExQueryInRefField(c.getField(), tmpContext, queryProvider);
}
for (OrderField c : d.getOrderBy()) {
checkIfThereIsExQueryInRefField(c.getField(), tmpContext, queryProvider);
}
// 处理其他的额外Query……注意要设置好拼装路径
while (queryProvider.size() > 0) {
int left = queryProvider.size();
for (Iterator<Query<?>> iter = queryProvider.iterator(); iter.hasNext();) {
Query<?> tq = iter.next();
AbstractJoinImpl ng = JoinUtil.create(join == null ? d : join, tq, null, null, false);
if (ng != null) {
iter.remove();
join = ng;
}
}
if (left == queryProvider.size()) {// reverse look for...
for (Iterator<Query<?>> iter = queryProvider.iterator(); iter.hasNext();) {
Query<?> tq = iter.next();
AbstractJoinImpl ng = JoinUtil.create(join == null ? d : join, tq, null, null, true);
if (ng != null) {
iter.remove();
join = ng;
}
}
}
if (left == queryProvider.size()) {// 用户提供的额外查询表实例无法拼装到已有的查询对象上
LogUtil.error("There 's still " + queryProvider.size() + " query not added into join.");
break;
}
}
if (join != null)
join.fillAttribute((Query<?>) d);
return join;
}
/*
* 递归检查field绑定情况(如果怕field是RefField……)
*/
private static void checkIfThereIsExQueryInRefField(Field field, AbstractEntityMappingProvider tmpContext,
List<Query<?>> qt) {
// 为了在RefField中省略默认的查询,所以要对所有条件树中的refField进行检查,将未指定的Query实例自动补全
if (field instanceof RefField) {
rebindRefField((RefField) field, tmpContext, qt);
} else if (field instanceof IConditionField) {
IConditionField condf = (IConditionField) field;
processConditionField(condf, tmpContext, qt);
}
}
// 检查所有条件中的REFField(递归)
private static void rebindRefField(RefField ref, AbstractEntityMappingProvider tmpContext, List<Query<?>> qt) {
if (!ref.isBindOn(tmpContext)) {
Query<?> refQuery = ref.getInstanceQuery(null);
for (Query<?> extQuery : qt) {
if (refQuery == extQuery) {
return;
} else if (refQuery.getType() == extQuery.getType()
&& refQuery.getConditions().equals(extQuery.getConditions())) {
// ref.rebind(extQuery,null);
return;
}
}
qt.add(refQuery);
}
}
// 检查所有条件中的REFField(递归)
private static void processConditionField(IConditionField container, AbstractEntityMappingProvider tmpContext,
List<Query<?>> qt) {
for (Condition c : container.getConditions()) {
Field field = c.getField();
if (field instanceof IConditionField) {
processConditionField((IConditionField) field, tmpContext, qt);
} else if (field instanceof RefField) {
rebindRefField((RefField) field, tmpContext, qt);
}
}
}
/**
* 转换为合理的集合类型容器
*
* @param subs
* @param container
* @return
* @throws SQLException
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
protected static Object toProperContainerType(Collection<? extends IQueryableEntity> subs, Class<?> container,
Class<?> bean, AbstractRefField config) throws SQLException {
if (container.isAssignableFrom(subs.getClass())) {
return subs;
}
if (container == Set.class) {
HashSet set = new HashSet();
set.addAll(subs);
return set;
} else if (container == List.class) {
ArrayList list = new ArrayList();
list.addAll(subs);
return list;
} else if (container == Array.class) {
return subs.toArray();
} else if (container == Map.class) {
Cascade cascade = config.getCascadeInfo();
if (cascade == null) {
throw new SQLException("@Cascade annotation is required for Map mapping " + config.toString());
}
Map map = new HashMap();
String key = cascade.keyOfMap();
BeanAccessor ba = FastBeanWrapperImpl.getAccessorFor(bean);
if (StringUtils.isEmpty(cascade.valueOfMap())) {
for (IQueryableEntity e : subs) {
map.put(ba.getProperty(e, key), e);
}
} else {
String vField = cascade.valueOfMap();
for (IQueryableEntity e : subs) {
map.put(ba.getProperty(e, key), ba.getProperty(e, vField));
}
}
return map;
}
throw new SQLException("the type " + container.getName() + " is not supported as a collection container.");
}
/**
* 得到外连接加载的引用。
*
* @param data
* @return
*/
protected static Map<Reference, List<AbstractRefField>> getMergeAsOuterJoinRef(Query<?> q) {
Map<Reference, List<AbstractRefField>> result = new HashMap<Reference, List<AbstractRefField>>(5);
// 获得所有未配置为延迟加载的引用。
for (Map.Entry<Reference, List<AbstractRefField>> entry : q.getMeta().getRefFieldsByRef().entrySet()) {
Reference key = entry.getKey();
if (key.getType().isToOne()) {
List<AbstractRefField> value = entry.getValue();
AbstractRefField first = value.get(0);
if (first.getFetch() == FetchType.LAZY) {
continue;
}
result.put(key, value);
}
}
// 过滤掉一部分因为使用了过滤条件而不得不延迟加载的应用
if (q.getFilterCondition() != null) {
for (Reference ref : q.getFilterCondition().keySet()) {
if (ref.getType().isToOne() && ref.getJoinType() != JoinType.LEFT) {
result.remove(ref);
}
}
}
return result;
}
/**
* 得到二次加载的Ref
*
* @param data
* 表对象
* @param excludeReference
* 需要排除的关联,为null表示默认方式。为空表示全部延迟加载
* @return
*/
protected static Map<Reference, List<AbstractRefField>> getLazyLoadRef(ITableMetadata data,
Collection<Reference> excludeReference) {
// ==null时,对单关联使用外连接,对多关联使用延迟加载,上个版本的形式
// 应该逐渐淘汰的形式
if (excludeReference == null) {
Map<Reference, List<AbstractRefField>> result = new HashMap<Reference, List<AbstractRefField>>(5);
for (Map.Entry<Reference, List<AbstractRefField>> entry : data.getRefFieldsByRef().entrySet()) {
Reference key = entry.getKey();
ReferenceType type = key.getType();
List<AbstractRefField> value = entry.getValue();
if (type.isToOne()) {
if (value.get(0).getFetch() == FetchType.LAZY) {
result.put(key, value);
}
} else {
result.put(key, value);
}
}
return result;
// 对单关联和对多关联都使用延迟加载的场合
} else if (excludeReference.isEmpty()) {
return data.getRefFieldsByRef();
// !!今后主流的形式,过滤掉已经合并加载的ref
} else {
Map<Reference, List<AbstractRefField>> result = new HashMap<Reference, List<AbstractRefField>>(
data.getRefFieldsByRef());
for (Reference ref : excludeReference) {
result.remove(ref);
}
return result;
}
}
/**
* 得到定义的class
*
* @param field
* @return
*/
public static AbstractMetadata getTableMeta(Field field) {
Assert.notNull(field);
if (field instanceof MetadataContainer) {
return (AbstractMetadata) ((MetadataContainer) field).getMeta();
}
if (field instanceof Enum) {
// FIXME 这个算法对原始功能是适用的,但当动态扩展等系列功能出现后,适用上有一定问题。
Class<?> c = field.getClass().getDeclaringClass();
Assert.isTrue(IQueryableEntity.class.isAssignableFrom(c),
field + " is not a defined in a IQueryableEntity's meta-model.");
return MetaHolder.getMeta(c.asSubclass(IQueryableEntity.class));
} else {
throw new IllegalArgumentException(
"method 'getTableMeta' doesn't support field type of " + field.getClass());
}
}
/**
* 得到列的完整定义
*
* @param field
* 列的枚举对象
* @return 完整的字段到数据库列的映射信息。
*/
public static ColumnMapping toColumnMapping(Field field) {
if (field instanceof ColumnMapping) {
return (ColumnMapping) field;
} else if (field instanceof MetadataContainer) {
return ((MetadataContainer) field).getMeta().getColumnDef(field);
} else if (field instanceof Enum) {
Class<?> c = field.getClass().getDeclaringClass();
Assert.isTrue(IQueryableEntity.class.isAssignableFrom(c),
field + " is not a defined in a IQueryableEntity's meta-model.");
ITableMetadata meta = MetaHolder.getMeta(c);
return meta.getColumnDef(field);
}
throw new IllegalArgumentException("method 'getTableMeta' doesn't support field type of " + field.getClass());
}
/**
* 根据引用关系字段,填充查询条件
*
* @param bean
* @param rs
* @param query
* @return
*/
protected static boolean appendRefCondition(BeanWrapper bean, JoinPath rs, Query<?> query,
List<Condition> filters) {
query.clearQuery();
boolean hasValue = false;
for (JoinKey r : rs.getJoinKeys()) {
Object value = bean.getPropertyValue(r.getLeft().name());
query.addCondition(r.getRightAsField(), value);
if (value != null)
hasValue = true;
}
// 辅助过滤条件,不作为hasValue标记
for (JoinKey condition : rs.getJoinExpression()) {
Field f = condition.getLeft();
if (f == null) {
continue;
}
if (f instanceof SqlExpression) {
query.addCondition(condition);
continue;
}
if (f instanceof JpqlExpression) {
query.addCondition(condition);
continue;
}
ITableMetadata meta = DbUtils.getTableMeta(f);
if (meta == query.getMeta()) {
query.addCondition(condition);
}
}
if (filters != null) {
Query<?> bq = query;
bq.getConditions().addAll(filters);
}
return hasValue;
}
/**
* 当请求为空时,调用此方法,将有效的主键字段到请求中
*
* @param obj
* @param query
* @param isUpdate
* 当isUpdate为true时,当填充主键条件时,会从updateMap中去除这些字段,
* 避免出现where和set中都对主键进行操作
* @param force
* @return
*/
protected static void fillConditionFromField(IQueryableEntity obj, Query<?> query, UpdateContext update,
boolean force) {
Assert.isTrue(query.getConditions().isEmpty());
ITableMetadata meta = query.getMeta();
boolean isUpdate = update != null;
if (fillPKConditions(obj, meta, query, isUpdate, force)) {
if (isUpdate)
update.setIsPkQuery(true);
return;
}
populateExampleConditions(obj);
}
/*
* (nojava doc)
*/
private static boolean isValidPKValue(IQueryableEntity obj, ITableMetadata meta, ColumnMapping field) {
Class<?> type = field.getFieldAccessor().getType();
Object value = field.getFieldAccessor().get(obj);
if (field.isUnsavedValueDeclared()) {
return !field.isUnsavedValue(value);
} else if (type.isPrimitive()) {
if (field.isUnsavedValue(value)) {
if (meta.getPKFields().size() == 1 && !obj.isUsed(field.field()))
return false;
}
return true;
} else {
return value != null;
}
}
/**
* 判定一个从对象中值是否为有效的数据。 剔除两种情形 1、用户显式指定的非数据库有效值 2、当原生类型时,且无任何证据表明用户对该字段值进行的赋值
*
* @param value
* @param field
* @param isUsed
* @return 如果是无效值 返回true
*/
public static boolean isInvalidValue(Object value, ColumnMapping field, boolean isUsed) {
if (field.isUnsavedValueDeclared()) {
return field.isUnsavedValue(value);
}
// 辅助逻辑,后面看要不要去除此逻辑
// 当字段无标记,并且等于原生值的primitive类型时,视作无效值
if (!isUsed && field.getFieldAccessor().getType().isPrimitive()) {
return field.isUnsavedValue(value);
}
return false;
}
/*
* @param obj 对象
*
* @param meta 元数据
*
* @param wrapper 实例包装
*
* @param query 请求
*
* @param removePkUpdate
*
* @param force
*
* @return
*/
protected static boolean fillPKConditions(IQueryableEntity obj, ITableMetadata meta, Query<?> query,
boolean isUpdate, boolean force) {
if (meta.getPKFields().isEmpty())
return false;
if (!force) {
for (ColumnMapping field : meta.getPKFields()) {
if (!isValidPKValue(obj, meta, field))
return false;
}
}
Map<Field, Object> map = obj.getUpdateValueMap();
for (ColumnMapping mapping : meta.getPKFields()) {
Object value = mapping.getFieldAccessor().get(obj);
Field field = mapping.field();
query.addCondition(field, value);
if (isUpdate && map.containsKey(field)) {//
Object v = map.get(field);
if (Objects.equal(value, v)) {
map.remove(field);
}
}
}
return true;
}
/**
* 将指定对象中除了主键以外的所有字段都作为需要update的字段。(标记为'已修改的') <br>
* 这个方法实际操作时:即除了主键以外的所有字段都放置到updateMap中去
*
* @param <T>
* @param prepareObj
*/
public static <T extends IQueryableEntity> void fillUpdateMap(T... obj) {
if (obj == null || obj.length == 0)
return;
ITableMetadata m = MetaHolder.getMeta(obj[0]);
for (T o : obj) {
BeanWrapper bean = BeanWrapper.wrap(o);
for (ColumnMapping mType : m.getColumns()) {
if (mType.isPk()) {
continue;
}
Field field = mType.field();
o.prepareUpdate(field, bean.getPropertyValue(field.name()), true);
}
}
}
/**
* 数值处理,拼装条件Example条件
*
* @param obj
* @return
*/
@SuppressWarnings("unchecked")
public static <T extends IQueryableEntity> Query<T> populateExampleConditions(T obj, String... properties) {
Query<T> query = (Query<T>) obj.getQuery();
ITableMetadata meta = query.getMeta();
BeanWrapper bw = BeanWrapper.wrap(obj, BeanWrapper.FAST);
if (properties.length == 0) {
for (ColumnMapping mType : meta.getColumns()) {
Field field = mType.field();
if (obj.isUsed(field)) {
Object value = bw.getPropertyValue(field.name());
query.addCondition(field, value);
}
}
} else {
for (String s : properties) {
Field field = meta.getField(s);
if (field == null) {
throw new IllegalArgumentException("field [" + s + "] not found in object " + meta.getName());
}
Object value = bw.getPropertyValue(field.name());
query.addCondition(field, value);
}
}
return query;
}
/**
* 从查询中得到关于对象拼装的映射提示
*
* @param queryObj
* @return
*/
protected static EntityMappingProvider getMappingProvider(ConditionQuery queryObj) {
if (queryObj instanceof JoinElement) {
return ((JoinElement) queryObj).getSelectItems();
}
return null;
}
/**
* 根据对象获得表名,支持分表,允许返回多表,主要用于查询中
*
* @param name
* @param needTranslate
* @return
*/
public static PartitionResult[] toTableNames(IQueryableEntity obj, String customName, Query<?> q,
PartitionSupport processor) {
AbstractMetadata meta = q == null ? MetaHolder.getMeta(obj) : (AbstractMetadata) q.getMeta();
if (StringUtils.isNotEmpty(customName))
return new PartitionResult[] { new PartitionResult(customName).setDatabase(meta.getBindDsName()) };
PartitionResult[] result = partitionUtil.toTableNames(meta, obj, q, processor,
ORMConfig.getInstance().isFilterAbsentTables());
// if(ORMConfig.getInstance().isDebugMode()){
// LogUtil.show("Partitions:"+Arrays.toString(result));
// }
return result;
}
/**
* 分表和路由计算,在没有对象实例的情况下计算路由,这个计算将会返回所有可能的表名组合
*
*
* @param meta
* 元数据描述
* @param processor
* @param operateType
* 计算表名所用的操作。0基表 1 不含基表 2 分表+基表 3 数据库中的存在表(不含基表) 4所有存在的表
* 影响效果——建表的多寡。
*
*
* @return
*/
public static PartitionResult[] toTableNames(ITableMetadata meta, PartitionSupport processor, int operateType) {
Assert.notNull(meta);
// long start=System.nanoTime();
// try{
return partitionUtil.toTableNames((AbstractMetadata) meta, processor, operateType);
// }finally{
// System.out.println((System.nanoTime()-start)/1000+"us");
// }
}
/**
* 根据对象获得表名,支持分表,返回单表,主要用与插入更新中
*
* @param obj
* @param customName
* @param q
* @param profile
* @return
*/
public static PartitionResult toTableName(IQueryableEntity obj, String customName, Query<?> q,
PartitionSupport profile) {
AbstractMetadata meta = obj == null ? (AbstractMetadata) q.getMeta() : MetaHolder.getMeta(obj);
if (StringUtils.isNotEmpty(customName))
return new PartitionResult(customName).setDatabase(meta.getBindDsName());
PartitionResult result = partitionUtil.toTableName(meta, obj, q, profile);
Assert.notNull(result);
return result;
}
// /**
// * 判断两个dbkey指向的是否为相同的物理数据库
// * 对于相同rac组的认为是同一物理库
// * @param dbkey,anotherDbKey都为空时,返回true
// * @return
// */
// public static boolean isSameDb(String dbkey,String anotherDbKey){
// if(StringUtils.isEmpty(dbkey)&&StringUtils.isEmpty(anotherDbKey)){
// return true;
// }
// if(StringUtils.isEmpty(dbkey))
// return false;
// if(dbkey.equalsIgnoreCase(anotherDbKey))
// return true;
// String racId = getRacId(dbkey);
// String anotherRacId = getRacId(anotherDbKey);
//
// return
// racId!=null&&!String.valueOf(NO_RAC_ID).equals(racId)&&racId.equalsIgnoreCase(anotherRacId);
// }
/**
* 安静的关闭结果集
*
* @param rs
*/
public static void close(ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
}
}
}
/**
* 关闭指定的Statement
*
* @param st
*/
public static void close(Statement st) {
try {
if (st != null)
st.close();
} catch (SQLException e) {
}
}
/**
* 将异常包装为RuntimeException
*
* @param e
* @return
*/
public static PersistenceException toRuntimeException(SQLException e) {
String s = e.getSQLState();
if (e instanceof SQLIntegrityConstraintViolationException) {
return new EntityExistsException(e);
} else if (e instanceof SQLTimeoutException) {
return new QueryTimeoutException(s, e);
}
return new PersistenceException(s, e);
}
/**
* 将异常包装为Runtime异常
*
* @param e
* @return
*/
public static RuntimeException toRuntimeException(Throwable e) {
while (true) {
if (e instanceof RuntimeException) {
return (RuntimeException) e;
}
if (e instanceof InvocationTargetException) {
e = e.getCause();
continue;
}
if (e instanceof Error) {
throw (Error) e;
}
if (e instanceof SQLException) {
return toRuntimeException((SQLException) e);
}
return new IllegalStateException(e);
}
}
private static final String DEFAULT_SEQUENCE_PATTERN = "S_%s";
private static final int TABLE_NAME_MAX_LENGTH = 26;
public static String calcSeqNameByTable(String schema, String tableName, String columnName) {
String pattern = JefConfiguration.get(DbCfg.SEQUENCE_NAME_PATTERN);
if (StringUtils.isBlank(pattern))
pattern = DEFAULT_SEQUENCE_PATTERN;
String tblName = tableName;
if (tblName.length() > TABLE_NAME_MAX_LENGTH) {
tblName = tblName.substring(0, TABLE_NAME_MAX_LENGTH);
}
if (schema == null) {
return StringUtils.upperCase(String.format(pattern, tblName));
} else {
String name = String.format(pattern, tblName);
return new StringBuilder(schema.length() + name.length() + 1).append(schema).append('.').append(name)
.toString().toUpperCase();
}
}
// TODO 关于Oracle RAC模式下的URL简化问题
// public static String getSimpleUrl(String url) {
// if (url.toLowerCase().indexOf("service_name") > -1) {
// StringBuilder sb=new StringBuilder();
// StringTokenizer st=new StringTokenizer(url,"()");
// while(st.hasMoreTokens()){
// String str=st.nextToken();
// String x=str.toUpperCase();
// if(x.startsWith("SERVICE_NAME") || x.startsWith("HOST")){
// sb.append('(').append(str).append(')');
// }
// }
// url = sb.toString();
// }
// return url;
// }
/**
* 使用JDBC URL等构造出一个datasource对象,构造前能自动查找驱动类名称并注册
*
* @param url
* @param user
* @param password
* @return
*/
public static SimpleDataSource createSimpleDataSource(String url, String user, String password) {
SimpleDataSource s = new SimpleDataSource();
s.setUsername(user);
s.setUrl(url);
s.setPassword(password);
return s;
}
/**
* 得到继承上级所指定的泛型类型
*
* @param subclass
* @param superclass
* @return
*/
public static Type[] getTypeParameters(Class<?> subclass, Class<?> superclass) {
if (superclass == null) {// 在没有指定父类的情况下,默认选择第一个接口
if (subclass.getSuperclass() == Object.class && subclass.getInterfaces().length > 0) {
superclass = subclass.getInterfaces()[0];
} else {
superclass = subclass.getSuperclass();
}
}
Type type = GenericUtils.getSuperType(null, subclass, superclass);
if (type instanceof ParameterizedType) {
return ((ParameterizedType) type).getActualTypeArguments();
}
throw new RuntimeException("Can not get the generic param type for class:" + subclass.getName());
}
/**
* 通过比较两个对象,在旧对象中准备更新Map
*
* @param <T>
* @param changedObj
* @param oldObj
* @throws SQLException
* @return the object who is able to update.
*/
public static <T extends IQueryableEntity> T compareToUpdateMap(T changedObj, T oldObj) {
Assert.isTrue(Objects.equal(DbUtils.getPrimaryKeyValue(changedObj), DbUtils.getPKValueSafe(oldObj)),
"For consistence, the two parameter must hava equally primary keys.");
ITableMetadata m = MetaHolder.getMeta(oldObj);
boolean safeMerge = ORMConfig.getInstance().isSafeMerge();
for (ColumnMapping mType : m.getColumns()) {
if (mType.isPk())
continue;
Field field = mType.field();
Object value = mType.getFieldAccessor().get(changedObj);
boolean used = changedObj.isUsed(field);
if (mType.isGenerated() && !used) {
continue;
}
// 安全更新下,发现字段数值无效,跳过
if (safeMerge && DbUtils.isInvalidValue(value, mType, used)) {
continue;
}
Object oldValue = mType.getFieldAccessor().get(oldObj);
if (!ObjectUtils.equals(value, oldValue)) {
oldObj.prepareUpdate(field, value);
}
}
return oldObj;
}
}