/** * Helper method to set the first of specified PersistentEntity instances into the * ShardedEntityContextHolder if it is of type ShardedPersistentEntity. Also checks to see if all * the ShardedPersistentEntity instances belong to the same shard i.e. {@link * ShardedPersistentEntity#getShardHint()} returns the same value. Throws a PersistenceException * otherwise. This defensive check is done to avoid indeterministic transaction boundary behavior * when shards are mapped to multiple database instances. * * @param entities the PersistentEntity instances, the first of which is to be set into the * ShardedEntityContextHolder if it is of type ShardedPersistentEntity * @throws PersistenceException in case the shard hints of specified ShardedPersistentEntity * instances do not match */ private void checkAndPopulateShardedEntityContextHolder(PersistentEntity[] entities) throws PersistenceException { if (entities.length != 0) { // check to see if sharded and non-sharded entities are part of the same call ShardedEntity shardedEntity = null; PersistentEntity nonShardedEntity = null; for (PersistentEntity entity : entities) { if (ShardedEntity.class.isAssignableFrom(entity.getClass())) { shardedEntity = (ShardedEntity) entity; } else { nonShardedEntity = entity; } } if (shardedEntity != null && nonShardedEntity != null) { throw new PersistenceException( "Attempt to use sharded and non-sharded persistent entities in the same call. " + "Mixed types are : " + shardedEntity.getClass().getName() + "," + nonShardedEntity.getClass().getName()); } PersistentEntity firstEntity = entities[0]; String shardHint = (ShardedEntity.class.isAssignableFrom(firstEntity.getClass())) ? ((ShardedEntity) firstEntity).getShardHint() : ShardedEntity.DEFAULT_SHARD; for (PersistentEntity entity : entities) { if (ShardedEntity.class.isAssignableFrom(entity.getClass())) { if (!((ShardedEntity) entity).getShardHint().equals(shardHint)) { throw new PersistenceException( "Shard hints do not match for the specified ShardedEntity instances." + " Mismatched values are : " + shardHint + "," + ((ShardedEntity) entity).getShardHint()); } } // Set the PersistentEntity into the ShardedEntityContextHolder if the type is // ShardedPersistentEntity. Will be used in Datasource resolution if (ShardedEntity.class.isAssignableFrom(firstEntity.getClass())) { ShardedEntityContextHolder.setShardedEntity((ShardedEntity) firstEntity); } } } }
/** * Helper method to validate multi-sharded persistent entities passed into a persistence call * * @param multiShardedEntity the AbstractMultiShardedPersistentEntity to be used for validation * @param entities the list of PersistentEntity to be validated for multi-shard persistence * @throws PersistenceException in case of errors in count or value of shard-hints of the * persistent entities being validated */ private void checkValidityOfMultiShardedPersistentities( AbstractMultiShardedPersistentEntity multiShardedEntity, PersistentEntity[] entities) throws PersistenceException { for (PersistentEntity listPersistentEntity : entities) { if (!AbstractMultiShardedPersistentEntity.class.isAssignableFrom( listPersistentEntity.getClass())) { // attempt to mix multi sharded persistent entity with other types in same persistence call throw new PersistenceException( "Attempt to persist Multi-sharded persistent entity with other types. Multi-sharded entity found of type : (" + multiShardedEntity.getEntityName() + ")" + multiShardedEntity.getClass().getName() + " .One of the other types is : (" + listPersistentEntity.getEntityName() + ")" + listPersistentEntity.getClass().getName()); } // now check if the shard hint count and value match, else throw an exception AbstractMultiShardedPersistentEntity listMultiShardedEntity = (AbstractMultiShardedPersistentEntity) listPersistentEntity; if (multiShardedEntity.getShardHints().length != listMultiShardedEntity.getShardHints().length) { throw new PersistenceException( "Multi-shard count mismatch between entities - (" + multiShardedEntity.getEntityName() + ")and(" + listPersistentEntity.getEntityName() + ")"); } // also check if the individual shard hint values match, else throw an exception for (int i = 0; i < multiShardedEntity.getShardHints().length; i++) { if (!multiShardedEntity.getShardHints()[i].equalsIgnoreCase( listMultiShardedEntity.getShardHints()[i])) { throw new PersistenceException( "Multi-shard values mismatch between entities - (" + multiShardedEntity.getEntityName() + ")and(" + listPersistentEntity.getEntityName() + ")"); } } } }
/** * Interface method implementation. This method is transactional by default and implemented by * calling {@link PersistenceDelegate#makePersistent(PersistentEntity[], PersistenceProvider[])} * For multi-sharded entities, the TX guarantee is limited to per shard and DOES NOT span across * multiple shards. * * @see PersistenceManager#makePersistent(PersistentEntity[]) */ public PersistentEntity[] makePersistent(PersistentEntity[] entities) throws PersistenceException { if (null == entities || entities.length == 0) { // If no entities to persist then return whatever is passed and do not execute rest of the // code. return entities; } entities = filterNullEntities(entities); // place holder for the multi-sharded entity, if any, that has been passed into this persistence // call AbstractMultiShardedPersistentEntity multiShardedEntity = null; for (PersistentEntity persistentEntity : entities) { // For Multi sharded persistent entities, check if all are multi-sharded and have the same // shard count and values if (AbstractMultiShardedPersistentEntity.class.isAssignableFrom( persistentEntity.getClass())) { multiShardedEntity = (AbstractMultiShardedPersistentEntity) persistentEntity; // check for validity of multi-sharded persistent entities checkValidityOfMultiShardedPersistentities(multiShardedEntity, entities); } } if (multiShardedEntity != null) { // data is valid. Iterate through shards and invoke the persistence call on the delegate // once for each retrieved shard hint for (String shardHint : multiShardedEntity.getShardHints()) { // set the returned shards one at a time and make persistence calls on the delegate for (PersistentEntity entityToPersist : entities) { AbstractMultiShardedPersistentEntity shardedEntityToPersist = (AbstractMultiShardedPersistentEntity) entityToPersist; shardedEntityToPersist.setShardHint(shardHint); } checkAndPopulateShardedEntityContextHolder(entities); entities = this.persistenceDelegate.makePersistent(entities, findSuitableProviders(entities)); // unset the context using the first entity checkAndUnsetShardedEntityContextHolder(entities[0]); } // return the persisted multi-sharded entities return entities; } // none of the passed in entities are multi-sharded. Proceed to deal with single sharded (or // none) entities checkAndPopulateShardedEntityContextHolder(entities); entities = this.persistenceDelegate.makePersistent(entities, findSuitableProviders(entities)); // unset the context using the first entity checkAndUnsetShardedEntityContextHolder(entities[0]); return entities; }
/** Helper method to locate the PersistenceProvider for the specified PersistentEntity */ private PersistenceProvider findSuitableProvider(PersistentEntity entity) throws PersistenceException { return findSuitableProvider(entity.getClass()); }