@Override
  public List<AlternativeGenerator> getAlternativeGenerators() {
    List<AlternativeGenerator> generators = CollectionsUtil.newArrayList(10);
    generators.add(
        new AlternativeGenerator() {
          @Override
          public String getName() {
            return "sequence";
          }

          @Override
          public void generate(
              List<Map<String, String>> objectDef, AbstractDbObjectParser context) {
            if (CollectionUtils.isEmpty(objectDef)) return;

            try {
              FileUtils.write(
                  new File(context.getOutputDirectory(), "01_CREATE_SEQUENCES.sql"),
                  "create TABLE sequences("
                      + "name varchar(255) primary key,"
                      + "seq BIGINT default 1);");
            } catch (IOException e) {
              throw new IORuntimeException(e);
            }
          }
        });
    return generators;
  }
Exemplo n.º 2
0
/**
 * 期待値を読み込む実装クラスです。
 *
 * <p>期待値はExcelから読み込みます。
 *
 * @author taedium
 */
public class ExpectedDataReaderImpl implements ExpectedDataReader {

  /** ロガー */
  protected static final Logger logger = Logger.getLogger(ExpectedDataReaderImpl.class);

  /** 期待値が記述されたExcelのパスのリスト */
  protected final List<String> expectedDataXlsPaths = CollectionsUtil.newArrayList();

  /** データアクセッサー */
  protected DataAccessor dataAccessor;

  /**
   * データアクセッサーを設定します。
   *
   * @param dataAccessor データアクセッサー
   */
  @Binding(bindingType = BindingType.MUST)
  public void setDataAccessor(final DataAccessor dataAccessor) {
    this.dataAccessor = dataAccessor;
  }

  /**
   * 期待値が記述されたExcelのパスを登録します。
   *
   * @param path 期待値が記述されたExcelのパス
   */
  public void addExpectedDataXlsPath(final String path) {
    expectedDataXlsPaths.add(path);
  }

  public DataSet read(TestContext testContext) {
    final String dirPath = testContext.getTestClassPackagePath();
    final boolean trimString = testContext.isTrimString();
    for (final String path : expectedDataXlsPaths) {
      if (ResourceUtil.isExist(path)) {
        return readXls(path, trimString);
      }
      final String newPath = dirPath + "/" + path;
      if (ResourceUtil.isExist(newPath)) {
        return readXls(newPath, trimString);
      }
    }
    return null;
  }

  /**
   * 指定されたExcelを読みデータセットとして返します。
   *
   * @param path Excelのパス
   * @param trimString 文字列に含まれる空白を取り除く場合<code>true</code>
   * @return Excel内のデータのデータセット表現
   */
  protected DataSet readXls(final String path, final boolean trimString) {
    if (logger.isDebugEnabled()) {
      logger.log("DSSR0104", new Object[] {path});
    }
    return dataAccessor.readXls(path, trimString);
  }
}
 /**
  * IDコンテキストを返します。
  *
  * @param jdbcManager 内部的なJDBCマネージャ
  * @return IDコンテキスト
  */
 protected IdContext getIdContext(final JdbcManagerImplementor jdbcManager) {
   final String dsName = jdbcManager.getSelectableDataSourceName();
   final String key = dsName != null ? dsName : DEFAULT_ID_CONTEXT_KEY;
   final IdContext context = idContextMap.get(key);
   if (context != null) {
     return context;
   }
   return CollectionsUtil.putIfAbsent(idContextMap, key, new IdContext());
 }
Exemplo n.º 4
0
 /**
  * {@link #ignoreWhitespace()}が呼び出された場合で パラメータ値の要素が空文字列または空白のみの文字列なら <code>null</code>、
  * それ以外なら元の値からなるリストを返します。
  *
  * @param values パラメータ値のコレクション
  * @return {@link #ignoreWhitespace()}が呼び出された場合でパラメータ値の要素が空文字列または空白のみの文字列なら <code>null</code>、
  *     それ以外なら元の値からなるリスト
  */
 protected Collection<?> normalizeList(final Collection<?> values) {
   if (!excludesWhitespace || values == null) {
     return values;
   }
   final List<Object> list = CollectionsUtil.newArrayList(values.size());
   for (final Object value : values) {
     final Object normalizedValue = normalize(value);
     if (normalizedValue != null) {
       list.add(normalizedValue);
     }
   }
   return list;
 }
Exemplo n.º 5
0
 /**
  * {@link #ignoreWhitespace()}が呼び出された場合で パラメータ値の要素が空文字列または空白のみの文字列なら <code>null</code>、
  * それ以外なら元の値からなる配列を返します。
  *
  * @param values パラメータ値の配列
  * @return {@link #ignoreWhitespace()}が呼び出された場合でパラメータ値の要素が空文字列または空白のみの文字列なら <code>null</code>、
  *     それ以外なら元の値からなる配列
  */
 protected Object[] normalizeArray(final Object... values) {
   if (!excludesWhitespace || values == null) {
     return values;
   }
   final List<Object> list = CollectionsUtil.newArrayList(values.length);
   for (int i = 0; i < values.length; ++i) {
     final Object normalizedValue = normalize(values[i]);
     if (normalizedValue != null) {
       list.add(normalizedValue);
     }
   }
   return list.toArray(new Object[list.size()]);
 }
Exemplo n.º 6
0
/**
 * 未ロードのクラスをトランスフォームし、トランスフォーム後のクラスのクラスを適切なクラスローダーにロードするクラスローダです。
 *
 * @author taedium
 */
public class TransformClassLoader extends AbstractClassLoader {

  /** 永続クラスのトランスフォーマ */
  protected PersistenceClassTransformer persistenceClassTransformer;

  /** 永続ユニット情報 */
  protected PersistenceUnitInfo persistenceUnitInfo;

  /** クラストランスフォーマのリスト */
  protected List<ClassTransformer> transformers = CollectionsUtil.newArrayList();

  /**
   * インスタンスを構築します。
   *
   * <p>コンテキストクラスローダからロードされるクラス及び、<code>java.</code>または<code>javax.</code>で始まるクラスを除いて
   * 親クラスローダに委譲せず、{@link PersistenceClassTransformer#transform(PersistenceUnitInfo, String, byte[])}
   * を呼び出してロードするように構成します。
   */
  public TransformClassLoader() {
    this(Thread.currentThread().getContextClassLoader());
  }

  /**
   * インスタンスを構築します。
   *
   * <p>ブートストラップクラスローダからロードされるクラス及び、<code>java.</code>または<code>javax.</code>で始まるクラスを除いて
   * 親クラスローダに委譲せず、{@link PersistenceClassTransformer#transform(PersistenceUnitInfo, String, byte[])}
   * を呼び出してロードするように構成します。
   *
   * @param parent 親クラスローダ
   */
  public TransformClassLoader(ClassLoader parent) {
    super(parent);
  }

  /**
   * インスタンスを構築します。
   *
   * <p><code>includedNames</code>に含まれる名前のクラスのみ、 親クラスローダに委譲せず、 {@link
   * PersistenceClassTransformer#transform(PersistenceUnitInfo, String, byte[])}を呼び出してロードするように構成します。
   * ただし、ブートストラップクラスローダからロードされるクラス及び、 <code>java.</code>または<code>javax.</code>で始まるクラスを除きます。
   *
   * @param parent 親クラスローダ
   * @param includedNames 親より先にロードする対象となるクラス名のセット
   */
  public TransformClassLoader(final ClassLoader parent, final Set<String> includedNames) {
    super(parent, includedNames);
  }

  /**
   * 永続クラスのトランスフォーマを登録します。
   *
   * @param persistenceClassTransformer 永続クラスのトランスフォーマ
   */
  public void registerPersistenceClassTransformer(
      final PersistenceClassTransformer persistenceClassTransformer) {
    this.persistenceClassTransformer = persistenceClassTransformer;
  }

  /**
   * 永続ユニット情報を登録します。
   *
   * @param persistenceUnitInfo 永続ユニット情報
   */
  public void registerPersistenceUnitInfo(final PersistenceUnitInfo persistenceUnitInfo) {
    this.persistenceUnitInfo = persistenceUnitInfo;
  }

  /**
   * クラストランスフォーマを登録します。
   *
   * @param transformer クラストランスフォーマ
   */
  public void addTransformer(final ClassTransformer transformer) {
    transformers.add(transformer);
  }

  @Override
  protected Class<?> doDefineClass(String className, byte[] bytes) {
    return persistenceClassTransformer.transform(persistenceUnitInfo, className, bytes);
  }
}
/**
 * INSERT前に識別子を自動生成するIDジェネレータの抽象クラスです。
 *
 * @author koichik
 */
public abstract class AbstractPreAllocateIdGenerator extends AbstractIdGenerator {

  /** {@link #idContextMap}に対するデフォルトのキー */
  protected static String DEFAULT_ID_CONTEXT_KEY = IdContext.class.getName();

  /** 割り当てサイズ */
  protected final long allocationSize;

  /** IDコンテキストの{@link Map} */
  protected ConcurrentMap<String, IdContext> idContextMap = CollectionsUtil.newConcurrentHashMap();

  /**
   * インスタンスを構築します。
   *
   * @param entityMeta エンティティメタデータ
   * @param propertyMeta プロパティメタデータ
   * @param allocationSize 割り当てサイズ
   */
  public AbstractPreAllocateIdGenerator(
      final EntityMeta entityMeta, final PropertyMeta propertyMeta, final long allocationSize) {
    super(entityMeta, propertyMeta);
    this.allocationSize = allocationSize;
  }

  public boolean supportBatch(final JdbcManagerImplementor jdbcManager) {
    return true;
  }

  public boolean useGetGeneratedKeys(final JdbcManagerImplementor jdbcManager) {
    return false;
  }

  public boolean isInsertInto(final JdbcManagerImplementor jdbcManager) {
    return true;
  }

  public Object preInsert(
      final JdbcManagerImplementor jdbcManager, final Object entity, final SqlLogger sqlLogger) {
    final long id = getIdContext(jdbcManager).getNextValue(jdbcManager, sqlLogger);
    setId(entity, id);
    return Long.valueOf(id);
  }

  public void postInsert(
      final JdbcManagerImplementor jdbcManager,
      final Object entity,
      final Statement statement,
      final SqlLogger sqlLogger) {}

  /**
   * IDコンテキストを返します。
   *
   * @param jdbcManager 内部的なJDBCマネージャ
   * @return IDコンテキスト
   */
  protected IdContext getIdContext(final JdbcManagerImplementor jdbcManager) {
    final String dsName = jdbcManager.getSelectableDataSourceName();
    final String key = dsName != null ? dsName : DEFAULT_ID_CONTEXT_KEY;
    final IdContext context = idContextMap.get(key);
    if (context != null) {
      return context;
    }
    return CollectionsUtil.putIfAbsent(idContextMap, key, new IdContext());
  }

  /**
   * 次の初期値を返します。
   *
   * @param jdbcManager 内部的なJDBCマネージャ
   * @param sqlLogger SQLロガー
   * @return 次の初期値
   */
  protected abstract long getNewInitialValue(
      JdbcManagerImplementor jdbcManager, SqlLogger sqlLogger);

  /**
   * 自動生成される識別子のコンテキスト情報を保持するクラスです。
   *
   * @author koichik
   */
  public class IdContext {

    /** 初期値 */
    protected long initialValue;

    /** 割り当て済みの値 */
    protected long allocated = Long.MAX_VALUE;

    /**
     * 自動生成された識別子の値を返します。
     *
     * @param jdbcManager 内部的なJDBCマネージャ
     * @param sqlLogger SQLロガー
     * @return 自動生成された識別子の値
     */
    public synchronized long getNextValue(
        final JdbcManagerImplementor jdbcManager, final SqlLogger sqlLogger) {
      if (allocated < allocationSize) {
        return initialValue + allocated++;
      }
      initialValue = getNewInitialValue(jdbcManager, sqlLogger);
      allocated = 1;
      return initialValue;
    }
  }
}
Exemplo n.º 8
0
/**
 * {@link AutoBatchUpdate}の実装クラスです。
 *
 * @author koichik
 * @param <T> エンティティの型です。
 */
public class AutoBatchUpdateImpl<T> extends AbstractAutoBatchUpdate<T, AutoBatchUpdate<T>>
    implements AutoBatchUpdate<T> {

  /** UDPATE文 */
  protected static final String UPDATE_STATEMENT = "update ";

  /** バージョンプロパティを更新対象に含める場合<code>true</code> */
  protected boolean includeVersion;

  /** 更新対象とするプロパティ */
  protected final Set<String> includesProperties = CollectionsUtil.newHashSet();

  /** 更新から除外するプロパティ */
  protected final Set<String> excludesProperties = CollectionsUtil.newHashSet();

  /** 更新対象となるプロパティメタデータの{@link List} */
  protected final List<PropertyMeta> targetProperties = CollectionsUtil.newArrayList();

  /** set句 */
  protected final SetClause setClause = new SetClause();

  /** where句 */
  protected final WhereClause whereClause = new WhereClause();

  /**
   * @param jdbcManager 内部的なJDBCマネージャ
   * @param entities エンティティのリスト
   */
  public AutoBatchUpdateImpl(final JdbcManagerImplementor jdbcManager, final List<T> entities) {
    super(jdbcManager, entities);
  }

  public AutoBatchUpdate<T> includesVersion() {
    includeVersion = true;
    return this;
  }

  public AutoBatchUpdate<T> includes(final CharSequence... propertyNames) {
    includesProperties.addAll(Arrays.asList(toStringArray(propertyNames)));
    return this;
  }

  public AutoBatchUpdate<T> excludes(final CharSequence... propertyNames) {
    excludesProperties.addAll(Arrays.asList(toStringArray(propertyNames)));
    return this;
  }

  public AutoBatchUpdate<T> suppresOptimisticLockException() {
    suppresOptimisticLockException = true;
    return this;
  }

  @Override
  protected void prepare(final String methodName) {
    prepareCallerClassAndMethodName(methodName);
    prepareTargetProperties();
    prepareSetClause();
    prepareWhereClause();
    prepareSql();
  }

  /** set句に設定されるプロパティの準備をします。 */
  protected void prepareTargetProperties() {
    for (final PropertyMeta propertyMeta : entityMeta.getAllColumnPropertyMeta()) {
      final String propertyName = propertyMeta.getName();
      if (propertyMeta.isId() || !propertyMeta.getColumnMeta().isUpdatable()) {
        continue;
      }
      if (propertyMeta.isVersion() && !includeVersion) {
        continue;
      }
      if (!includesProperties.isEmpty() && !includesProperties.contains(propertyName)) {
        continue;
      }
      if (excludesProperties.contains(propertyName)) {
        continue;
      }
      targetProperties.add(propertyMeta);
    }
  }

  /** set句の準備をします。 */
  protected void prepareSetClause() {
    for (final PropertyMeta propertyMeta : targetProperties) {
      setClause.addSql(propertyMeta.getColumnMeta().getName());
    }
    if (!includeVersion && entityMeta.hasVersionPropertyMeta()) {
      final PropertyMeta propertyMeta = entityMeta.getVersionPropertyMeta();
      final String columnName = propertyMeta.getColumnMeta().getName();
      setClause.addSql(columnName, columnName + " + 1");
    }
  }

  /** where句の準備をします。 */
  protected void prepareWhereClause() {
    for (final PropertyMeta propertyMeta : entityMeta.getIdPropertyMetaList()) {
      whereClause.addAndSql(
          ConditionType.EQ.getCondition(propertyMeta.getColumnMeta().getName(), null));
    }
    if (!includeVersion && entityMeta.hasVersionPropertyMeta()) {
      final PropertyMeta propertyMeta = entityMeta.getVersionPropertyMeta();
      whereClause.addAndSql(
          ConditionType.EQ.getCondition(propertyMeta.getColumnMeta().getName(), null));
    }
  }

  @Override
  protected void prepareParams(final T entity) {
    for (final PropertyMeta propertyMeta : targetProperties) {
      final Object value = FieldUtil.get(propertyMeta.getField(), entity);
      addParam(value, propertyMeta);
    }
    for (final PropertyMeta propertyMeta : entityMeta.getIdPropertyMetaList()) {
      final Object value = FieldUtil.get(propertyMeta.getField(), entity);
      addParam(value, propertyMeta);
    }
    if (!includeVersion && entityMeta.hasVersionPropertyMeta()) {
      final PropertyMeta propertyMeta = entityMeta.getVersionPropertyMeta();
      final Object value = FieldUtil.get(propertyMeta.getField(), entity);
      addParam(value, propertyMeta);
    }
  }

  /**
   * SQLに変換します。
   *
   * @return SQL
   */
  @Override
  protected String toSql() {
    final String tableName = entityMeta.getTableMeta().getFullName();
    final StringBuilder buf =
        new StringBuilder(
            UPDATE_STATEMENT.length()
                + tableName.length()
                + setClause.getLength()
                + whereClause.getLength());
    return new String(
        buf.append(UPDATE_STATEMENT)
            .append(tableName)
            .append(setClause.toSql())
            .append(whereClause.toSql()));
  }

  @Override
  protected boolean isOptimisticLock() {
    return !includeVersion && entityMeta.hasVersionPropertyMeta();
  }

  @Override
  protected void incrementVersions() {
    if (includeVersion) {
      return;
    }
    final Field field = entityMeta.getVersionPropertyMeta().getField();
    for (final T entity : entities) {
      if (field.getType() == int.class || field.getType() == Integer.class) {
        final int version = IntegerConversionUtil.toPrimitiveInt(FieldUtil.get(field, entity)) + 1;
        FieldUtil.set(field, entity, Integer.valueOf(version));
      } else if (field.getType() == long.class || field.getType() == Long.class) {
        final long version = LongConversionUtil.toPrimitiveLong(FieldUtil.get(field, entity)) + 1;
        FieldUtil.set(field, entity, Long.valueOf(version));
      }
    }
  }
}
Exemplo n.º 9
0
/**
 * {@link PropertyMetaFactory}の実装クラスです。
 *
 * @author higa
 */
@SequenceGenerator(name = "default")
@TableGenerator(name = "default")
public class PropertyMetaFactoryImpl implements PropertyMetaFactory {

  /** デフォルトの{@link SequenceGenerator}です。 */
  protected static final SequenceGenerator DEFAULT_SEQUENCE_GENERATOR =
      PropertyMetaFactoryImpl.class.getAnnotation(SequenceGenerator.class);

  /** デフォルトの{@link TableGenerator}です。 */
  protected static final TableGenerator DEFAULT_TABLE_GENERATOR =
      PropertyMetaFactoryImpl.class.getAnnotation(TableGenerator.class);

  /** フィールドの型に対応する値タイプのマップです。 */
  protected static final Map<Class<?>, ValueType> valueTypes = CollectionsUtil.newHashMap();

  static {
    valueTypes.put(boolean.class, ValueTypes.BOOLEAN);
    valueTypes.put(Boolean.class, ValueTypes.BOOLEAN);
    valueTypes.put(char.class, ValueTypes.CHARACTER);
    valueTypes.put(Character.class, ValueTypes.CHARACTER);
    valueTypes.put(byte.class, ValueTypes.BYTE);
    valueTypes.put(Byte.class, ValueTypes.BYTE);
    valueTypes.put(short.class, ValueTypes.SHORT);
    valueTypes.put(Short.class, ValueTypes.SHORT);
    valueTypes.put(int.class, ValueTypes.INTEGER);
    valueTypes.put(Integer.class, ValueTypes.INTEGER);
    valueTypes.put(long.class, ValueTypes.LONG);
    valueTypes.put(Long.class, ValueTypes.LONG);
    valueTypes.put(float.class, ValueTypes.FLOAT);
    valueTypes.put(Float.class, ValueTypes.FLOAT);
    valueTypes.put(double.class, ValueTypes.DOUBLE);
    valueTypes.put(Double.class, ValueTypes.DOUBLE);
    valueTypes.put(BigDecimal.class, ValueTypes.BIGDECIMAL);
    valueTypes.put(BigInteger.class, ValueTypes.BIGINTEGER);
    valueTypes.put(java.sql.Date.class, ValueTypes.SQLDATE);
    valueTypes.put(java.sql.Time.class, ValueTypes.TIME);
    valueTypes.put(java.sql.Timestamp.class, ValueTypes.TIMESTAMP);
  }

  /** カラムメタデータファクトリです。 */
  protected ColumnMetaFactory columnMetaFactory;

  /** 永続化層の命名規約です。 */
  protected PersistenceConvention persistenceConvention;

  public PropertyMeta createPropertyMeta(Field field, EntityMeta entityMeta) {
    PropertyMeta propertyMeta = new PropertyMeta();
    doField(propertyMeta, field, entityMeta);
    doName(propertyMeta, field, entityMeta);
    doTransient(propertyMeta, field, entityMeta);
    if (!propertyMeta.isTransient()) {
      Object relationshipAnnotation = getRelationshipAnnotation(field);
      if (relationshipAnnotation == null) {
        doColumnMeta(propertyMeta, field, entityMeta);
        doId(propertyMeta, field, entityMeta);
        doFetchType(propertyMeta, field, entityMeta);
        doTemporal(propertyMeta, field, entityMeta);
        doEnum(propertyMeta, field, entityMeta);
        doVersion(propertyMeta, field, entityMeta);
        doLob(propertyMeta, field, entityMeta);
        doValueType(propertyMeta, entityMeta);
      } else {
        doRelationship(propertyMeta, field, entityMeta, relationshipAnnotation);
      }
    }
    doCustomize(propertyMeta, field, entityMeta);
    return propertyMeta;
  }

  /**
   * フィールドを処理します。
   *
   * @param propertyMeta プロパティメタデータ
   * @param field フィールド
   * @param entityMeta エンティティメタデータ
   */
  protected void doField(
      PropertyMeta propertyMeta, Field field, @SuppressWarnings("unused") EntityMeta entityMeta) {
    propertyMeta.setField(field);
  }

  /**
   * 名前を処理します。
   *
   * @param propertyMeta プロパティメタデータ
   * @param field フィールド
   * @param entityMeta エンティティメタデータ
   */
  protected void doName(
      PropertyMeta propertyMeta, Field field, @SuppressWarnings("unused") EntityMeta entityMeta) {
    propertyMeta.setName(persistenceConvention.fromFieldNameToPropertyName(field.getName()));
  }

  /**
   * カラムメタデータを処理します。
   *
   * @param propertyMeta プロパティメタデータ
   * @param field フィールド
   * @param entityMeta エンティティメタデータ
   */
  protected void doColumnMeta(PropertyMeta propertyMeta, Field field, EntityMeta entityMeta) {
    propertyMeta.setColumnMeta(columnMetaFactory.createColumnMeta(field, entityMeta, propertyMeta));
  }

  /**
   * 識別子メタデータを処理します。
   *
   * @param propertyMeta プロパティメタデータ
   * @param field フィールド
   * @param entityMeta エンティティメタデータ
   */
  protected void doId(PropertyMeta propertyMeta, Field field, EntityMeta entityMeta) {
    propertyMeta.setId(field.getAnnotation(Id.class) != null);
    GeneratedValue generatedValue = field.getAnnotation(GeneratedValue.class);
    if (generatedValue == null) {
      return;
    }
    GenerationType generationType = generatedValue.strategy();
    propertyMeta.setGenerationType(generationType);
    switch (generationType) {
      case AUTO:
        doIdentityIdGenerator(propertyMeta, entityMeta);
        doSequenceIdGenerator(propertyMeta, generatedValue, entityMeta);
        doTableIdGenerator(propertyMeta, generatedValue, entityMeta);
        break;
      case IDENTITY:
        doIdentityIdGenerator(propertyMeta, entityMeta);
        break;
      case SEQUENCE:
        if (!doSequenceIdGenerator(propertyMeta, generatedValue, entityMeta)) {
          throw new IdGeneratorNotFoundRuntimeException(
              entityMeta.getName(), propertyMeta.getName(), generatedValue.generator());
        }
        break;
      case TABLE:
        if (!doTableIdGenerator(propertyMeta, generatedValue, entityMeta)) {
          throw new IdGeneratorNotFoundRuntimeException(
              entityMeta.getName(), propertyMeta.getName(), generatedValue.generator());
        }
        break;
    }
  }

  /**
   * {@link GenerationType#IDENTITY}方式で識別子の値を自動生成するIDジェネレータを処理します。
   *
   * @param propertyMeta プロパティメタデータ
   * @param entityMeta エンティティのメタデータ
   */
  protected void doIdentityIdGenerator(PropertyMeta propertyMeta, EntityMeta entityMeta) {
    propertyMeta.setIdentityIdGenerator(new IdentityIdGenerator(entityMeta, propertyMeta));
  }

  /**
   * {@link GenerationType#SEQUENCE}方式で識別子の値を自動生成するIDジェネレータを処理します。
   *
   * @param propertyMeta プロパティメタデータ
   * @param generatedValue 識別子に付けられた{@link GeneratedValue}アノテーション
   * @param entityMeta エンティティのメタデータ
   * @return {@link GenerationType#SEQUENCE}方式で識別子の値を自動生成するIDジェネレータが存在した場合に <code>true</code>
   */
  protected boolean doSequenceIdGenerator(
      PropertyMeta propertyMeta, GeneratedValue generatedValue, EntityMeta entityMeta) {
    String name = generatedValue.generator();
    SequenceGenerator sequenceGenerator;
    if (StringUtil.isEmpty(name)) {
      sequenceGenerator = DEFAULT_SEQUENCE_GENERATOR;
    } else {
      sequenceGenerator = propertyMeta.getField().getAnnotation(SequenceGenerator.class);
      if (sequenceGenerator == null || !name.equals(sequenceGenerator.name())) {
        sequenceGenerator = entityMeta.getEntityClass().getAnnotation(SequenceGenerator.class);
        if (sequenceGenerator == null || !name.equals(sequenceGenerator.name())) {
          return false;
        }
      }
    }
    propertyMeta.setSequenceIdGenerator(
        new SequenceIdGenerator(entityMeta, propertyMeta, sequenceGenerator));
    return true;
  }

  /**
   * {@link GenerationType#TABLE}方式で識別子の値を自動生成するIDジェネレータを処理します。
   *
   * @param propertyMeta プロパティメタデータ
   * @param generatedValue 識別子に付けられた{@link GeneratedValue}アノテーション
   * @param entityMeta エンティティのメタデータ
   * @return {@link GenerationType#TABLE}方式で識別子の値を自動生成するIDジェネレータが存在した場合に <code>true</code>
   */
  protected boolean doTableIdGenerator(
      PropertyMeta propertyMeta, GeneratedValue generatedValue, EntityMeta entityMeta) {
    String name = generatedValue.generator();
    TableGenerator tableGenerator;
    if (StringUtil.isEmpty(name)) {
      tableGenerator = DEFAULT_TABLE_GENERATOR;
    } else {
      tableGenerator = propertyMeta.getField().getAnnotation(TableGenerator.class);
      if (tableGenerator == null || !name.equals(tableGenerator.name())) {
        tableGenerator = entityMeta.getEntityClass().getAnnotation(TableGenerator.class);
        if (tableGenerator == null || !name.equals(tableGenerator.name())) {
          return false;
        }
      }
    }
    propertyMeta.setTableIdGenerator(
        new TableIdGenerator(entityMeta, propertyMeta, tableGenerator));
    return true;
  }

  /**
   * フェッチタイプを処理します。
   *
   * @param propertyMeta プロパティメタデータ
   * @param field フィールド
   * @param entityMeta エンティティメタデータ
   */
  protected void doFetchType(
      final PropertyMeta propertyMeta, final Field field, final EntityMeta entityMeta) {
    final Basic basic = field.getAnnotation(Basic.class);
    if (basic == null) {
      propertyMeta.setFetchType(FetchType.EAGER);
      return;
    }
    if (propertyMeta.isId() && basic.fetch() == FetchType.LAZY) {
      throw new LazyFetchSpecifiedRuntimeException(entityMeta.getName(), propertyMeta.getName());
    }
    propertyMeta.setFetchType(basic.fetch());
  }

  /**
   * 時制の種別を処理します。
   *
   * @param propertyMeta プロパティメタデータ
   * @param field フィールド
   * @param entityMeta エンティティメタデータ
   */
  protected void doTemporal(PropertyMeta propertyMeta, Field field, EntityMeta entityMeta) {
    if (propertyMeta.getPropertyClass() != java.util.Date.class
        && propertyMeta.getPropertyClass() != Calendar.class) {
      return;
    }
    Temporal temporal = field.getAnnotation(Temporal.class);
    if (temporal == null) {
      throw new TemporalTypeNotSpecifiedRuntimeException(
          entityMeta.getName(), propertyMeta.getName());
    }
    propertyMeta.setTemporalType(temporal.value());
  }

  /**
   * enumの種別を処理します。
   *
   * @param propertyMeta プロパティメタデータ
   * @param field フィールド
   * @param entityMeta エンティティメタデータ
   */
  protected void doEnum(PropertyMeta propertyMeta, Field field, EntityMeta entityMeta) {
    if (!propertyMeta.getPropertyClass().isEnum()) {
      return;
    }
    Enumerated enumerated = field.getAnnotation(Enumerated.class);
    if (enumerated == null) {
      return;
    }
    propertyMeta.setEnumType(enumerated.value());
  }

  /**
   * バージョンチェック用かどうかを処理します。
   *
   * @param propertyMeta プロパティメタデータ
   * @param field フィールド
   * @param entityMeta エンティティメタデータ
   */
  protected void doVersion(
      PropertyMeta propertyMeta, Field field, @SuppressWarnings("unused") EntityMeta entityMeta) {
    if (field.getAnnotation(Version.class) == null) {
      return;
    }
    Class<?> clazz = ClassUtil.getWrapperClassIfPrimitive(field.getType());
    if (clazz != Integer.class
        && clazz != Long.class
        && clazz != int.class
        && clazz != long.class) {
      throw new VersionPropertyNotNumberRuntimeException(
          entityMeta.getName(), propertyMeta.getName());
    }
    propertyMeta.setVersion(true);
  }

  /**
   * 一時的かどうかを処理します。
   *
   * @param propertyMeta プロパティメタデータ
   * @param field フィールド
   * @param entityMeta エンティティメタデータ
   */
  protected void doTransient(
      PropertyMeta propertyMeta, Field field, @SuppressWarnings("unused") EntityMeta entityMeta) {
    propertyMeta.setTransient(
        field.getAnnotation(Transient.class) != null || ModifierUtil.isTransient(field));
  }

  /**
   * <code>LOB</code>かどうかを処理します。
   *
   * @param propertyMeta プロパティメタデータ
   * @param field フィールド
   * @param entityMeta エンティティメタデータ
   */
  protected void doLob(
      PropertyMeta propertyMeta, Field field, @SuppressWarnings("unused") EntityMeta entityMeta) {
    propertyMeta.setLob(field.getAnnotation(Lob.class) != null);
  }

  /**
   * {@link ValueType}を処理します。
   *
   * @param propertyMeta プロパティメタデータ
   * @param entityMeta エンティティメタデータ
   */
  @SuppressWarnings("unchecked")
  protected void doValueType(final PropertyMeta propertyMeta, final EntityMeta entityMeta) {
    final Class<?> propertyClass = propertyMeta.getPropertyClass();
    final ValueType valueType = valueTypes.get(propertyClass);
    if (valueType != null) {
      propertyMeta.setValueType(valueType);
      return;
    }

    if (propertyClass == String.class) {
      if (propertyMeta.isLob()) {
        propertyMeta.setValueType(ValueTypes.CLOB);
      } else {
        propertyMeta.setValueType(ValueTypes.STRING);
      }
      return;
    }

    if (propertyClass == byte[].class) {
      if (propertyMeta.isLob()) {
        propertyMeta.setValueType(ValueTypes.BLOB);
      } else {
        propertyMeta.setValueType(ValueTypes.BYTE_ARRAY);
      }
      return;
    }

    if (propertyClass == Date.class) {
      switch (propertyMeta.getTemporalType()) {
        case DATE:
          propertyMeta.setValueType(ValueTypes.DATE_SQLDATE);
          return;
        case TIME:
          propertyMeta.setValueType(ValueTypes.DATE_TIME);
          return;
        case TIMESTAMP:
          propertyMeta.setValueType(ValueTypes.DATE_TIMESTAMP);
          return;
      }
    }

    if (propertyClass == Calendar.class) {
      switch (propertyMeta.getTemporalType()) {
        case DATE:
          propertyMeta.setValueType(ValueTypes.CALENDAR_SQLDATE);
          return;
        case TIME:
          propertyMeta.setValueType(ValueTypes.CALENDAR_TIME);
          return;
        case TIMESTAMP:
          propertyMeta.setValueType(ValueTypes.CALENDAR_TIMESTAMP);
          return;
      }
    }

    if (propertyClass.isEnum()) {
      if (propertyMeta.getEnumType() == null) {
        propertyMeta.setValueType(ValueTypes.getValueType(propertyClass));
        return;
      }
      switch (propertyMeta.getEnumType()) {
        case ORDINAL:
          propertyMeta.setValueType(ValueTypes.getEnumOrdinalValueType(propertyClass));
          return;
        case STRING:
          propertyMeta.setValueType(ValueTypes.getEnumStringValueType(propertyClass));
          return;
      }
    }

    final ValueType userDefinedValueType = ValueTypes.createUserDefineValueType(propertyClass);
    if (userDefinedValueType != null) {
      propertyMeta.setValueType(userDefinedValueType);
      return;
    }

    if (Serializable.class.isAssignableFrom(propertyClass)) {
      if (propertyMeta.isLob()) {
        propertyMeta.setValueType(ValueTypes.SERIALIZABLE_BLOB);
      } else {
        propertyMeta.setValueType(ValueTypes.SERIALIZABLE_BYTE_ARRAY);
      }
      return;
    }

    throw new UnsupportedPropertyTypeRuntimeException(
        entityMeta.getName(), propertyMeta.getName(), propertyMeta.getPropertyClass());
  }

  /**
   * フィールドに関連のアノテーションが指定されていればそれを返します。
   *
   * @param field フィールド
   * @return 関連のアノテーションまたは<code>null</code>
   */
  protected Object getRelationshipAnnotation(final Field field) {
    final OneToOne oneToOne = field.getAnnotation(OneToOne.class);
    if (oneToOne != null) {
      return oneToOne;
    }
    final OneToMany oneToMany = field.getAnnotation(OneToMany.class);
    if (oneToMany != null) {
      return oneToMany;
    }
    final ManyToOne manyToOne = field.getAnnotation(ManyToOne.class);
    if (manyToOne != null) {
      return manyToOne;
    }
    final ManyToMany manyToMany = field.getAnnotation(ManyToMany.class);
    if (manyToMany != null) {
      return manyToMany;
    }
    return null;
  }

  /**
   * 関連を処理します。
   *
   * @param propertyMeta プロパティメタデータ
   * @param field フィールド
   * @param entityMeta エンティティメタデータ
   * @param annotation 関連のアノテーション
   */
  protected void doRelationship(
      PropertyMeta propertyMeta, Field field, EntityMeta entityMeta, Object annotation) {
    doJoinColumn(propertyMeta, field, entityMeta);
    if (OneToOne.class.isInstance(annotation)) {
      doOneToOne(propertyMeta, field, entityMeta, OneToOne.class.cast(annotation));
    } else if (OneToMany.class.isInstance(annotation)) {
      doOneToMany(propertyMeta, field, entityMeta, OneToMany.class.cast(annotation));
    } else if (ManyToOne.class.isInstance(annotation)) {
      doManyToOne(propertyMeta, field, entityMeta, ManyToOne.class.cast(annotation));
    } else {
      throw new UnsupportedRelationshipRuntimeException(
          entityMeta.getName(), propertyMeta.getName());
    }
  }

  /**
   * JoinColumnを処理します。
   *
   * @param propertyMeta プロパティメタデータ
   * @param field フィールド
   * @param entityMeta エンティティメタデータ
   */
  protected void doJoinColumn(PropertyMeta propertyMeta, Field field, EntityMeta entityMeta) {
    JoinColumn joinColumn = field.getAnnotation(JoinColumn.class);
    if (joinColumn != null) {
      JoinColumnMeta meta =
          new JoinColumnMeta(joinColumn.name(), joinColumn.referencedColumnName());
      propertyMeta.addJoinColumnMeta(meta);
    } else {
      JoinColumns joinColumns = field.getAnnotation(JoinColumns.class);
      if (joinColumns != null) {
        JoinColumn[] array = joinColumns.value();
        for (int i = 0; i < array.length; i++) {
          JoinColumn jc = array[i];
          JoinColumnMeta meta = new JoinColumnMeta(jc.name(), jc.referencedColumnName());
          if (i > 0 && (meta.getName() == null || meta.getReferencedColumnName() == null)) {
            throw new JoinColumnNameAndReferencedColumnNameMandatoryRuntimeException(
                entityMeta.getName(), propertyMeta.getName(), i + 1);
          }
          propertyMeta.addJoinColumnMeta(meta);
        }
      }
    }
  }

  /**
   * 一対一の関連を処理します。
   *
   * @param propertyMeta プロパティメタデータ
   * @param field フィールド
   * @param entityMeta エンティティメタデータ
   * @param oneToOne 一対一関連
   */
  protected void doOneToOne(
      PropertyMeta propertyMeta, Field field, EntityMeta entityMeta, OneToOne oneToOne) {
    propertyMeta.setRelationshipType(RelationshipType.ONE_TO_ONE);
    Class<?> relationshipClass = field.getType();
    if (relationshipClass.getAnnotation(Entity.class) == null) {
      throw new RelationshipNotEntityRuntimeException(
          entityMeta.getName(), propertyMeta.getName(), relationshipClass);
    }
    propertyMeta.setRelationshipClass(relationshipClass);
    String mappedBy = oneToOne.mappedBy();
    if (!StringUtil.isEmpty(mappedBy)) {
      if (propertyMeta.getJoinColumnMetaList().size() > 0) {
        throw new BothMappedByAndJoinColumnRuntimeException(
            entityMeta.getName(), propertyMeta.getName());
      }
      propertyMeta.setMappedBy(mappedBy);
    }
  }

  /**
   * 一対多の関連を処理します。
   *
   * @param propertyMeta プロパティメタデータ
   * @param field フィールド
   * @param entityMeta エンティティメタデータ
   * @param oneToMany 一対多関連
   */
  protected void doOneToMany(
      PropertyMeta propertyMeta, Field field, EntityMeta entityMeta, OneToMany oneToMany) {
    propertyMeta.setRelationshipType(RelationshipType.ONE_TO_MANY);
    if (!List.class.isAssignableFrom(field.getType())) {
      throw new OneToManyNotListRuntimeException(entityMeta.getName(), propertyMeta.getName());
    }
    Class<?> relationshipClass = ReflectionUtil.getElementTypeOfList(field.getGenericType());
    if (relationshipClass == null) {
      throw new OneToManyNotGenericsRuntimeException(entityMeta.getName(), propertyMeta.getName());
    }
    if (relationshipClass.getAnnotation(Entity.class) == null) {
      throw new RelationshipNotEntityRuntimeException(
          entityMeta.getName(), propertyMeta.getName(), relationshipClass);
    }
    propertyMeta.setRelationshipClass(relationshipClass);
    String mappedBy = oneToMany.mappedBy();
    if (!StringUtil.isEmpty(mappedBy)) {
      if (propertyMeta.getJoinColumnMetaList().size() > 0) {
        throw new BothMappedByAndJoinColumnRuntimeException(
            entityMeta.getName(), propertyMeta.getName());
      }
      propertyMeta.setMappedBy(mappedBy);
    } else {
      throw new MappedByMandatoryRuntimeException(entityMeta.getName(), propertyMeta.getName());
    }
  }

  /**
   * 多対一の関連を処理します。
   *
   * @param propertyMeta プロパティメタデータ
   * @param field フィールド
   * @param entityMeta エンティティメタデータ
   * @param manyToOne 多対一関連
   */
  protected void doManyToOne(
      PropertyMeta propertyMeta,
      Field field,
      EntityMeta entityMeta,
      @SuppressWarnings("unused") ManyToOne manyToOne) {
    propertyMeta.setRelationshipType(RelationshipType.MANY_TO_ONE);
    Class<?> relationshipClass = field.getType();
    if (relationshipClass.getAnnotation(Entity.class) == null) {
      throw new RelationshipNotEntityRuntimeException(
          entityMeta.getName(), propertyMeta.getName(), relationshipClass);
    }
    propertyMeta.setRelationshipClass(relationshipClass);
  }

  /**
   * 関連用のクラスを返します。
   *
   * @param propertyMeta プロパティメタデータ
   * @param field フィールド
   * @param entityMeta エンティティメタデータ
   * @return 関連用のクラス
   * @throws OneToManyNotGenericsRuntimeException 一対多の関連がジェネリクスのリストではない場合。
   */
  protected Class<?> getRelationshipClass(
      PropertyMeta propertyMeta, Field field, EntityMeta entityMeta)
      throws OneToManyNotGenericsRuntimeException {
    Class<?> clazz = field.getType();
    if (List.class.isAssignableFrom(clazz)) {
      clazz = ReflectionUtil.getElementTypeOfList(field.getGenericType());
      if (clazz == null) {
        throw new OneToManyNotGenericsRuntimeException(
            entityMeta.getName(), propertyMeta.getName());
      }
    }
    return clazz;
  }

  /**
   * カスタマイズします。
   *
   * @param propertyMeta プロパティメタデータ
   * @param field フィールド
   * @param entityMeta エンティティメタデータ
   */
  @SuppressWarnings("unused")
  protected void doCustomize(PropertyMeta propertyMeta, Field field, EntityMeta entityMeta) {}

  /**
   * カラムメタデータファクトリを返します。
   *
   * @return カラムメタデータファクトリ
   */
  public ColumnMetaFactory getColumnMetaFactory() {
    return columnMetaFactory;
  }

  /**
   * カラムメタデータファクトリを設定します。
   *
   * @param columnMetaFactory カラムメタデータファクトリ
   */
  @Binding(bindingType = BindingType.MUST)
  public void setColumnMetaFactory(ColumnMetaFactory columnMetaFactory) {
    this.columnMetaFactory = columnMetaFactory;
  }

  /**
   * 永続化層の命名規約を返します。
   *
   * @return 永続化層の命名規約
   */
  public PersistenceConvention getPersistenceConvention() {
    return persistenceConvention;
  }

  /**
   * 永続化層の命名規約を設定します。
   *
   * @param persistenceConvention 永続化層の命名規約
   */
  public void setPersistenceConvention(PersistenceConvention persistenceConvention) {
    this.persistenceConvention = persistenceConvention;
  }
}