@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; }
/** * 期待値を読み込む実装クラスです。 * * <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()); }
/** * {@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; }
/** * {@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()]); }
/** * 未ロードのクラスをトランスフォームし、トランスフォーム後のクラスのクラスを適切なクラスローダーにロードするクラスローダです。 * * @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; } } }
/** * {@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)); } } } }
/** * {@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; } }