alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Hibernate example source code file (EntityClass.java)

This example Hibernate source code file (EntityClass.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Hibernate tags/keywords

annotationinstance, annotationinstance, caching, callback, customsql, jpacallbackclassimpl, list, list, map, persistenceexception, persistenceexception, reflection, set, string, string, util

The Hibernate EntityClass.java source code

/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * Copyright (c) 2011, Red Hat Inc. or third-party contributors as
 * indicated by the @author tags or express copyright attribution
 * statements applied by the authors.  All third-party contributions are
 * distributed under license by Red Hat Inc.
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package org.hibernate.metamodel.source.annotations.entity;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.AccessType;
import javax.persistence.DiscriminatorType;
import javax.persistence.PersistenceException;
import javax.persistence.PostLoad;
import javax.persistence.PostPersist;
import javax.persistence.PostRemove;
import javax.persistence.PostUpdate;
import javax.persistence.PrePersist;
import javax.persistence.PreRemove;
import javax.persistence.PreUpdate;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationValue;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.MethodInfo;
import org.jboss.jandex.Type;
import org.jboss.jandex.Type.Kind;

import org.hibernate.AnnotationException;
import org.hibernate.MappingException;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.OptimisticLockType;
import org.hibernate.annotations.PolymorphismType;
import org.hibernate.engine.OptimisticLockStyle;
import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.metamodel.binding.Caching;
import org.hibernate.metamodel.binding.CustomSQL;
import org.hibernate.metamodel.binding.InheritanceType;
import org.hibernate.metamodel.source.annotations.AnnotationBindingContext;
import org.hibernate.metamodel.source.annotations.HibernateDotNames;
import org.hibernate.metamodel.source.annotations.JPADotNames;
import org.hibernate.metamodel.source.annotations.JandexHelper;
import org.hibernate.metamodel.source.annotations.attribute.ColumnValues;
import org.hibernate.metamodel.source.annotations.attribute.FormulaValue;
import org.hibernate.metamodel.source.annotations.xml.PseudoJpaDotNames;
import org.hibernate.metamodel.source.binder.ConstraintSource;
import org.hibernate.metamodel.source.binder.JpaCallbackClass;
import org.hibernate.metamodel.source.binder.TableSource;

/**
 * Represents an entity or mapped superclass configured via annotations/orm-xml.
 *
 * @author Hardy Ferentschik
 */
public class EntityClass extends ConfiguredClass {
	private final IdType idType;
	private final InheritanceType inheritanceType;

	private final String explicitEntityName;
	private final String customLoaderQueryName;
	private final List<String> synchronizedTableNames;
	private final int batchSize;

	private final TableSource primaryTableSource;
	private final Set<TableSource> secondaryTableSources;
	private final Set<ConstraintSource> constraintSources;

	private boolean isMutable;
	private boolean isExplicitPolymorphism;
	private OptimisticLockStyle optimisticLockStyle;
	private String whereClause;
	private String rowId;
	private Caching caching;
	private boolean isDynamicInsert;
	private boolean isDynamicUpdate;
	private boolean isSelectBeforeUpdate;
	private String customPersister;

	private CustomSQL customInsert;
	private CustomSQL customUpdate;
	private CustomSQL customDelete;

	private boolean isLazy;
	private String proxy;

	private ColumnValues discriminatorColumnValues;
	private FormulaValue discriminatorFormula;
	private Class<?> discriminatorType;
	private String discriminatorMatchValue;
	private boolean isDiscriminatorForced = true;
	private boolean isDiscriminatorIncludedInSql = true;

	private final List<JpaCallbackClass> jpaCallbacks;

	public EntityClass(
			ClassInfo classInfo,
			EntityClass parent,
			AccessType hierarchyAccessType,
			InheritanceType inheritanceType,
			AnnotationBindingContext context) {
		super( classInfo, hierarchyAccessType, parent, context );
		this.inheritanceType = inheritanceType;
		this.idType = determineIdType();
		boolean hasOwnTable = definesItsOwnTable();
		this.explicitEntityName = determineExplicitEntityName();
		this.constraintSources = new HashSet<ConstraintSource>();

		if ( hasOwnTable ) {
			AnnotationInstance tableAnnotation = JandexHelper.getSingleAnnotation(
					getClassInfo(),
					JPADotNames.TABLE
			);
			this.primaryTableSource = createTableSource( tableAnnotation );
		}
		else {
			this.primaryTableSource = null;
		}

		this.secondaryTableSources = createSecondaryTableSources();
		this.customLoaderQueryName = determineCustomLoader();
		this.synchronizedTableNames = determineSynchronizedTableNames();
		this.batchSize = determineBatchSize();
		this.jpaCallbacks = determineEntityListeners();

		processHibernateEntitySpecificAnnotations();
		processCustomSqlAnnotations();
		processProxyGeneration();
		processDiscriminator();
	}

	public ColumnValues getDiscriminatorColumnValues() {
		return discriminatorColumnValues;
	}

	public FormulaValue getDiscriminatorFormula() {
		return discriminatorFormula;
	}

	public Class<?> getDiscriminatorType() {
		return discriminatorType;
	}

	public IdType getIdType() {
		return idType;
	}

	public boolean isExplicitPolymorphism() {
		return isExplicitPolymorphism;
	}

	public boolean isMutable() {
		return isMutable;
	}

	public OptimisticLockStyle getOptimisticLockStyle() {
		return optimisticLockStyle;
	}

	public String getWhereClause() {
		return whereClause;
	}

	public String getRowId() {
		return rowId;
	}

	public Caching getCaching() {
		return caching;
	}

	public TableSource getPrimaryTableSource() {
		if ( definesItsOwnTable() ) {
			return primaryTableSource;
		}
		else {
			return ( (EntityClass) getParent() ).getPrimaryTableSource();
		}
	}

	public Set<TableSource> getSecondaryTableSources() {
		return secondaryTableSources;
	}

	public Set<ConstraintSource> getConstraintSources() {
		return constraintSources;
	}

	public String getExplicitEntityName() {
		return explicitEntityName;
	}

	public String getEntityName() {
		return getConfiguredClass().getSimpleName();
	}

	public boolean isDynamicInsert() {
		return isDynamicInsert;
	}

	public boolean isDynamicUpdate() {
		return isDynamicUpdate;
	}

	public boolean isSelectBeforeUpdate() {
		return isSelectBeforeUpdate;
	}

	public String getCustomLoaderQueryName() {
		return customLoaderQueryName;
	}

	public CustomSQL getCustomInsert() {
		return customInsert;
	}

	public CustomSQL getCustomUpdate() {
		return customUpdate;
	}

	public CustomSQL getCustomDelete() {
		return customDelete;
	}

	public List<String> getSynchronizedTableNames() {
		return synchronizedTableNames;
	}

	public String getCustomPersister() {
		return customPersister;
	}

	public boolean isLazy() {
		return isLazy;
	}

	public String getProxy() {
		return proxy;
	}

	public int getBatchSize() {
		return batchSize;
	}

	public boolean isEntityRoot() {
		return getParent() == null;
	}

	public boolean isDiscriminatorForced() {
		return isDiscriminatorForced;
	}

	public boolean isDiscriminatorIncludedInSql() {
		return isDiscriminatorIncludedInSql;
	}

	public String getDiscriminatorMatchValue() {
		return discriminatorMatchValue;
	}

	public List<JpaCallbackClass> getJpaCallbacks() {
		return jpaCallbacks;
	}

	private String determineExplicitEntityName() {
		final AnnotationInstance jpaEntityAnnotation = JandexHelper.getSingleAnnotation(
				getClassInfo(), JPADotNames.ENTITY
		);
		return JandexHelper.getValue( jpaEntityAnnotation, "name", String.class );
	}


	private boolean definesItsOwnTable() {
		return !InheritanceType.SINGLE_TABLE.equals( inheritanceType ) || isEntityRoot();
	}

	private IdType determineIdType() {
		List<AnnotationInstance> idAnnotations = findIdAnnotations( JPADotNames.ID );
		List<AnnotationInstance> embeddedIdAnnotations = findIdAnnotations( JPADotNames.EMBEDDED_ID );

		if ( !idAnnotations.isEmpty() && !embeddedIdAnnotations.isEmpty() ) {
			throw new MappingException(
					"@EmbeddedId and @Id cannot be used together. Check the configuration for " + getName() + "."
			);
		}

		if ( !embeddedIdAnnotations.isEmpty() ) {
			if ( embeddedIdAnnotations.size() == 1 ) {
				return IdType.EMBEDDED;
			}
			else {
				throw new AnnotationException( "Multiple @EmbeddedId annotations are not allowed" );
			}
		}

		if ( !idAnnotations.isEmpty() ) {
			return idAnnotations.size() == 1 ? IdType.SIMPLE : IdType.COMPOSED;
		}
		return IdType.NONE;
	}

	private List<AnnotationInstance> findIdAnnotations(DotName idAnnotationType) {
		List<AnnotationInstance> idAnnotationList = new ArrayList();
		if ( getClassInfo().annotations().containsKey( idAnnotationType ) ) {
			idAnnotationList.addAll( getClassInfo().annotations().get( idAnnotationType ) );
		}
		ConfiguredClass parent = getParent();
		while ( parent != null ) {
			if ( parent.getClassInfo().annotations().containsKey( idAnnotationType ) ) {
				idAnnotationList.addAll( parent.getClassInfo().annotations().get( idAnnotationType ) );
			}
			parent = parent.getParent();
		}
		return idAnnotationList;
	}

	private void processDiscriminator() {
		if ( !InheritanceType.SINGLE_TABLE.equals( inheritanceType ) ) {
			return;
		}

		final AnnotationInstance discriminatorValueAnnotation = JandexHelper.getSingleAnnotation(
				getClassInfo(), JPADotNames.DISCRIMINATOR_VALUE
		);
		if ( discriminatorValueAnnotation != null ) {
			this.discriminatorMatchValue = discriminatorValueAnnotation.value().asString();
		}

		final AnnotationInstance discriminatorColumnAnnotation = JandexHelper.getSingleAnnotation(
				getClassInfo(), JPADotNames.DISCRIMINATOR_COLUMN
		);

		final AnnotationInstance discriminatorFormulaAnnotation = JandexHelper.getSingleAnnotation(
				getClassInfo(),
				HibernateDotNames.DISCRIMINATOR_FORMULA
		);


		Class<?> type = String.class; // string is the discriminator default
		if ( discriminatorFormulaAnnotation != null ) {
			String expression = JandexHelper.getValue( discriminatorFormulaAnnotation, "value", String.class );
			discriminatorFormula = new FormulaValue( getPrimaryTableSource().getExplicitTableName(), expression );
		}
		discriminatorColumnValues = new ColumnValues( null ); //(stliu) give null here, will populate values below
		discriminatorColumnValues.setNullable( false ); // discriminator column cannot be null
		if ( discriminatorColumnAnnotation != null ) {

			DiscriminatorType discriminatorType = Enum.valueOf(
					DiscriminatorType.class, discriminatorColumnAnnotation.value( "discriminatorType" ).asEnum()
			);
			switch ( discriminatorType ) {
				case STRING: {
					type = String.class;
					break;
				}
				case CHAR: {
					type = Character.class;
					break;
				}
				case INTEGER: {
					type = Integer.class;
					break;
				}
				default: {
					throw new AnnotationException( "Unsupported discriminator type: " + discriminatorType );
				}
			}

			discriminatorColumnValues.setName(
					JandexHelper.getValue(
							discriminatorColumnAnnotation,
							"name",
							String.class
					)
			);
			discriminatorColumnValues.setLength(
					JandexHelper.getValue(
							discriminatorColumnAnnotation,
							"length",
							Integer.class
					)
			);
			discriminatorColumnValues.setColumnDefinition(
					JandexHelper.getValue(
							discriminatorColumnAnnotation,
							"columnDefinition",
							String.class
					)
			);
		}
		discriminatorType = type;

		AnnotationInstance discriminatorOptionsAnnotation = JandexHelper.getSingleAnnotation(
				getClassInfo(), HibernateDotNames.DISCRIMINATOR_OPTIONS
		);
		if ( discriminatorOptionsAnnotation != null ) {
			isDiscriminatorForced = discriminatorOptionsAnnotation.value( "force" ).asBoolean();
			isDiscriminatorIncludedInSql = discriminatorOptionsAnnotation.value( "insert" ).asBoolean();
		}
		else {
			isDiscriminatorForced = false;
			isDiscriminatorIncludedInSql = true;
		}
	}

	private void processHibernateEntitySpecificAnnotations() {
		final AnnotationInstance hibernateEntityAnnotation = JandexHelper.getSingleAnnotation(
				getClassInfo(), HibernateDotNames.ENTITY
		);

		// see HHH-6400
		PolymorphismType polymorphism = PolymorphismType.IMPLICIT;
		if ( hibernateEntityAnnotation != null && hibernateEntityAnnotation.value( "polymorphism" ) != null ) {
			polymorphism = PolymorphismType.valueOf( hibernateEntityAnnotation.value( "polymorphism" ).asEnum() );
		}
		isExplicitPolymorphism = polymorphism == PolymorphismType.EXPLICIT;

		// see HHH-6401
		OptimisticLockType optimisticLockType = OptimisticLockType.VERSION;
		if ( hibernateEntityAnnotation != null && hibernateEntityAnnotation.value( "optimisticLock" ) != null ) {
			optimisticLockType = OptimisticLockType.valueOf(
					hibernateEntityAnnotation.value( "optimisticLock" )
							.asEnum()
			);
		}
		optimisticLockStyle = OptimisticLockStyle.valueOf( optimisticLockType.name() );

		final AnnotationInstance hibernateImmutableAnnotation = JandexHelper.getSingleAnnotation(
				getClassInfo(), HibernateDotNames.IMMUTABLE
		);
		isMutable = hibernateImmutableAnnotation == null
				&& hibernateEntityAnnotation != null
				&& hibernateEntityAnnotation.value( "mutable" ) != null
				&& hibernateEntityAnnotation.value( "mutable" ).asBoolean();


		final AnnotationInstance whereAnnotation = JandexHelper.getSingleAnnotation(
				getClassInfo(), HibernateDotNames.WHERE
		);
		whereClause = whereAnnotation != null && whereAnnotation.value( "clause" ) != null ?
				whereAnnotation.value( "clause" ).asString() : null;

		final AnnotationInstance rowIdAnnotation = JandexHelper.getSingleAnnotation(
				getClassInfo(), HibernateDotNames.ROW_ID
		);
		rowId = rowIdAnnotation != null && rowIdAnnotation.value() != null
				? rowIdAnnotation.value().asString() : null;

		caching = determineCachingSettings();

		// see HHH-6397
		isDynamicInsert =
				hibernateEntityAnnotation != null
						&& hibernateEntityAnnotation.value( "dynamicInsert" ) != null
						&& hibernateEntityAnnotation.value( "dynamicInsert" ).asBoolean();

		// see HHH-6398
		isDynamicUpdate =
				hibernateEntityAnnotation != null
						&& hibernateEntityAnnotation.value( "dynamicUpdate" ) != null
						&& hibernateEntityAnnotation.value( "dynamicUpdate" ).asBoolean();


		// see HHH-6399
		isSelectBeforeUpdate =
				hibernateEntityAnnotation != null
						&& hibernateEntityAnnotation.value( "selectBeforeUpdate" ) != null
						&& hibernateEntityAnnotation.value( "selectBeforeUpdate" ).asBoolean();

		// Custom persister
		final String entityPersisterClass;
		final AnnotationInstance persisterAnnotation = JandexHelper.getSingleAnnotation(
				getClassInfo(), HibernateDotNames.PERSISTER
		);
		if ( persisterAnnotation == null || persisterAnnotation.value( "impl" ) == null ) {
			if ( hibernateEntityAnnotation != null && hibernateEntityAnnotation.value( "persister" ) != null ) {
				entityPersisterClass = hibernateEntityAnnotation.value( "persister" ).asString();
			}
			else {
				entityPersisterClass = null;
			}
		}
		else {
			if ( hibernateEntityAnnotation != null && hibernateEntityAnnotation.value( "persister" ) != null ) {
				// todo : error?
			}
			entityPersisterClass = persisterAnnotation.value( "impl" ).asString();
		}
		this.customPersister = entityPersisterClass;
	}

	private Caching determineCachingSettings() {
		final AnnotationInstance hibernateCacheAnnotation = JandexHelper.getSingleAnnotation(
				getClassInfo(), HibernateDotNames.CACHE
		);
		if ( hibernateCacheAnnotation != null ) {
			final org.hibernate.cache.spi.access.AccessType accessType = hibernateCacheAnnotation.value( "usage" ) == null
					? getLocalBindingContext().getMappingDefaults().getCacheAccessType()
					: CacheConcurrencyStrategy.parse( hibernateCacheAnnotation.value( "usage" ).asEnum() )
					.toAccessType();
			return new Caching(
					hibernateCacheAnnotation.value( "region" ) == null
							? getName()
							: hibernateCacheAnnotation.value( "region" ).asString(),
					accessType,
					hibernateCacheAnnotation.value( "include" ) != null
							&& "all".equals( hibernateCacheAnnotation.value( "include" ).asString() )
			);
		}

		final AnnotationInstance jpaCacheableAnnotation = JandexHelper.getSingleAnnotation(
				getClassInfo(), JPADotNames.CACHEABLE
		);

		boolean cacheable = true; // true is the default
		if ( jpaCacheableAnnotation != null && jpaCacheableAnnotation.value() != null ) {
			cacheable = jpaCacheableAnnotation.value().asBoolean();
		}

		final boolean doCaching;
		switch ( getLocalBindingContext().getMetadataImplementor().getOptions().getSharedCacheMode() ) {
			case ALL: {
				doCaching = true;
				break;
			}
			case ENABLE_SELECTIVE: {
				doCaching = cacheable;
				break;
			}
			case DISABLE_SELECTIVE: {
				doCaching = jpaCacheableAnnotation == null || cacheable;
				break;
			}
			default: {
				// treat both NONE and UNSPECIFIED the same
				doCaching = false;
				break;
			}
		}

		if ( !doCaching ) {
			return null;
		}

		return new Caching(
				getName(),
				getLocalBindingContext().getMappingDefaults().getCacheAccessType(),
				true
		);
	}

	/**
	 * todo see {@code Binder#createTable}
	 *
	 * @param tableAnnotation a annotation instance, either {@link javax.persistence.Table} or {@link javax.persistence.SecondaryTable}
	 *
	 * @return A table source for the specified annotation instance
	 */
	private TableSource createTableSource(AnnotationInstance tableAnnotation) {
		String schema = null;
		String catalog = null;
		if ( tableAnnotation != null ) {
			schema = JandexHelper.getValue( tableAnnotation, "schema", String.class );
			catalog = JandexHelper.getValue( tableAnnotation, "catalog", String.class );
		}
		// process the table name
		String tableName = null;
		String logicalTableName = null;

		if ( tableAnnotation != null ) {
			logicalTableName = JandexHelper.getValue( tableAnnotation, "name", String.class );
			if ( StringHelper.isNotEmpty( logicalTableName ) ) {
				tableName = logicalTableName;
			}
			createUniqueConstraints( tableAnnotation, tableName );
		}

		TableSourceImpl tableSourceImpl;
		if ( tableAnnotation == null || JPADotNames.TABLE.equals( tableAnnotation.name() ) ) {
			// for the main table @Table we use 'null' as logical name
			tableSourceImpl = new TableSourceImpl( schema, catalog, tableName, null );
		}
		else {
			// for secondary tables a name must be specified which is used as logical table name
			tableSourceImpl = new TableSourceImpl( schema, catalog, tableName, logicalTableName );
		}
		return tableSourceImpl;
	}

	private Set<TableSource> createSecondaryTableSources() {
		Set<TableSource> secondaryTableSources = new HashSet();
		AnnotationInstance secondaryTables = JandexHelper.getSingleAnnotation(
				getClassInfo(),
				JPADotNames.SECONDARY_TABLES
		);
		AnnotationInstance secondaryTable = JandexHelper.getSingleAnnotation(
				getClassInfo(),
				JPADotNames.SECONDARY_TABLE
		);
		// collect all @secondaryTable annotations
		List<AnnotationInstance> secondaryTableAnnotations = new ArrayList();
		if ( secondaryTable != null ) {
			secondaryTableAnnotations.add(
					secondaryTable
			);
		}

		if ( secondaryTables != null ) {
			secondaryTableAnnotations.addAll(
					Arrays.asList(
							JandexHelper.getValue( secondaryTables, "value", AnnotationInstance[].class )
					)
			);
		}

		// create table sources
		for ( AnnotationInstance annotationInstance : secondaryTableAnnotations ) {
			secondaryTableSources.add( createTableSource( annotationInstance ) );
		}

		return secondaryTableSources;
	}


	private void createUniqueConstraints(AnnotationInstance tableAnnotation, String tableName) {
		AnnotationValue value = tableAnnotation.value( "uniqueConstraints" );
		if ( value == null ) {
			return;
		}

		AnnotationInstance[] uniqueConstraints = value.asNestedArray();
		for ( AnnotationInstance unique : uniqueConstraints ) {
			String name = unique.value( "name" ) == null ? null : unique.value( "name" ).asString();
			String[] columnNames = unique.value( "columnNames" ).asStringArray();
			UniqueConstraintSourceImpl uniqueConstraintSource =
					new UniqueConstraintSourceImpl(
							name, tableName, Arrays.asList( columnNames )
					);
			constraintSources.add( uniqueConstraintSource );
		}
	}

	private String determineCustomLoader() {
		String customLoader = null;
		// Custom sql loader
		final AnnotationInstance sqlLoaderAnnotation = JandexHelper.getSingleAnnotation(
				getClassInfo(), HibernateDotNames.LOADER
		);
		if ( sqlLoaderAnnotation != null ) {
			customLoader = sqlLoaderAnnotation.value( "namedQuery" ).asString();
		}
		return customLoader;
	}

	private CustomSQL createCustomSQL(AnnotationInstance customSqlAnnotation) {
		if ( customSqlAnnotation == null ) {
			return null;
		}

		final String sql = customSqlAnnotation.value( "sql" ).asString();
		final boolean isCallable = customSqlAnnotation.value( "callable" ) != null
				&& customSqlAnnotation.value( "callable" ).asBoolean();

		final ExecuteUpdateResultCheckStyle checkStyle = customSqlAnnotation.value( "check" ) == null
				? isCallable
				? ExecuteUpdateResultCheckStyle.NONE
				: ExecuteUpdateResultCheckStyle.COUNT
				: ExecuteUpdateResultCheckStyle.valueOf( customSqlAnnotation.value( "check" ).asEnum() );

		return new CustomSQL( sql, isCallable, checkStyle );
	}

	private void processCustomSqlAnnotations() {
		// Custom sql insert
		final AnnotationInstance sqlInsertAnnotation = JandexHelper.getSingleAnnotation(
				getClassInfo(), HibernateDotNames.SQL_INSERT
		);
		customInsert = createCustomSQL( sqlInsertAnnotation );

		// Custom sql update
		final AnnotationInstance sqlUpdateAnnotation = JandexHelper.getSingleAnnotation(
				getClassInfo(), HibernateDotNames.SQL_UPDATE
		);
		customUpdate = createCustomSQL( sqlUpdateAnnotation );

		// Custom sql delete
		final AnnotationInstance sqlDeleteAnnotation = JandexHelper.getSingleAnnotation(
				getClassInfo(), HibernateDotNames.SQL_DELETE
		);
		customDelete = createCustomSQL( sqlDeleteAnnotation );
	}

	private List<String> determineSynchronizedTableNames() {
		final AnnotationInstance synchronizeAnnotation = JandexHelper.getSingleAnnotation(
				getClassInfo(), HibernateDotNames.SYNCHRONIZE
		);
		if ( synchronizeAnnotation != null ) {
			final String[] tableNames = synchronizeAnnotation.value().asStringArray();
			return Arrays.asList( tableNames );
		}
		else {
			return Collections.emptyList();
		}
	}

	private void processProxyGeneration() {
		// Proxy generation
		final AnnotationInstance hibernateProxyAnnotation = JandexHelper.getSingleAnnotation(
				getClassInfo(), HibernateDotNames.PROXY
		);
		if ( hibernateProxyAnnotation != null ) {
			isLazy = hibernateProxyAnnotation.value( "lazy" ) == null
					|| hibernateProxyAnnotation.value( "lazy" ).asBoolean();
			if ( isLazy ) {
				final AnnotationValue proxyClassValue = hibernateProxyAnnotation.value( "proxyClass" );
				if ( proxyClassValue == null ) {
					proxy = getName();
				}
				else {
					proxy = proxyClassValue.asString();
				}
			}
			else {
				proxy = null;
			}
		}
		else {
			isLazy = true;
			proxy = getName();
		}
	}

	private int determineBatchSize() {
		final AnnotationInstance batchSizeAnnotation = JandexHelper.getSingleAnnotation(
				getClassInfo(), HibernateDotNames.BATCH_SIZE
		);
		return batchSizeAnnotation == null ? -1 : batchSizeAnnotation.value( "size" ).asInt();
	}

	private List<JpaCallbackClass> determineEntityListeners() {
		List<JpaCallbackClass> callbackClassList = new ArrayList();

		// Bind default JPA entity listener callbacks (unless excluded), using superclasses first (unless excluded)
		if ( JandexHelper.getSingleAnnotation( getClassInfo(), JPADotNames.EXCLUDE_DEFAULT_LISTENERS ) == null ) {
			List<AnnotationInstance> defaultEntityListenerAnnotations = getLocalBindingContext().getIndex()
					.getAnnotations( PseudoJpaDotNames.DEFAULT_ENTITY_LISTENERS );
			for ( AnnotationInstance annotation : defaultEntityListenerAnnotations ) {
				for ( Type callbackClass : annotation.value().asClassArray() ) {
					String callbackClassName = callbackClass.name().toString();
					try {
						processDefaultJpaCallbacks( callbackClassName, callbackClassList );
					}
					catch ( PersistenceException error ) {
						throw new PersistenceException( error.getMessage() + "default entity listener " + callbackClassName );
					}
				}
			}
		}

		// Bind JPA entity listener callbacks, using superclasses first (unless excluded)
		List<AnnotationInstance> annotationList = getClassInfo().annotations().get( JPADotNames.ENTITY_LISTENERS );
		if ( annotationList != null ) {
			for ( AnnotationInstance annotation : annotationList ) {
				for ( Type callbackClass : annotation.value().asClassArray() ) {
					String callbackClassName = callbackClass.name().toString();
					try {
						processJpaCallbacks( callbackClassName, true, callbackClassList );
					}
					catch ( PersistenceException error ) {
						throw new PersistenceException( error.getMessage() + "entity listener " + callbackClassName );
					}
				}
			}
		}

		// Bind JPA entity.mapped superclass callbacks, using superclasses first (unless excluded)
		try {
			processJpaCallbacks( getName(), false, callbackClassList );
		}
		catch ( PersistenceException error ) {
			throw new PersistenceException(
					error.getMessage() + "entity/mapped superclass " + getClassInfo().name().toString()
			);
		}

		return callbackClassList;
	}

	private void processDefaultJpaCallbacks(String instanceCallbackClassName, List<JpaCallbackClass> jpaCallbackClassList) {
		ClassInfo callbackClassInfo = getLocalBindingContext().getClassInfo( instanceCallbackClassName );

		// Process superclass first if available and not excluded
		if ( JandexHelper.getSingleAnnotation( callbackClassInfo, JPADotNames.EXCLUDE_SUPERCLASS_LISTENERS ) != null ) {
			DotName superName = callbackClassInfo.superName();
			if ( superName != null ) {
				processDefaultJpaCallbacks( instanceCallbackClassName, jpaCallbackClassList );
			}
		}

		String callbackClassName = callbackClassInfo.name().toString();
		Map<Class callbacksByType = new HashMap, String>();
		createDefaultCallback(
				PrePersist.class, PseudoJpaDotNames.DEFAULT_PRE_PERSIST, callbackClassName, callbacksByType
		);
		createDefaultCallback(
				PreRemove.class, PseudoJpaDotNames.DEFAULT_PRE_REMOVE, callbackClassName, callbacksByType
		);
		createDefaultCallback(
				PreUpdate.class, PseudoJpaDotNames.DEFAULT_PRE_UPDATE, callbackClassName, callbacksByType
		);
		createDefaultCallback(
				PostLoad.class, PseudoJpaDotNames.DEFAULT_POST_LOAD, callbackClassName, callbacksByType
		);
		createDefaultCallback(
				PostPersist.class, PseudoJpaDotNames.DEFAULT_POST_PERSIST, callbackClassName, callbacksByType
		);
		createDefaultCallback(
				PostRemove.class, PseudoJpaDotNames.DEFAULT_POST_REMOVE, callbackClassName, callbacksByType
		);
		createDefaultCallback(
				PostUpdate.class, PseudoJpaDotNames.DEFAULT_POST_UPDATE, callbackClassName, callbacksByType
		);
		if ( !callbacksByType.isEmpty() ) {
			jpaCallbackClassList.add( new JpaCallbackClassImpl( instanceCallbackClassName, callbacksByType, true ) );
		}
	}

	private void processJpaCallbacks(String instanceCallbackClassName, boolean isListener, List<JpaCallbackClass> callbackClassList) {

		ClassInfo callbackClassInfo = getLocalBindingContext().getClassInfo( instanceCallbackClassName );

		// Process superclass first if available and not excluded
		if ( JandexHelper.getSingleAnnotation( callbackClassInfo, JPADotNames.EXCLUDE_SUPERCLASS_LISTENERS ) != null ) {
			DotName superName = callbackClassInfo.superName();
			if ( superName != null ) {
				processJpaCallbacks(
						instanceCallbackClassName,
						isListener,
						callbackClassList
				);
			}
		}

		Map<Class callbacksByType = new HashMap, String>();
		createCallback( PrePersist.class, JPADotNames.PRE_PERSIST, callbacksByType, callbackClassInfo, isListener );
		createCallback( PreRemove.class, JPADotNames.PRE_REMOVE, callbacksByType, callbackClassInfo, isListener );
		createCallback( PreUpdate.class, JPADotNames.PRE_UPDATE, callbacksByType, callbackClassInfo, isListener );
		createCallback( PostLoad.class, JPADotNames.POST_LOAD, callbacksByType, callbackClassInfo, isListener );
		createCallback( PostPersist.class, JPADotNames.POST_PERSIST, callbacksByType, callbackClassInfo, isListener );
		createCallback( PostRemove.class, JPADotNames.POST_REMOVE, callbacksByType, callbackClassInfo, isListener );
		createCallback( PostUpdate.class, JPADotNames.POST_UPDATE, callbacksByType, callbackClassInfo, isListener );
		if ( !callbacksByType.isEmpty() ) {
			callbackClassList.add( new JpaCallbackClassImpl( instanceCallbackClassName, callbacksByType, isListener ) );
		}
	}

	private void createDefaultCallback(Class callbackTypeClass,
									   DotName callbackTypeName,
									   String callbackClassName,
									   Map<Class callbacksByClass) {
		for ( AnnotationInstance callback : getLocalBindingContext().getIndex().getAnnotations( callbackTypeName ) ) {
			MethodInfo methodInfo = (MethodInfo) callback.target();
			validateMethod( methodInfo, callbackTypeClass, callbacksByClass, true );
			if ( methodInfo.declaringClass().name().toString().equals( callbackClassName ) ) {
				if ( methodInfo.args().length != 1 ) {
					throw new PersistenceException(
							String.format(
									"Callback method %s must have exactly one argument defined as either Object or %s in ",
									methodInfo.name(),
									getEntityName()
							)
					);
				}
				callbacksByClass.put( callbackTypeClass, methodInfo.name() );
			}
		}
	}

	private void createCallback(Class callbackTypeClass,
								DotName callbackTypeName,
								Map<Class callbacksByClass,
								ClassInfo callbackClassInfo,
								boolean isListener) {
		Map<DotName, List annotations = callbackClassInfo.annotations();
		List<AnnotationInstance> annotationInstances = annotations.get( callbackTypeName );
		if ( annotationInstances == null ) {
			return;
		}
		for ( AnnotationInstance callbackAnnotation : annotationInstances ) {
			MethodInfo methodInfo = (MethodInfo) callbackAnnotation.target();
			validateMethod( methodInfo, callbackTypeClass, callbacksByClass, isListener );
			callbacksByClass.put( callbackTypeClass, methodInfo.name() );
		}
	}

	private void validateMethod(MethodInfo methodInfo,
								Class callbackTypeClass,
								Map<Class callbacksByClass,
								boolean isListener) {
		if ( methodInfo.returnType().kind() != Kind.VOID ) {
			throw new PersistenceException( "Callback method " + methodInfo.name() + " must have a void return type in " );
		}
		if ( Modifier.isStatic( methodInfo.flags() ) || Modifier.isFinal( methodInfo.flags() ) ) {
			throw new PersistenceException( "Callback method " + methodInfo.name() + " must not be static or final in " );
		}
		Type[] argTypes = methodInfo.args();
		if ( isListener ) {
			if ( argTypes.length != 1 ) {
				throw new PersistenceException( "Callback method " + methodInfo.name() + " must have exactly one argument in " );
			}
			String argTypeName = argTypes[0].name().toString();
			if ( !argTypeName.equals( Object.class.getName() ) && !argTypeName.equals( getName() ) ) {
				throw new PersistenceException(
						"The argument for callback method " + methodInfo.name() +
								" must be defined as either Object or " + getEntityName() + " in "
				);
			}
		}
		else if ( argTypes.length != 0 ) {
			throw new PersistenceException( "Callback method " + methodInfo.name() + " must have no arguments in " );
		}
		if ( callbacksByClass.containsKey( callbackTypeClass ) ) {
			throw new PersistenceException(
					"Only one method may be annotated as a " + callbackTypeClass.getSimpleName() +
							" callback method in "
			);
		}
	}

	// Process JPA callbacks, in superclass-first order (unless superclasses are excluded), using default listeners first
	// (unless default listeners are excluded), then entity listeners, and finally the entity/mapped superclass itself
	private class JpaCallbackClassImpl implements JpaCallbackClass {

		private final Map<Class callbacksByType;
		private final String name;
		private final boolean isListener;

		private JpaCallbackClassImpl(String name,
									 Map<Class callbacksByType,
									 boolean isListener) {
			this.name = name;
			this.callbacksByType = callbacksByType;
			this.isListener = isListener;
		}

		@Override
		public String getCallbackMethod(Class<?> callbackType) {
			return callbacksByType.get( callbackType );
		}

		@Override
		public String getName() {
			return name;
		}

		@Override
		public boolean isListener() {
			return isListener;
		}
	}
}

Other Hibernate examples (source code examples)

Here is a short list of links related to this Hibernate EntityClass.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.