/** * Copyright 2014 Duan Bingnan * * 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 org.pinus4j.api; import java.util.List; import java.util.Map; import java.util.concurrent.locks.Lock; import javax.transaction.TransactionManager; import org.pinus4j.api.query.IQuery; import org.pinus4j.api.query.impl.ResultSetableQueryImpl; import org.pinus4j.cache.IPrimaryCache; import org.pinus4j.cache.ISecondCache; import org.pinus4j.cluster.IDBCluster; import org.pinus4j.cluster.IDBClusterBuilder; import org.pinus4j.cluster.beans.IShardingKey; import org.pinus4j.cluster.enums.EnumDBMasterSlave; import org.pinus4j.cluster.enums.EnumSyncAction; import org.pinus4j.cluster.impl.DefaultDBClusterBuilder; import org.pinus4j.constant.Const; import org.pinus4j.datalayer.IDataLayerBuilder; import org.pinus4j.datalayer.JdbcDataLayerBuilder; import org.pinus4j.datalayer.query.IGlobalQuery; import org.pinus4j.datalayer.query.IShardingQuery; import org.pinus4j.datalayer.update.IGlobalUpdate; import org.pinus4j.datalayer.update.IShardingUpdate; import org.pinus4j.entity.DefaultEntityMetaManager; import org.pinus4j.entity.IEntityMetaManager; import org.pinus4j.entity.meta.EntityPK; import org.pinus4j.exceptions.DBClusterException; import org.pinus4j.exceptions.DBOperationException; import org.pinus4j.generator.IIdGenerator; import org.pinus4j.task.ITask; import org.pinus4j.task.TaskExecutor; import org.pinus4j.task.TaskFuture; import org.pinus4j.transaction.enums.EnumTransactionIsolationLevel; import org.pinus4j.transaction.impl.BestEffortsOnePCJtaTransaction; import org.pinus4j.transaction.impl.BestEffortsOnePCJtaTransactionManager; import org.pinus4j.utils.BeansUtil; import org.pinus4j.utils.CheckUtil; import org.pinus4j.utils.StringUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Lists; import com.google.common.collect.Maps; /** * default main api implement. * * @author duanbn */ public class DefaultPinusClient implements PinusClient { /** * 日志. */ public static final Logger LOG = LoggerFactory.getLogger(DefaultPinusClient.class); /** * reference it self; */ public static PinusClient instance; /** * 同步数据表操作. */ private EnumSyncAction syncAction = EnumSyncAction.CREATE; /** * 扫描数据对象的包. 数据对象是使用了@Table注解的javabean. */ private String scanPackage; /** * id generator. */ private IIdGenerator idGenerator; /** * ref of db cluster. */ protected IDBCluster dbCluster; /** * transaction manager. */ private TransactionManager txManager; /** * global updater. */ private IGlobalUpdate globalUpdater; /** * global query. */ private IGlobalQuery globalQuery; /** * 分库分表更新实现. */ private IShardingUpdate shardingUpdater; /** * sharding query. */ private IShardingQuery shardingQuery; private IEntityMetaManager entityMetaManager = DefaultEntityMetaManager.getInstance(); /** * init method */ public void init() { IDBClusterBuilder dbClusterBuilder = new DefaultDBClusterBuilder(); dbClusterBuilder.setScanPackage(this.scanPackage); dbClusterBuilder.setSyncAction(this.syncAction); this.dbCluster = dbClusterBuilder.build(); // set id generator this.idGenerator = this.dbCluster.getIdGenerator(); // set transaction manager this.txManager = this.dbCluster.getTransactionManager(); // // 初始化分库分表增删改查实现. // IDataLayerBuilder dataLayerBuilder = JdbcDataLayerBuilder.valueOf(dbCluster); dataLayerBuilder.setPrimaryCache(this.dbCluster.getPrimaryCache()); dataLayerBuilder.setSecondCache(this.dbCluster.getSecondCache()); this.globalUpdater = dataLayerBuilder.buildGlobalUpdate(this.dbCluster.getIdGenerator()); this.globalQuery = dataLayerBuilder.buildGlobalQuery(); this.shardingUpdater = dataLayerBuilder.buildShardingUpdate(this.dbCluster.getIdGenerator()); this.shardingQuery = dataLayerBuilder.buildShardingQuery(); // FashionEntity dependency this. instance = this; } @Override public void destroy() { // close database cluster. try { this.dbCluster.shutdown(); } catch (DBClusterException e) { throw new RuntimeException(e); } } // //////////////////////////////////////////////////////// // 事务相关 // //////////////////////////////////////////////////////// @Override public void beginTransaction() { beginTransaction(EnumTransactionIsolationLevel.READ_COMMITTED); } @Override public void beginTransaction(EnumTransactionIsolationLevel txLevel) { ((BestEffortsOnePCJtaTransactionManager) this.txManager).setTransactionIsolationLevel(txLevel); try { this.txManager.begin(); } catch (Exception e) { throw new DBOperationException(e); } } @Override public void commit() { try { this.txManager.commit(); } catch (Exception e) { throw new DBOperationException(e); } } @Override public void rollback() { try { this.txManager.rollback(); } catch (Exception e) { throw new DBOperationException(e); } } @Override public void flush() { try { BestEffortsOnePCJtaTransaction localTx = (BestEffortsOnePCJtaTransaction) this.txManager.getTransaction(); if (localTx != null) localTx.flush(); } catch (Exception e) { throw new DBOperationException(e); } } // //////////////////////////////////////////////////////// // 数据处理相关 // //////////////////////////////////////////////////////// @Override public <T> TaskFuture submit(ITask<T> task, Class<T> clazz) { TaskExecutor<T> taskExecutor = new TaskExecutor<T>(clazz, this.dbCluster); return taskExecutor.execute(task); } @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public <T> TaskFuture submit(ITask<T> task, Class<T> clazz, IQuery<T> query) { TaskExecutor taskExecutor = new TaskExecutor(clazz, this.dbCluster); return taskExecutor.execute(task, query); } // //////////////////////////////////////////////////////// // 数据操作相关 // //////////////////////////////////////////////////////// @Override public void save(Object entity) { if (entity == null) { throw new IllegalArgumentException("param should not be null"); } Class<?> clazz = entity.getClass(); if (entityMetaManager.isShardingEntity(clazz)) { CheckUtil.checkShardingEntity(entity); IShardingKey<?> sk = entityMetaManager.getShardingKey(entity); CheckUtil.checkShardingKey(sk); this.shardingUpdater.save(entity, sk); } else { CheckUtil.checkGlobalEntity(entity); String clusterName = entityMetaManager.getClusterName(entity.getClass()); CheckUtil.checkClusterName(clusterName); this.globalUpdater.save(entity, clusterName); } } @Override public void saveBatch(List<? extends Object> entityList) { saveBatch(entityList, true); } @Override public void saveBatch(List<? extends Object> entityList, boolean autoGeneratedKeys) { if (entityList == null) { throw new IllegalArgumentException("param should not be null"); } List<Object> globalList = Lists.newArrayList(); List<Object> shardingList = Lists.newArrayList(); for (Object entity : entityList) { if (entityMetaManager.isShardingEntity(entity.getClass())) { shardingList.add(entity); } else { globalList.add(entity); } } // handle global entity list. if (!globalList.isEmpty()) { Map<String, List<Object>> clusterEntityMap = Maps.newHashMap(); String clusterName = null; for (Object globalEntity : globalList) { clusterName = entityMetaManager.getClusterName(globalEntity.getClass()); List<Object> theSameClusterNameList = clusterEntityMap.get(clusterName); if (theSameClusterNameList != null) { theSameClusterNameList.add(globalEntity); } else { theSameClusterNameList = Lists.newArrayList(globalEntity); clusterEntityMap.put(clusterName, theSameClusterNameList); } } for (Map.Entry<String, List<Object>> entry : clusterEntityMap.entrySet()) { this.globalUpdater.saveBatch(entry.getValue(), entry.getKey(), autoGeneratedKeys); } } // handle sharding entity list. if (!shardingList.isEmpty()) { Map<IShardingKey<?>, List<Object>> theSameShardingKeyMap = Maps.newHashMap(); IShardingKey<?> shardingKey = null; for (Object shardingEntity : shardingList) { shardingKey = entityMetaManager.getShardingKey(shardingEntity); List<Object> theSameShardingKeyList = theSameShardingKeyMap.get(shardingKey); if (theSameShardingKeyList != null) { theSameShardingKeyList.add(shardingEntity); } else { theSameShardingKeyList = Lists.newArrayList(shardingEntity); theSameShardingKeyMap.put(shardingKey, theSameShardingKeyList); } } for (Map.Entry<IShardingKey<?>, List<Object>> entry : theSameShardingKeyMap.entrySet()) { this.shardingUpdater.saveBatch(entry.getValue(), entry.getKey(), autoGeneratedKeys); } } } @Override public void update(Object entity) { if (entity == null) { throw new IllegalArgumentException("param should not be null"); } Class<?> clazz = entity.getClass(); if (entityMetaManager.isShardingEntity(clazz)) { CheckUtil.checkShardingEntity(entity); IShardingKey<?> sk = entityMetaManager.getShardingKey(entity); CheckUtil.checkShardingKey(sk); this.shardingUpdater.update(entity, sk); } else { CheckUtil.checkGlobalEntity(entity); String clusterName = entityMetaManager.getClusterName(entity.getClass()); CheckUtil.checkClusterName(clusterName); this.globalUpdater.update(entity, clusterName); } } @Override public void updateBatch(List<? extends Object> entityList) { if (entityList == null) { throw new IllegalArgumentException("param should not be null"); } List<Object> globalList = Lists.newArrayList(); List<Object> shardingList = Lists.newArrayList(); for (Object entity : entityList) { if (entityMetaManager.isShardingEntity(entity.getClass())) { shardingList.add(entity); } else { globalList.add(entity); } } // handle global entity list. if (!globalList.isEmpty()) { Map<String, List<Object>> theSameClusterNameMap = Maps.newHashMap(); String clusterName = null; for (Object globalEntity : globalList) { clusterName = entityMetaManager.getClusterName(globalEntity.getClass()); List<Object> theSameClusterNameList = theSameClusterNameMap.get(clusterName); if (theSameClusterNameList != null) { theSameClusterNameList.add(globalEntity); } else { theSameClusterNameList = Lists.newArrayList(globalEntity); theSameClusterNameMap.put(clusterName, theSameClusterNameList); } } for (Map.Entry<String, List<Object>> entry : theSameClusterNameMap.entrySet()) { this.globalUpdater.updateBatch(entry.getValue(), entry.getKey()); } } // handle sharding entity list. if (!shardingList.isEmpty()) { Map<IShardingKey<?>, List<Object>> theSameShardingKeyMap = Maps.newHashMap(); IShardingKey<?> shardingKey = null; for (Object shardingEntity : shardingList) { shardingKey = entityMetaManager.getShardingKey(shardingEntity); List<Object> theSameShardingKeyList = theSameShardingKeyMap.get(shardingKey); if (theSameShardingKeyList != null) { theSameShardingKeyList.add(shardingEntity); } else { theSameShardingKeyList = Lists.newArrayList(shardingEntity); theSameShardingKeyMap.put(shardingKey, theSameShardingKeyList); } } for (Map.Entry<IShardingKey<?>, List<Object>> entry : theSameShardingKeyMap.entrySet()) { this.shardingUpdater.updateBatch(entry.getValue(), entry.getKey()); } } } @Override public void delete(Object entity) { if (entity == null) { throw new IllegalArgumentException("param should not be null"); } Class<?> clazz = entity.getClass(); if (entityMetaManager.isShardingEntity(clazz)) { CheckUtil.checkShardingEntity(entity); EntityPK entityPk = entityMetaManager.getEntityPK(entity); IShardingKey<?> shardingKey = entityMetaManager.getShardingKey(entity); this.shardingUpdater.removeByPk(entityPk, shardingKey, clazz); } else { CheckUtil.checkGlobalEntity(entity); EntityPK entityPk = entityMetaManager.getEntityPK(entity); String clusterName = entityMetaManager.getClusterName(clazz); this.globalUpdater.removeByPk(entityPk, clazz, clusterName); } } @Override public void delete(List<? extends Object> entityList) { if (entityList == null) { throw new IllegalArgumentException("param should not be null"); } List<Object> globalList = Lists.newArrayList(); List<Object> shardingList = Lists.newArrayList(); for (Object entity : entityList) { if (entityMetaManager.isShardingEntity(entity.getClass())) { shardingList.add(entity); } else { globalList.add(entity); } } // handle global entity list. if (!globalList.isEmpty()) { // filter the same cluster name Map<String, List<Object>> theSameClusterNameMap = Maps.newHashMap(); String clusterName = null; for (Object globalEntity : globalList) { clusterName = entityMetaManager.getClusterName(globalEntity.getClass()); List<Object> theSameClusterNameList = theSameClusterNameMap.get(clusterName); if (theSameClusterNameList != null) { theSameClusterNameList.add(globalEntity); } else { theSameClusterNameList = Lists.newArrayList(globalEntity); theSameClusterNameMap.put(clusterName, theSameClusterNameList); } } // filter the same class for (Map.Entry<String, List<Object>> sameClusterNameEntry : theSameClusterNameMap.entrySet()) { Map<Class<?>, List<Object>> theSameClassMap = Maps.newHashMap(); Class<?> clazz = null; for (Object sameClusterNameEntity : sameClusterNameEntry.getValue()) { clazz = sameClusterNameEntity.getClass(); List<Object> theSameClassList = theSameClassMap.get(clazz); if (theSameClassList != null) { theSameClassList.add(sameClusterNameEntity); } else { theSameClassMap.put(clazz, Lists.newArrayList(sameClusterNameEntity)); } // do delete for (Map.Entry<Class<?>, List<Object>> sameClassEntry : theSameClassMap.entrySet()) { List<EntityPK> pks = Lists.newArrayList(); for (Object sameClassEntity : sameClassEntry.getValue()) { pks.add(entityMetaManager.getEntityPK(sameClassEntity)); } this.globalUpdater.removeByPks(pks, sameClassEntry.getKey(), sameClusterNameEntry.getKey()); } } } } // handle sharding entity list. if (!shardingList.isEmpty()) { // filter the same sharding key Map<IShardingKey<?>, List<Object>> theSameShardingKeyMap = Maps.newHashMap(); IShardingKey<?> shardingKey = null; for (Object shardingEntity : shardingList) { shardingKey = entityMetaManager.getShardingKey(shardingEntity); List<Object> theSameShardingKeyList = theSameShardingKeyMap.get(shardingKey); if (theSameShardingKeyList != null) { theSameShardingKeyList.add(shardingEntity); } else { theSameShardingKeyList = Lists.newArrayList(shardingEntity); theSameShardingKeyMap.put(shardingKey, theSameShardingKeyList); } } // filter the same class for (Map.Entry<IShardingKey<?>, List<Object>> sameShardingKeyEntry : theSameShardingKeyMap.entrySet()) { Map<Class<?>, List<Object>> theSameClassMap = Maps.newHashMap(); Class<?> clazz = null; for (Object sameShardingKeyEntity : sameShardingKeyEntry.getValue()) { clazz = sameShardingKeyEntity.getClass(); List<Object> theSameClassList = theSameClassMap.get(clazz); if (theSameClassList != null) { theSameClassList.add(sameShardingKeyEntity); } else { theSameClassMap.put(clazz, Lists.newArrayList(sameShardingKeyEntity)); } // do delete for (Map.Entry<Class<?>, List<Object>> sameClassEntry : theSameClassMap.entrySet()) { List<EntityPK> pks = Lists.newArrayList(); for (Object sameClassEntity : sameClassEntry.getValue()) { pks.add(entityMetaManager.getEntityPK(sameClassEntity)); } this.shardingUpdater.removeByPks(pks, sameShardingKeyEntry.getKey(), sameClassEntry.getKey()); } } } } } @Override public void load(Object entity) { load(entity, true, EnumDBMasterSlave.AUTO); } @Override public void load(Object entity, boolean useCache) { load(entity, useCache, EnumDBMasterSlave.AUTO); } @Override public void load(Object entity, EnumDBMasterSlave masterSlave) { load(entity, true, masterSlave); } @Override public void load(Object entity, boolean useCache, EnumDBMasterSlave masterSlave) { if (entity == null) { throw new IllegalArgumentException("param should not be null"); } EntityPK entityPk = entityMetaManager.getEntityPK(entity); Object loadEntity = null; Class<?> clazz = entity.getClass(); if (entityMetaManager.isShardingEntity(clazz)) { IShardingKey<?> shardingKey = entityMetaManager.getShardingKey(entity); loadEntity = this.shardingQuery.findByPk(entityPk, shardingKey, clazz, useCache, masterSlave); } else { loadEntity = this.globalQuery.findByPk(entityPk, clazz, useCache, masterSlave); } if (loadEntity == null) { throw new DBOperationException("找不到记录, pk=" + entityPk); } try { BeansUtil.copyProperties(loadEntity, entity); } catch (Exception e) { throw new DBOperationException(e); } } @Override public <T> IQuery<T> createQuery(Class<T> clazz) { ResultSetableQueryImpl<T> query = new ResultSetableQueryImpl<T>(clazz); query.setGlobalQuery(this.globalQuery); query.setShardingQuery(this.shardingQuery); return query; } @Override public List<Map<String, Object>> findBySQL(SQL sql, Class<?> clazz) { if (sql == null) { throw new IllegalArgumentException("param sql should not be null"); } if (clazz == null) { throw new IllegalArgumentException("param class should not be null"); } CheckUtil.checkSQL(sql); CheckUtil.checkClass(clazz); String clusterName = entityMetaManager.getClusterName(clazz); if (entityMetaManager.isShardingEntity(clazz)) { return this.shardingQuery.findBySql(sql, EnumDBMasterSlave.AUTO); } else { return this.globalQuery.findBySql(sql, clusterName, EnumDBMasterSlave.AUTO); } } @Override public List<Map<String, Object>> findBySQL(SQL sql, String clusterName) { if (sql == null) { throw new IllegalArgumentException("param sql should not be null"); } CheckUtil.checkSQL(sql); return this.globalQuery.findBySql(sql, clusterName, EnumDBMasterSlave.AUTO); } @Override public IDBCluster getDBCluster() { return this.dbCluster; } @Override public IPrimaryCache getPrimaryCache() { return this.dbCluster.getPrimaryCache(); } @Override public ISecondCache getSecondCache() { return this.dbCluster.getSecondCache(); } @Override public int genClusterUniqueIntId(String name) { return this.idGenerator.genClusterUniqueIntId(Const.ZK_SEQUENCE, name); } @Override public long genClusterUniqueLongId(String name) { return this.idGenerator.genClusterUniqueLongId(Const.ZK_SEQUENCE, name); } @Override public long[] genClusterUniqueLongIdBatch(String name, int batchSize) { return this.idGenerator.genClusterUniqueLongIdBatch(Const.ZK_SEQUENCE, name, batchSize); } @Override public int[] genClusterUniqueIntIdBatch(String name, int batchSize) { return this.idGenerator.genClusterUniqueIntIdBatch(Const.ZK_SEQUENCE, name, batchSize); } @Override public Lock createLock(String lockName) { return this.dbCluster.createLock(lockName); } public EnumSyncAction getSyncAction() { return syncAction; } @Override public void setSyncAction(EnumSyncAction syncAction) { this.syncAction = syncAction; } public String getScanPackage() { return scanPackage; } @Override public void setScanPackage(String scanPackage) { if (StringUtil.isBlank(scanPackage)) { throw new IllegalArgumentException("参数错误,参数不能为空"); } this.scanPackage = scanPackage; } }