ORM + Gradle

HHH-14285 - project template
HHH-14286 - Gradle plugin
This commit is contained in:
Steve Ebersole 2020-10-15 17:20:30 -05:00
parent 4d8c89920a
commit 29848c49db
55 changed files with 2696 additions and 576 deletions

View File

@ -14,7 +14,7 @@ before_script:
- java -version
- ./gradlew assemble
script:
- ./gradlew check -Plog-test-progress=true
- ./gradlew check -Plog-test-progress=true --stacktrace
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/

View File

@ -10,6 +10,7 @@ buildscript {
jcenter()
mavenCentral()
}
dependencies {
classpath 'org.hibernate.build.gradle:hibernate-matrix-testing:3.0.0.Final'
classpath 'org.hibernate.build.gradle:version-injection-plugin:1.0.0'

View File

@ -12,6 +12,8 @@ ext {
projectsToSkipWhenAggregatingJavadocs = [
'documentation',
'hibernate-entitymanager',
'hibernate-gradle-plugin',
'project-template',
'hibernate-infinispan',
'hibernate-ehcache',
'hibernate-java8',

60
gradle/javadoc.gradle Normal file
View File

@ -0,0 +1,60 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
// make sure Java plugin is applied
apply plugin : 'java'
apply from: rootProject.file( 'gradle/base-information.gradle' )
javadoc {
exclude( "**/internal/*" )
exclude( "**/generated-src/**" )
final int currentYear = new GregorianCalendar().get( Calendar.YEAR )
configure( options ) {
// this is the config needed to use asciidoclet for Javadoc rendering. It relies on a build from John's PR @ https://github.com/asciidoctor/asciidoclet/pull/91
// however, the PR does not work for me in that Javadocs with `@asciidoclet` are not rendered using asciidoc(tor/let). Also tried the preferable `@asciidoc`
// with the same result. Leaving all this config in place however as the outcome is the same as not enabling it.
// todo (6.0) : need to find out why the asciidoclet PR does not work
//
// Travis CI JDK 11 build did not like this
// docletpath = configurations.asciidoclet.files.asType(List)
// doclet = 'org.asciidoctor.Asciidoclet'
windowTitle = "$project.name JavaDocs"
docTitle = "$project.name JavaDocs ($project.version)"
bottom = "Copyright &copy; 2001-$currentYear <a href=\"http://redhat.com\">Red Hat, Inc.</a> All Rights Reserved."
use = true
encoding = 'UTF-8'
links += [
'https://docs.oracle.com/javase/8/docs/api/',
'http://docs.jboss.org/hibernate/beanvalidation/spec/2.0/api/',
'http://docs.jboss.org/cdi/api/2.0/',
'https://javaee.github.io/javaee-spec/javadocs/'
]
tags = [ "apiNote", 'implSpec', 'implNote', 'todo' ]
if ( JavaVersion.current().isJava11Compatible() ) {
//The need to set `--source 1.8` applies to all JVMs after 11, and also to 11
// but after excluding the first two builds; see also specific comments on
// https://bugs.openjdk.java.net/browse/JDK-8212233?focusedCommentId=14245762
// For now, let's be compatible with JDK 11.0.3+. We can improve on it if people
// complain they cannot build with JDK 11.0.0, 11.0.1 and 11.0.2.
System.out.println("Forcing Javadoc in Java 8 compatible mode");
options.source = project.baselineJavaVersion
}
addStringOption( 'Xdoclint:none', '-quiet' )
tags(
'todo:X"',
'apiNote:a:"API Note:"',
'implSpec:a:"Implementation Specification:"',
'implNote:a:"Implementation Note:"'
)
}
}

View File

@ -10,8 +10,8 @@
ext {
junitVersion = '4.12'
junitVintageVersion = '5.3.1'
junit5Version = '5.3.1'
junitVintageVersion = '5.4.2'
junit5Version = '5.4.2'
h2Version = '1.4.199'
bytemanVersion = '4.0.13' //Compatible with JDK16

View File

@ -117,61 +117,9 @@ task javadocJar(type: Jar) {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Javadoc
apply from: rootProject.file( 'gradle/javadoc.gradle' )
javadoc {
exclude( "**/internal/*" )
exclude( "**/generated-src/**" )
final int currentYear = new GregorianCalendar().get( Calendar.YEAR )
configure( options ) {
// this is the config needed to use asciidoclet for Javadoc rendering. It relies on a build from John's PR @ https://github.com/asciidoctor/asciidoclet/pull/91
// however, the PR does not work for me in that Javadocs with `@asciidoclet` are not rendered using asciidoc(tor/let). Also tried the preferable `@asciidoc`
// with the same result. Leaving all this config in place however as the outcome is the same as not enabling it.
// todo (6.0) : need to find out why the asciidoclet PR does not work
//
// Travis CI JDK 11 build did not like this
// docletpath = configurations.asciidoclet.files.asType(List)
// doclet = 'org.asciidoctor.Asciidoclet'
windowTitle = "$project.name JavaDocs"
docTitle = "$project.name JavaDocs ($project.version)"
bottom = "Copyright &copy; 2001-$currentYear <a href=\"http://redhat.com\">Red Hat, Inc.</a> All Rights Reserved."
use = true
encoding = 'UTF-8'
links += [
'https://docs.oracle.com/javase/8/docs/api/',
'http://docs.jboss.org/hibernate/beanvalidation/spec/2.0/api/',
'http://docs.jboss.org/cdi/api/2.0/',
'https://javaee.github.io/javaee-spec/javadocs/'
]
tags = [ "apiNote", 'implSpec', 'implNote', 'todo' ]
if ( JavaVersion.current().isJava11Compatible() ) {
//The need to set `--source 1.8` applies to all JVMs after 11, and also to 11
// but after excluding the first two builds; see also specific comments on
// https://bugs.openjdk.java.net/browse/JDK-8212233?focusedCommentId=14245762
// For now, let's be compatible with JDK 11.0.3+. We can improve on it if people
// complain they cannot build with JDK 11.0.0, 11.0.1 and 11.0.2.
System.out.println("Forcing Javadoc in Java 8 compatible mode");
options.source = project.baselineJavaVersion
}
if ( JavaVersion.current().isJava8Compatible() ) {
addStringOption( 'Xdoclint:none', '-quiet' )
}
// // by default, exclude the files from Asciidoclet processing
// // add the @asciidoclet tag to enable Asciidoclet on a particular file
// options.addStringOption( '-exclude-asciidoclet-process', '**' )
tags(
'todo:X"',
'apiNote:a:"API Note:"',
'implSpec:a:"Implementation Specification:"',
'implNote:a:"Implementation Note:"'
)
}
options.addStringOption( 'Xdoclint:none', '-quiet' )
doFirst {
// ordering problems if we try to do this during config phase :(
classpath += project.sourceSets.main.output.classesDirs + project.sourceSets.main.compileClasspath + project.configurations.provided

View File

@ -194,8 +194,7 @@ abstract class AggregatedServiceLoader<S> {
Set<S> result = new LinkedHashSet<>();
// Always try the aggregated class loader first
Iterator<? extends Supplier<S>> providerIterator = providerStream( aggregatedClassLoaderServiceLoader )
.iterator();
Iterator<? extends Supplier<S>> providerIterator = providerStream( aggregatedClassLoaderServiceLoader ).iterator();
while ( providerIterator.hasNext() ) {
Supplier<S> provider = providerIterator.next();
collectServiceIfNotDuplicate( result, alreadyEncountered, provider );

View File

@ -1198,6 +1198,26 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil
return metadata;
}
@Override
public ManagedResources getManagedResources() {
return managedResources;
}
/**
* Used by extensions : Hibernate Reactive
*/
@Override
public MetadataImplementor metadata() {
if ( this.metadata == null ) {
this.metadata = MetadataBuildingProcess.complete(
managedResources,
metamodelBuilder.getBootstrapContext(),
metamodelBuilder.getMetadataBuildingOptions()
);
}
return metadata;
}
@Override
public EntityManagerFactoryBuilder withValidatorFactory(Object validatorFactory) {
this.validatorFactory = validatorFactory;
@ -1220,20 +1240,6 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil
// todo : close the bootstrap registry (not critical, but nice to do)
}
/**
* Used by extensions : Hibernate Reactive
*/
protected MetadataImplementor metadata() {
if ( this.metadata == null ) {
this.metadata = MetadataBuildingProcess.complete(
managedResources,
metamodelBuilder.getBootstrapContext(),
metamodelBuilder.getMetadataBuildingOptions()
);
}
return metadata;
}
@Override
public void generateSchema() {
// This seems overkill, but building the SF is necessary to get the Integrators to kick in.

View File

@ -9,6 +9,9 @@ package org.hibernate.jpa.boot.spi;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.hibernate.boot.model.process.spi.ManagedResources;
import org.hibernate.boot.spi.MetadataImplementor;
/**
* Represents a 2-phase JPA bootstrap process for building a Hibernate EntityManagerFactory.
*
@ -24,6 +27,10 @@ import javax.sql.DataSource;
* @author Scott Marlow
*/
public interface EntityManagerFactoryBuilder {
ManagedResources getManagedResources();
MetadataImplementor metadata();
/**
* Allows passing in a Java EE ValidatorFactory (delayed from constructing the builder, AKA phase 2) to be used
* in building the EntityManagerFactory
@ -32,7 +39,7 @@ public interface EntityManagerFactoryBuilder {
*
* @return {@code this}, for method chaining
*/
public EntityManagerFactoryBuilder withValidatorFactory(Object validatorFactory);
EntityManagerFactoryBuilder withValidatorFactory(Object validatorFactory);
/**
* Allows passing in a DataSource (delayed from constructing the builder, AKA phase 2) to be used
@ -42,23 +49,23 @@ public interface EntityManagerFactoryBuilder {
*
* @return {@code this}, for method chaining
*/
public EntityManagerFactoryBuilder withDataSource(DataSource dataSource);
EntityManagerFactoryBuilder withDataSource(DataSource dataSource);
/**
* Build {@link EntityManagerFactory} instance
*
* @return The built {@link EntityManagerFactory}
*/
public EntityManagerFactory build();
EntityManagerFactory build();
/**
* Cancel the building processing. This is used to signal the builder to release any resources in the case of
* something having gone wrong during the bootstrap process
*/
public void cancel();
void cancel();
/**
* Perform an explicit schema generation (rather than an "auto" one) based on the
*/
public void generateSchema();
void generateSchema();
}

View File

@ -27,6 +27,10 @@ public class DependantValue extends SimpleValue implements Resolvable {
this.wrappedValue = prototype;
}
public KeyValue getWrappedValue() {
return wrappedValue;
}
public Type getType() throws MappingException {
return wrappedValue.getType();
}

View File

@ -22,7 +22,8 @@ ext {
idea.module {
}
final File documentationDir = mkdir( "${project.buildDir}/documentation" );
final File documentationDir = mkdir( "${project.buildDir}/documentation" )
final File projectTemplateStagingDir = mkdir( "${project.buildDir}/projectTemplate" )
task releaseChecks() {
doFirst {
@ -104,6 +105,12 @@ task assembleDocumentation(type: Task, dependsOn: [rootProject.project( 'documen
}
}
task assembleProjectTemplates(type:Copy, dependsOn: project( ":project-template" ).tasks.assembleDist) {
def templateProject = project( ":project-template" )
from templateProject.layout.buildDirectory.dir( "distributions" )
into projectTemplateStagingDir
}
/**
* Upload the documentation to the JBoss doc server
*/
@ -150,9 +157,10 @@ distributions {
from parent.project( 'hibernate-core' ).configurations.provided.files { dep -> dep.name == 'javassist' }
}
// into( 'lib/jpa' ) {
// from parent.project( 'hibernate-entitymanager' ).configurations.archives.allArtifacts.files.filter{ file -> !file.name.endsWith('-sources.jar') }
// }
into( 'project-template' ) {
// todo : hook in some form of variable replacement - especially for version
from project( ':project-template' ).files( 'src/main/dist' )
}
// todo (6.0) - add back spatial
// into( 'lib/spatial' ) {
@ -241,11 +249,9 @@ distributions {
}
}
distZip.dependsOn assembleDocumentation
distTar.dependsOn assembleDocumentation
distTar {
compression = Compression.GZIP
}
// this is the common task between distTar and distZip
assembleDist.dependsOn assembleDocumentation
distTar.compression = Compression.GZIP
/**
* "virtual" task for building both types of dist bundles

View File

@ -82,6 +82,9 @@ project(':metamodel-generator').name = 'hibernate-jpamodelgen'
include 'hibernate-gradle-plugin'
project(':hibernate-gradle-plugin').projectDir = new File(rootProject.projectDir, "tooling/hibernate-gradle-plugin")
include 'project-template'
project(':project-template').projectDir = new File(rootProject.projectDir, "tooling/project-template")
include 'hibernate-enhance-maven-plugin'
project(':hibernate-enhance-maven-plugin').projectDir = new File(rootProject.projectDir, "tooling/hibernate-enhance-maven-plugin")

View File

@ -0,0 +1,51 @@
= Hibernate ORM Gradle Plugin
A Gradle plugin for introducing Hibernate tasks and capabilities into a build.
== Set up
```
plugins {
id 'org.hibernate.orm' version='X'
}
// HibernateOrmSpec
hibernate {
...
}
```
== Bytecode Enhancement
The plugin can perform build-time enhancement of the domain classes. This is controlled
by the `enhancement` portion of the `hibernate` extension:
```
hibernate {
// EnhancementSpec
enhancement {
// available options - all default to false
lazyInitialization( true )
}
}
```
== DSL
The `hibernate` DSL extension is the main entry into configuring the plugin
```
hibernate {
}
```
At this time, there is no configuration at this level.
== Capa
== Tasks
== Additional Resources
* https://plugins.gradle.org/plugin/org.hibernate.orm

View File

@ -1,6 +0,0 @@
Defines a Gradle plugin for introducing Hibernate specific tasks and capabilities into and end-user build.
Currently the only capability added is for bytecode enhancement of the user domain model, although other capabilities are
planned.
todo : usage

View File

@ -1,29 +1,121 @@
import org.apache.tools.ant.filters.ReplaceTokens
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
apply plugin: 'groovy'
description = "Gradle plugin for integrating Hibernate functionality into your build"
plugins {
id 'java-gradle-plugin'
id 'com.github.sebersole.testkit-junit5' version '0.9.5'
apply from: rootProject.file( 'gradle/published-java-module.gradle' )
apply plugin: 'java-gradle-plugin'
apply plugin: 'maven'
// for portal publishing
id "com.gradle.plugin-publish" version "0.12.0"
id "nu.studer.credentials" version "2.1"
// for publishing snapshots
id 'maven-publish'
id 'org.hibernate.build.maven-repo-auth'
id 'idea'
id 'eclipse'
}
description = "Gradle plugin for integrating Hibernate aspects into your build"
apply from: rootProject.file( 'gradle/base-information.gradle' )
apply from: rootProject.file( 'gradle/libraries.gradle' )
apply from: rootProject.file( 'gradle/javadoc.gradle' )
ext {
pluginId = 'org.hibernate.orm'
pluginVersion = project.version
//noinspection GrUnresolvedAccess
publishKey = credentials.'hibernate.gradle.publish.key'
//noinspection GrUnresolvedAccess
publishSecret = credentials.'hibernate.gradle.publish.secret'
if ( publishKey != null && publishSecret != null ) {
project.'gradle.publish.key' = publishKey
project.'gradle.publish.secret' = publishSecret
}
}
dependencies {
compile( project( ':hibernate-core' ) )
compile( libraries.jpa )
compile( libraries.javassist )
compile( libraries.byteBuddy )
compile gradleApi()
compile localGroovy()
implementation( project( ':hibernate-core' ) )
implementation project( ':hibernate-testing' )
implementation( libraries.jpa )
implementation( libraries.javassist )
implementation( libraries.byteBuddy )
}
tasks.withType( GroovyCompile ) {
options.encoding = 'UTF-8'
sourceCompatibility = project.baselineJavaVersion
targetCompatibility = project.baselineJavaVersion
gradlePlugin {
plugins {
ormPlugin {
id = project.pluginId
implementationClass = 'org.hibernate.orm.tooling.gradle.HibernateOrmPlugin'
}
}
}
pluginBundle {
website = 'https://github.com/hibernate/hibernate-orm/tree/master/tooling/hibernate-gradle-plugin'
vcsUrl = 'https://github.com/hibernate/hibernate-orm/tree/master/tooling/hibernate-gradle-plugin'
tags = ['hibernate','orm','bytecode','enhancement','bytebuddy']
plugins {
ormPlugin {
id = project.pluginId
displayName = 'Gradle plugin for Hibernate ORM'
description = 'Applies Hibernate aspects into the build'
}
}
}
processResources {
filter( ReplaceTokens, tokens: [ 'hibernateVersion': getVersion() ] )
}
task release
if ( project.version.toString().endsWith( '-SNAPSHOT' ) ) {
tasks.publishPlugins.enabled = false
tasks.release.dependsOn tasks.publish
}
else {
tasks.publish.enabled = false
tasks.release.dependsOn( tasks.publishPlugins )
}
tasks.publishPlugins {
doFirst {
if ( project.'gradle.publish.key' == null ) {
throw new RuntimeException( "`gradle.publish.key` not found" )
}
if ( project.'gradle.publish.secret' == null ) {
throw new RuntimeException( "`gradle.publish.secret` not found" )
}
}
}
// for SNAPSHOT version
publishing {
publications {
plugin( MavenPublication ) {
from components.java
}
}
repositories {
maven {
name 'jboss-snapshots-repository'
url 'https://repository.jboss.org/nexus/content/repositories/snapshots'
}
}
}

View File

@ -1,24 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.orm.tooling.gradle
/**
* Gradle DSL extension for configuring various Hibernate bytecode enhancement. Registered
* under "hibernate.enhance".
*
* @author Steve Ebersole
*/
class EnhanceExtension implements Serializable {
def boolean enableLazyInitialization = false
def boolean enableDirtyTracking = false
def boolean enableAssociationManagement = false
def boolean enableExtendedEnhancement = false
boolean shouldApply() {
return enableLazyInitialization || enableDirtyTracking || enableAssociationManagement || enableExtendedEnhancement;
}
}

View File

@ -1,37 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.tooling.gradle
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.TaskAction
import org.gradle.util.ConfigureUtil
/**
* @author Steve Ebersole
*/
@SuppressWarnings("unused")
class EnhanceTask extends DefaultTask {
@Input
EnhanceExtension options
@InputFiles
SourceSet[] sourceSets
@TaskAction
void enhance() {
for ( SourceSet sourceSet: sourceSets ) {
EnhancementHelper.enhance( sourceSet, options, project )
}
}
void options(Closure closure) {
options = new EnhanceExtension()
ConfigureUtil.configure( closure, options )
}
}

View File

@ -1,178 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.tooling.gradle;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import org.gradle.api.GradleException;
import org.gradle.api.Project;
import org.gradle.api.file.FileCollection;
import org.gradle.api.file.FileTree;
import org.gradle.api.logging.Logger;
import org.gradle.api.tasks.SourceSet;
import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext;
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
import org.hibernate.bytecode.enhance.spi.Enhancer;
import org.hibernate.bytecode.enhance.spi.UnloadedClass;
import org.hibernate.bytecode.enhance.spi.UnloadedField;
import org.hibernate.cfg.Environment;
/**
* @author Steve Ebersole
*/
public class EnhancementHelper {
static void enhance(SourceSet sourceSet, EnhanceExtension options, Project project) {
final ClassLoader classLoader = toClassLoader( sourceSet.getRuntimeClasspath() );
final EnhancementContext enhancementContext = new DefaultEnhancementContext() {
@Override
public ClassLoader getLoadingClassLoader() {
return classLoader;
}
@Override
public boolean doBiDirectionalAssociationManagement(UnloadedField field) {
return options.getEnableAssociationManagement();
}
@Override
public boolean doDirtyCheckingInline(UnloadedClass classDescriptor) {
return options.getEnableDirtyTracking();
}
@Override
public boolean hasLazyLoadableAttributes(UnloadedClass classDescriptor) {
return options.getEnableLazyInitialization();
}
@Override
public boolean isLazyLoadable(UnloadedField field) {
return options.getEnableLazyInitialization();
}
@Override
public boolean doExtendedEnhancement(UnloadedClass classDescriptor) {
return options.getEnableExtendedEnhancement();
}
};
if ( options.getEnableExtendedEnhancement() ) {
project.getLogger().warn("Extended enhancement is enabled. Classes other than entities may be modified. You should consider access the entities using getter/setter methods and disable this property. Use at your own risk." );
}
final Enhancer enhancer = Environment.getBytecodeProvider().getEnhancer( enhancementContext );
for ( File classesDir: sourceSet.getOutput().getClassesDirs() ) {
final FileTree fileTree = project.fileTree( classesDir );
for ( File file : fileTree ) {
if ( !file.getName().endsWith( ".class" ) ) {
continue;
}
final byte[] enhancedBytecode = doEnhancement( classesDir, file, enhancer );
if ( enhancedBytecode != null ) {
writeOutEnhancedClass( enhancedBytecode, file, project.getLogger() );
project.getLogger().info( "Successfully enhanced class [" + file + "]" );
}
else {
project.getLogger().info( "Skipping class [" + file.getAbsolutePath() + "], not an entity nor embeddable" );
}
}
}
}
public static ClassLoader toClassLoader(FileCollection runtimeClasspath) {
List<URL> urls = new ArrayList<>();
for ( File file : runtimeClasspath ) {
try {
urls.add( file.toURI().toURL() );
}
catch (MalformedURLException e) {
throw new GradleException( "Unable to resolve classpath entry to URL : " + file.getAbsolutePath(), e );
}
}
return new URLClassLoader( urls.toArray( new URL[0] ), Enhancer.class.getClassLoader() );
}
@SuppressWarnings("WeakerAccess")
static byte[] doEnhancement(File root, File javaClassFile, Enhancer enhancer) {
try {
final String className = determineClassName( root, javaClassFile );
final ByteArrayOutputStream originalBytes = new ByteArrayOutputStream();
try (final FileInputStream fileInputStream = new FileInputStream( javaClassFile )) {
byte[] buffer = new byte[1024];
int length;
while ( ( length = fileInputStream.read( buffer ) ) != -1 ) {
originalBytes.write( buffer, 0, length );
}
}
return enhancer.enhance( className, originalBytes.toByteArray() );
}
catch (Exception e) {
throw new GradleException( "Unable to enhance class : " + javaClassFile, e );
}
}
private static String determineClassName(File root, File javaClassFile) {
return javaClassFile.getAbsolutePath().substring(
root.getAbsolutePath().length() + 1,
javaClassFile.getAbsolutePath().length() - ".class".length()
).replace( File.separatorChar, '.' );
}
private static void writeOutEnhancedClass(byte[] enhancedBytecode, File file, Logger logger) {
try {
if ( file.delete() ) {
if ( !file.createNewFile() ) {
logger.error( "Unable to recreate class file [" + file.getName() + "]" );
}
}
else {
logger.error( "Unable to delete class file [" + file.getName() + "]" );
}
}
catch (IOException e) {
logger.warn( "Problem preparing class file for writing out enhancements [" + file.getName() + "]" );
}
try {
FileOutputStream outputStream = new FileOutputStream( file, false );
try {
outputStream.write( enhancedBytecode );
outputStream.flush();
}
catch (IOException e) {
throw new GradleException( "Error writing to enhanced class [" + file.getName() + "] to file [" + file.getAbsolutePath() + "]", e );
}
finally {
try {
outputStream.close();
}
catch (IOException ignore) {
}
}
}
catch (FileNotFoundException e) {
throw new GradleException( "Error opening class file for writing : " + file.getAbsolutePath(), e );
}
}
private EnhancementHelper() {
}
}

View File

@ -1,54 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.orm.tooling.gradle
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.api.tasks.SourceSet
import org.gradle.util.ConfigureUtil
/**
* Gradle DSL extension for configuring various Hibernate built-time tasks. Registered
* under "hibernate".
*
* @author Steve Ebersole
*/
class HibernateExtension {
private final Project project
/**
* The source sets that hold persistent model. Default is project.sourceSets.main
*/
def SourceSet[] sourceSets
/**
* Configuration for bytecode enhancement. Private; see instead {@link #enhance(groovy.lang.Closure)}
*/
protected EnhanceExtension enhance
HibernateExtension(Project project) {
this.project = project
this.sourceSet( project.getConvention().getPlugin( JavaPluginConvention ).sourceSets.main )
}
/**
* Add a single SourceSet.
*
* @param sourceSet The SourceSet to add
*/
void sourceSet(SourceSet sourceSet) {
if ( sourceSets == null ) {
sourceSets = []
}
sourceSets += sourceSet
}
void enhance(Closure closure) {
enhance = new EnhanceExtension()
ConfigureUtil.configure( closure, enhance )
}
}

View File

@ -1,78 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.orm.tooling.gradle;
import org.gradle.api.Action;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.tasks.SourceSet;
/**
* The Hibernate Gradle plugin. Adds Hibernate build-time capabilities into your Gradle-based build.
*
* @author Jeremy Whiting
* @author Steve Ebersole
*/
@SuppressWarnings("serial")
public class HibernatePlugin implements Plugin<Project> {
public void apply(Project project) {
project.getPlugins().apply( "java" );
final HibernateExtension hibernateExtension = new HibernateExtension( project );
project.getLogger().debug( "Adding Hibernate extensions to the build [{}]", project.getName() );
project.getExtensions().add( "hibernate", hibernateExtension );
project.afterEvaluate(
p -> applyEnhancement( p, hibernateExtension )
);
}
private void applyEnhancement(final Project project, final HibernateExtension hibernateExtension) {
if ( hibernateExtension.enhance == null || ! hibernateExtension.enhance.shouldApply() ) {
project.getLogger().warn( "Skipping Hibernate bytecode enhancement since no feature is enabled" );
return;
}
for ( final SourceSet sourceSet : hibernateExtension.getSourceSets() ) {
project.getLogger().debug( "Applying Hibernate enhancement action to SourceSet.{}", sourceSet.getName() );
final Task compileTask = project.getTasks().findByName( sourceSet.getCompileJavaTaskName() );
assert compileTask != null;
compileTask.doLast(new EnhancerAction( sourceSet, hibernateExtension, project ));
}
}
/**
* Gradle doesn't allow lambdas in doLast or doFirst configurations and causing up-to-date checks
* to fail. Extracting the lambda to an inner class works around this issue.
*
* @link https://github.com/gradle/gradle/issues/5510
*/
private static class EnhancerAction implements Action<Task> {
private final SourceSet sourceSet;
private final HibernateExtension hibernateExtension;
private final Project project;
private EnhancerAction(SourceSet sourceSet, HibernateExtension hibernateExtension, Project project) {
this.sourceSet = sourceSet;
this.hibernateExtension = hibernateExtension;
this.project = project;
}
@Override
public void execute(Task task) {
EnhancementHelper.enhance( sourceSet, hibernateExtension.enhance, project );
}
}
}

View File

@ -0,0 +1,48 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.tooling.gradle;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
import org.gradle.api.GradleException;
import org.gradle.api.Project;
import org.gradle.api.file.Directory;
import org.hibernate.bytecode.enhance.spi.Enhancer;
/**
* @author Steve Ebersole
*/
public class Helper {
public static ClassLoader toClassLoader(Directory classesDir) {
final File classesDirFile = classesDir.getAsFile();
final URI classesDirUri = classesDirFile.toURI();
try {
final URL url = classesDirUri.toURL();
return new URLClassLoader( new URL[] { url }, Enhancer.class.getClassLoader() );
}
catch (MalformedURLException e) {
throw new GradleException( "Unable to resolve classpath entry to URL : " + classesDirFile.getAbsolutePath(), e );
}
}
public static String determineClassName(File root, File javaClassFile) {
final Path relativeClassPath = root.toPath().relativize( javaClassFile.toPath() );
final String relativeClassPathString = relativeClassPath.toString();
final String classNameBase = relativeClassPathString.substring(
0,
relativeClassPathString.length() - ".class".length()
);
return classNameBase.replace( File.separatorChar, '.' );
}
}

View File

@ -0,0 +1,52 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.tooling.gradle;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.SourceSetContainer;
import org.hibernate.orm.tooling.gradle.enhance.EnhancementTask;
import org.hibernate.orm.tooling.gradle.metamodel.JpaMetamodelGenerationTask;
/**
* Hibernate ORM Gradle plugin
*/
public class HibernateOrmPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
project.getPlugins().apply( JavaPlugin.class );
project.getLogger().debug( "Adding Hibernate extensions to the build [{}]", project.getPath() );
final HibernateOrmSpec ormDsl = project.getExtensions().create( HibernateOrmSpec.DSL_NAME, HibernateOrmSpec.class, project );
final Configuration hibernateOrm = project.getConfigurations().maybeCreate( "hibernateOrm" );
project.getDependencies().add(
"hibernateOrm",
project.provider( () -> "org.hibernate.orm:hibernate-core:" + HibernateVersion.version )
);
project.getConfigurations().getByName( "implementation" ).extendsFrom( hibernateOrm );
final JavaPluginConvention javaPluginConvention = project.getConvention().findPlugin( JavaPluginConvention.class );
assert javaPluginConvention != null;
final SourceSetContainer sourceSets = javaPluginConvention.getSourceSets();
final SourceSet mainSourceSet = sourceSets.getByName( SourceSet.MAIN_SOURCE_SET_NAME );
EnhancementTask.apply( ormDsl, mainSourceSet, project );
JpaMetamodelGenerationTask.apply( ormDsl, mainSourceSet, project );
project.getDependencies().add(
"implementation",
project.provider( () -> "org.hibernate.orm:hibernate-core:" + ormDsl.getHibernateVersionProperty().get() )
);
}
}

View File

@ -0,0 +1,98 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.tooling.gradle;
import javax.inject.Inject;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.plugins.ExtensionAware;
import org.gradle.api.plugins.ExtensionContainer;
import org.gradle.api.provider.Property;
import org.hibernate.orm.tooling.gradle.enhance.EnhancementSpec;
import org.hibernate.orm.tooling.gradle.metamodel.JpaMetamodelGenerationSpec;
/**
* Main DSL extension for Hibernate ORM. Available as `project.hibernate`
*/
public abstract class HibernateOrmSpec implements ExtensionAware {
public static final String HIBERNATE = "hibernate";
public static final String DSL_NAME = HIBERNATE;
private final Property<String> hibernateVersionProperty;
private final Property<Boolean> supportEnhancementProperty;
private final Property<Boolean> supportJpaMetamodelProperty;
private final EnhancementSpec enhancementDsl;
private final JpaMetamodelGenerationSpec jpaMetamodelDsl;
@Inject
@SuppressWarnings( "UnstableApiUsage" )
public HibernateOrmSpec(Project project) {
hibernateVersionProperty = project.getObjects().property( String.class );
hibernateVersionProperty.convention( HibernateVersion.version );
supportEnhancementProperty = project.getObjects().property( Boolean.class );
supportEnhancementProperty.convention( true );
supportJpaMetamodelProperty = project.getObjects().property( Boolean.class );
supportJpaMetamodelProperty.convention( true );
enhancementDsl = getExtensions().create( EnhancementSpec.DSL_NAME, EnhancementSpec.class, this, project );
jpaMetamodelDsl = getExtensions().create( JpaMetamodelGenerationSpec.DSL_NAME, JpaMetamodelGenerationSpec.class, this, project );
}
public Property<String> getHibernateVersionProperty() {
return hibernateVersionProperty;
}
public void hibernateVersion(String version) {
setHibernateVersion( version );
}
public void setHibernateVersion(String version) {
hibernateVersionProperty.set( version );
}
public Property<Boolean> getSupportEnhancementProperty() {
return supportEnhancementProperty;
}
public void disableEnhancement() {
supportEnhancementProperty.set( false );
}
public Property<Boolean> getSupportJpaMetamodelProperty() {
return supportJpaMetamodelProperty;
}
public void disableJpaMetamodel() {
supportJpaMetamodelProperty.set( false );
}
public EnhancementSpec getEnhancementSpec() {
return enhancementDsl;
}
public void enhancement(Action<EnhancementSpec> action) {
action.execute( enhancementDsl );
}
public JpaMetamodelGenerationSpec getJpaMetamodelSpec() {
return jpaMetamodelDsl;
}
public void jpaMetamodel(Action<JpaMetamodelGenerationSpec>action) {
action.execute( jpaMetamodelDsl );
}
@Override
public abstract ExtensionContainer getExtensions();
}

View File

@ -0,0 +1,50 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.tooling.gradle;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.net.URL;
import org.gradle.api.GradleException;
/**
* @author Steve Ebersole
*/
public class HibernateVersion {
public static volatile String version;
static {
version = determineHibernateVersion();
}
private static String determineHibernateVersion() {
final URL versionFileUrl = findVersionFile();
try ( final InputStream inputStream = versionFileUrl.openStream() ) {
try ( final InputStreamReader inputStreamReader = new InputStreamReader( inputStream ) ) {
return new LineNumberReader( inputStreamReader ).readLine();
}
}
catch (IOException e) {
throw new GradleException( "Unable to read `META-INF/hibernate-orm.version` resource" );
}
}
private static URL findVersionFile() {
final URL badGunsAndRoses = HibernateOrmSpec.class.getClassLoader().getResource( "META-INF/hibernate-orm.version" );
if ( badGunsAndRoses != null ) {
return badGunsAndRoses;
}
//noinspection UnnecessaryLocalVariable
final URL goodGunsAndRoses = HibernateOrmSpec.class.getClassLoader().getResource( "/META-INF/hibernate-orm.version" );
return goodGunsAndRoses;
}
}

View File

@ -0,0 +1,167 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.tooling.gradle.enhance;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import org.gradle.api.GradleException;
import org.gradle.api.Project;
import org.gradle.api.file.Directory;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.logging.Logger;
import org.gradle.work.InputChanges;
import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext;
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
import org.hibernate.bytecode.enhance.spi.Enhancer;
import org.hibernate.bytecode.enhance.spi.UnloadedClass;
import org.hibernate.bytecode.enhance.spi.UnloadedField;
import org.hibernate.cfg.Environment;
import org.hibernate.orm.tooling.gradle.Helper;
import static org.hibernate.orm.tooling.gradle.Helper.determineClassName;
/**
* @author Steve Ebersole
*/
public class EnhancementHelper {
public static void enhance(
DirectoryProperty classesDirectoryProperty,
InputChanges inputChanges,
EnhancementSpec enhancementDsl,
Project project) {
final Directory classesDir = classesDirectoryProperty.get();
final File classesDirFile = classesDir.getAsFile();
final Enhancer enhancer = generateEnhancer( classesDir, enhancementDsl );
final String classesDirPath = classesDirFile.getAbsolutePath();
inputChanges.getFileChanges( classesDirectoryProperty ).forEach(
change -> {
switch ( change.getChangeType() ) {
case ADDED:
case MODIFIED: {
final File changedFile = change.getFile();
if ( changedFile.getName().endsWith( ".class" ) ) {
final String classFilePath = changedFile.getAbsolutePath();
if ( classFilePath.startsWith( classesDirPath ) ) {
// we found the directory it came from
// -use that to determine the class name
enhance( changedFile, determineClassName( classesDirFile, changedFile ), enhancer, project );
break;
}
}
break;
}
case REMOVED: {
// nothing to do
break;
}
default: {
throw new UnsupportedOperationException( "Unexpected ChangeType : " + change.getChangeType().name() );
}
}
}
);
}
private static void enhance(
File javaClassFile,
String className,
Enhancer enhancer,
Project project) {
final byte[] enhancedBytecode = doEnhancement( javaClassFile, className, enhancer );
if ( enhancedBytecode != null ) {
writeOutEnhancedClass( enhancedBytecode, javaClassFile, project.getLogger() );
project.getLogger().info( "Successfully enhanced class : " + className );
}
else {
project.getLogger().info( "Skipping class : " + className );
}
}
private static byte[] doEnhancement(File javaClassFile, String className, Enhancer enhancer) {
try {
return enhancer.enhance( className, Files.readAllBytes( javaClassFile.toPath() ) );
}
catch (Exception e) {
throw new GradleException( "Unable to enhance class : " + className, e );
}
}
private static Enhancer generateEnhancer(Directory classesDir, EnhancementSpec enhancementDsl) {
final ClassLoader classLoader = Helper.toClassLoader( classesDir );
final EnhancementContext enhancementContext = new DefaultEnhancementContext() {
@Override
public ClassLoader getLoadingClassLoader() {
return classLoader;
}
@Override
public boolean doBiDirectionalAssociationManagement(UnloadedField field) {
return enhancementDsl.getEnableAssociationManagement().get();
}
@Override
public boolean doDirtyCheckingInline(UnloadedClass classDescriptor) {
return enhancementDsl.getEnableDirtyTracking().get();
}
@Override
public boolean hasLazyLoadableAttributes(UnloadedClass classDescriptor) {
return enhancementDsl.getEnableLazyInitialization().get();
}
@Override
public boolean isLazyLoadable(UnloadedField field) {
return enhancementDsl.getEnableLazyInitialization().get();
}
@Override
public boolean doExtendedEnhancement(UnloadedClass classDescriptor) {
return enhancementDsl.getEnableExtendedEnhancement().get();
}
};
//noinspection deprecation
return Environment.getBytecodeProvider().getEnhancer( enhancementContext );
}
private static void writeOutEnhancedClass(byte[] enhancedBytecode, File file, Logger logger) {
try {
if ( file.delete() ) {
if ( !file.createNewFile() ) {
logger.error( "Unable to recreate class file : " + file.getAbsolutePath() );
}
}
else {
logger.error( "Unable to delete class file : " + file.getAbsolutePath() );
}
}
catch (IOException e) {
logger.warn( "Problem preparing class file for writing out enhancements [" + file.getAbsolutePath() + "]" );
}
try {
Files.write( file.toPath(), enhancedBytecode );
}
catch (FileNotFoundException e) {
throw new GradleException( "Error opening class file for writing : " + file.getAbsolutePath(), e );
}
catch (IOException e) {
throw new GradleException( "Error writing enhanced class to file [" + file.getAbsolutePath() + "]", e );
}
}
private EnhancementHelper() {
}
}

View File

@ -0,0 +1,129 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.tooling.gradle.enhance;
import javax.inject.Inject;
import org.gradle.api.Project;
import org.gradle.api.provider.Property;
import org.hibernate.orm.tooling.gradle.HibernateOrmSpec;
/**
* DSL extension for configuring bytecode enhancement - available as `project.hibernateOrm.enhancement`
*/
@SuppressWarnings( { "unused", "RedundantSuppression" } )
public class EnhancementSpec {
public static final String ENHANCE = "enhance";
public static final String ENHANCEMENT = "enhancement";
public static final String DSL_NAME = ENHANCEMENT;
private final Property<Boolean> enableLazyInitialization;
private final Property<Boolean> enableDirtyTracking;
private final Property<Boolean> enableAssociationManagement;
private final Property<Boolean> enableExtendedEnhancement;
@Inject
public EnhancementSpec(HibernateOrmSpec ormDsl, Project project) {
enableLazyInitialization = makeProperty( project );
enableDirtyTracking = makeProperty( project );
enableAssociationManagement = makeProperty( project );
enableExtendedEnhancement = makeProperty( project );
}
public boolean hasAnythingToDo() {
return enableLazyInitialization.get()
|| enableDirtyTracking.get()
|| enableAssociationManagement.get()
|| enableExtendedEnhancement.get();
}
public Property<Boolean> getEnableLazyInitialization() {
return enableLazyInitialization;
}
public void setEnableLazyInitialization(boolean enable) {
enableLazyInitialization.set( enable );
}
public void enableLazyInitialization(boolean enable) {
setEnableLazyInitialization( enable );
}
public void lazyInitialization(boolean enable) {
setEnableLazyInitialization( enable );
}
public void setLazyInitialization(boolean enable) {
setEnableLazyInitialization( enable );
}
public Property<Boolean> getEnableDirtyTracking() {
return enableDirtyTracking;
}
public void setEnableDirtyTracking(boolean enable) {
enableDirtyTracking.set( enable );
}
public void enableDirtyTracking(boolean enable) {
setEnableDirtyTracking( enable );
}
public void dirtyTracking(boolean enable) {
setEnableDirtyTracking( enable );
}
public void setDirtyTracking(boolean enable) {
setEnableDirtyTracking( enable );
}
public Property<Boolean> getEnableAssociationManagement() {
return enableAssociationManagement;
}
public void setEnableAssociationManagement(boolean enable) {
enableAssociationManagement.set( enable );
}
public void enableAssociationManagement(boolean enable) {
setEnableAssociationManagement( enable );
}
public void associationManagement(boolean enable) {
setEnableAssociationManagement( enable );
}
public Property<Boolean> getEnableExtendedEnhancement() {
return enableExtendedEnhancement;
}
public void setEnableExtendedEnhancement(boolean enable) {
enableExtendedEnhancement.set( enable );
}
public void enableExtendedEnhancement(boolean enable) {
setEnableExtendedEnhancement( enable );
}
public void extendedEnhancement(boolean enable) {
setEnableExtendedEnhancement( enable );
}
@SuppressWarnings( "UnstableApiUsage" )
public static Property<Boolean> makeProperty(Project project) {
final Property<Boolean> createdProperty = project.getObjects().property( Boolean.class );
// default to false
createdProperty.convention( false );
return createdProperty;
}
}

View File

@ -0,0 +1,119 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.tooling.gradle.enhance;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.inject.Inject;
import org.gradle.api.DefaultTask;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.execution.TaskExecutionAdapter;
import org.gradle.api.execution.TaskExecutionGraph;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.tasks.InputDirectory;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.TaskAction;
import org.gradle.api.tasks.TaskState;
import org.gradle.work.Incremental;
import org.gradle.work.InputChanges;
import org.hibernate.orm.tooling.gradle.HibernateOrmSpec;
import static org.hibernate.orm.tooling.gradle.HibernateOrmSpec.HIBERNATE;
/**
* @author Steve Ebersole
*/
public class EnhancementTask extends DefaultTask {
public static final String DSL_NAME = "hibernateEnhance";
public static void apply(HibernateOrmSpec ormDsl, SourceSet mainSourceSet, Project project) {
final EnhancementTask enhancementTask = project.getTasks().create(
DSL_NAME,
EnhancementTask.class,
ormDsl,
mainSourceSet,
project
);
enhancementTask.setGroup( HIBERNATE );
enhancementTask.setDescription( "Performs Hibernate ORM enhancement of the project's compiled classes" );
final String compileJavaTaskName = mainSourceSet.getCompileJavaTaskName();
final Task compileJavaTask = project.getTasks().getByName( compileJavaTaskName );
enhancementTask.dependsOn( compileJavaTask );
compileJavaTask.finalizedBy( enhancementTask );
}
private final EnhancementSpec enhancementDsl;
private final DirectoryProperty javaCompileOutputDirectory;
private final DirectoryProperty outputDirectory;
@Inject
@SuppressWarnings( "UnstableApiUsage" )
public EnhancementTask(HibernateOrmSpec ormSpec, SourceSet mainSourceSet, Project project) {
this.enhancementDsl = ormSpec.getEnhancementSpec();
javaCompileOutputDirectory = mainSourceSet.getJava().getDestinationDirectory();
outputDirectory = project.getObjects().directoryProperty();
outputDirectory.set( project.getLayout().getBuildDirectory().dir( "tmp/hibernateEnhancement" ) );
final AtomicBoolean didCompileRun = new AtomicBoolean( false );
final TaskExecutionGraph taskGraph = project.getGradle().getTaskGraph();
taskGraph.addTaskExecutionListener(
new TaskExecutionAdapter() {
@Override
public void afterExecute(Task task, TaskState state) {
super.afterExecute( task, state );
if ( "compileJava".equals( task.getName() ) ) {
if ( state.getDidWork() ) {
didCompileRun.set( true );
}
}
taskGraph.removeTaskExecutionListener( this );
}
}
);
getOutputs().upToDateWhen( (task) -> ! didCompileRun.get() );
}
@InputDirectory
@Incremental
public DirectoryProperty getJavaCompileDirectory() {
return javaCompileOutputDirectory;
}
@OutputDirectory
public DirectoryProperty getOutputDirectory() {
return outputDirectory;
}
@TaskAction
public void enhanceClasses(InputChanges inputChanges) {
if ( !enhancementDsl.hasAnythingToDo() ) {
return;
}
if ( !inputChanges.isIncremental() ) {
getProject().getLogger().debug( "EnhancementTask inputs were not incremental" );
}
if ( enhancementDsl.getEnableExtendedEnhancement().get() ) {
// for extended enhancement, we may need to enhance everything...
// for now, assume we don't
getProject().getLogger().info( "Performing extended enhancement" );
}
EnhancementHelper.enhance( javaCompileOutputDirectory, inputChanges, enhancementDsl, getProject() );
}
}

View File

@ -0,0 +1,88 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.tooling.gradle.metamodel;
import java.util.Arrays;
import javax.inject.Inject;
import org.gradle.api.JavaVersion;
import org.gradle.api.Project;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider;
import org.gradle.api.provider.SetProperty;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.compile.JavaCompile;
import org.hibernate.orm.tooling.gradle.HibernateOrmSpec;
/**
* @author Steve Ebersole
*/
public class JpaMetamodelGenerationSpec {
public static final String JPA_METAMODEL = "jpaMetamodel";
public static final String DSL_NAME = JPA_METAMODEL;
private final Property<Boolean> applyGeneratedAnnotation;
private final SetProperty<String> suppressions;
private final DirectoryProperty generationOutputDirectory;
private final DirectoryProperty compileOutputDirectory;
private final Provider<JavaVersion> targetJavaVersionAccess;
@Inject
@SuppressWarnings( "UnstableApiUsage" )
public JpaMetamodelGenerationSpec(HibernateOrmSpec ormDsl, Project project) {
applyGeneratedAnnotation = project.getObjects().property( Boolean.class );
applyGeneratedAnnotation.convention( true );
suppressions = project.getObjects().setProperty( String.class );
suppressions.convention( Arrays.asList( "raw", "deprecation" ) );
generationOutputDirectory = project.getObjects().directoryProperty();
generationOutputDirectory.convention(
project.getLayout().getBuildDirectory().dir( "generated/sources/" + JPA_METAMODEL )
);
compileOutputDirectory = project.getObjects().directoryProperty();
compileOutputDirectory.convention(
project.getLayout().getBuildDirectory().dir( "classes/java/" + JPA_METAMODEL )
);
targetJavaVersionAccess = project.provider(
() -> {
final JavaPluginConvention javaPluginConvention = project.getConvention().findPlugin( JavaPluginConvention.class );
assert javaPluginConvention != null;
final SourceSet sourceSet = javaPluginConvention.getSourceSets().getByName( SourceSet.MAIN_SOURCE_SET_NAME );
final String compileTaskName = sourceSet.getCompileJavaTaskName();
final JavaCompile compileTask = (JavaCompile) project.getTasks().getByName( compileTaskName );
return JavaVersion.toVersion( compileTask.getTargetCompatibility() );
}
);
}
public Provider<JavaVersion> getTargetJavaVersionAccess() {
return targetJavaVersionAccess;
}
public Property<Boolean> getApplyGeneratedAnnotation() {
return applyGeneratedAnnotation;
}
public SetProperty<String> getSuppressions() {
return suppressions;
}
public DirectoryProperty getGenerationOutputDirectory() {
return generationOutputDirectory;
}
public DirectoryProperty getCompileOutputDirectory() {
return compileOutputDirectory;
}
}

View File

@ -0,0 +1,346 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.tooling.gradle.metamodel;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import javax.inject.Inject;
import javax.persistence.SharedCacheMode;
import javax.persistence.ValidationMode;
import javax.persistence.spi.ClassTransformer;
import javax.persistence.spi.PersistenceUnitInfo;
import javax.persistence.spi.PersistenceUnitTransactionType;
import javax.sql.DataSource;
import org.gradle.api.DefaultTask;
import org.gradle.api.GradleException;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.file.ConfigurableFileTree;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.file.FileCollection;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.SkipWhenEmpty;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.SourceSetOutput;
import org.gradle.api.tasks.TaskAction;
import org.gradle.api.tasks.compile.JavaCompile;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.hibernate.orm.tooling.gradle.Helper;
import org.hibernate.orm.tooling.gradle.HibernateOrmSpec;
import org.hibernate.orm.tooling.gradle.metamodel.model.JpaStaticMetamodelGenerator;
import org.hibernate.orm.tooling.gradle.metamodel.model.MetamodelClass;
import static org.hibernate.orm.tooling.gradle.HibernateOrmSpec.HIBERNATE;
/**
* Generates the "JPA static metamodel" from the domain model defined by the project
* via classes and possibly XML mappings
*
* @apiNote While there is also an annotation-processor that performs the same
* general function, that approach is limited in that it can only process compiled
* classes based on annotations. This task accounts for both classes and XML mappings
*/
public class JpaMetamodelGenerationTask extends DefaultTask {
public static final String DSL_NAME = "generateJpaMetamodel";
public static final String COMPILE_DSL_NAME = "compileJpaMetamodel";
private final HibernateOrmSpec ormSpec;
private final DirectoryProperty resourcesOutputDir;
private final SourceSet mainSourceSet;
@Inject
@SuppressWarnings( "UnstableApiUsage" )
public JpaMetamodelGenerationTask(
HibernateOrmSpec ormSpec,
SourceSet mainSourceSet,
JavaCompile mainCompileTask,
Project project) {
this.ormSpec = ormSpec;
dependsOn( mainCompileTask );
this.mainSourceSet = mainSourceSet;
final SourceSetOutput mainSourceSetOutput = mainSourceSet.getOutput();
resourcesOutputDir = project.getObjects().directoryProperty();
resourcesOutputDir.set( project.getLayout().dir( project.provider( mainSourceSetOutput::getResourcesDir ) ) );
}
@InputFiles
@SkipWhenEmpty
public FileCollection getJavaClassDirs() {
return mainSourceSet.getOutput();
}
@InputFiles
@SkipWhenEmpty
public DirectoryProperty getResourcesOutputDir() {
// for access to XML mappings
return resourcesOutputDir;
}
@OutputDirectory
public DirectoryProperty getGenerationOutputDirectory() {
return ormSpec.getJpaMetamodelSpec().getGenerationOutputDirectory();
}
@TaskAction
public void generateJpaMetamodel() {
final ClassLoader classLoader = determineUnitClassLoader( getProject(), mainSourceSet );
final PersistenceUnitInfoImpl unitInfo = new PersistenceUnitInfoImpl(
determineUnitUrl(),
generateIntegrationSettings(),
classLoader
);
getJavaClassDirs().forEach(
classesDir -> {
final ConfigurableFileTree files = getProject().fileTree( classesDir );
files.forEach(
file -> {
if ( file.getName().endsWith( ".class" ) ) {
final String className = Helper.determineClassName( classesDir, file );
unitInfo.addManagedClassName( className );
}
else if ( isMappingFile( file ) ) {
unitInfo.addMappingFile( file.getName() );
}
}
);
}
);
resourcesOutputDir.getAsFileTree().forEach(
file -> {
if ( isMappingFile( file ) ) {
unitInfo.addMappingFile( file.getName() );
}
}
);
JpaStaticMetamodelGenerator.processMetamodel( unitInfo, ormSpec.getJpaMetamodelSpec() );
}
private URL determineUnitUrl() {
try {
// NOTE : we just need *a* URL - we used the project dir
return getProject().getProjectDir().toURI().toURL();
}
catch (MalformedURLException e) {
throw new IllegalStateException( "Could not interpret project directory as URL" );
}
}
@SuppressWarnings( "UnstableApiUsage" )
private static ClassLoader determineUnitClassLoader(Project project, SourceSet mainSourceSet) {
final String compileJavaTaskName = mainSourceSet.getCompileJavaTaskName();
final JavaCompile javaCompileTask = (JavaCompile) project.getTasks().getByName( compileJavaTaskName );
final URL projectClassesDirUrl = toUrl( javaCompileTask.getDestinationDirectory().get().getAsFile() );
return new URLClassLoader( new URL[] { projectClassesDirUrl }, MetamodelClass.class.getClassLoader() );
}
private static URL toUrl(File file) {
final URI uri = file.toURI();
try {
return uri.toURL();
}
catch (MalformedURLException e) {
throw new GradleException( "Could not convert classpath entry into URL : " + file.getAbsolutePath(), e );
}
}
private Properties generateIntegrationSettings() {
final Properties settings = new Properties();
settings.put( "hibernate.temp.use_jdbc_metadata_defaults", "false" );
settings.put( AvailableSettings.DIALECT, "H2" );
settings.put( AvailableSettings.USE_SECOND_LEVEL_CACHE, false );
settings.put( AvailableSettings.USE_QUERY_CACHE, false );
return settings;
}
private boolean isMappingFile(File file) {
final String fileName = file.getName();
// convention
// - we could allow filters for flexibility?
return fileName.endsWith( ".hbm.xml" ) || fileName.endsWith( ".orm.xml" );
}
private static class PersistenceUnitInfoImpl implements PersistenceUnitInfo {
private final URL unitRoot;
private final Properties properties;
private final ClassLoader classLoader;
private final List<String> managedClassNames = new ArrayList<>();
private final List<String> mappingFileNames = new ArrayList<>();
public PersistenceUnitInfoImpl(URL unitRoot, Properties properties, ClassLoader classLoader) {
this.unitRoot = unitRoot;
this.properties = properties;
this.classLoader = classLoader;
}
@Override
public String getPersistenceUnitName() {
return "jpa-static-metamodel-gen";
}
@Override
public URL getPersistenceUnitRootUrl() {
return unitRoot;
}
@Override
public Properties getProperties() {
return properties;
}
@Override
public ClassLoader getClassLoader() {
return classLoader;
}
@Override
public List<String> getManagedClassNames() {
return managedClassNames;
}
public void addManagedClassName(String className) {
getManagedClassNames().add( className );
}
@Override
public List<String> getMappingFileNames() {
return mappingFileNames;
}
public void addMappingFile(String fileName) {
getMappingFileNames().add( fileName );
}
@Override
public String getPersistenceProviderClassName() {
return HibernatePersistenceProvider.class.getName();
}
@Override
public PersistenceUnitTransactionType getTransactionType() {
return null;
}
@Override
public DataSource getJtaDataSource() {
return null;
}
@Override
public DataSource getNonJtaDataSource() {
return null;
}
@Override
public List<URL> getJarFileUrls() {
return null;
}
@Override
public boolean excludeUnlistedClasses() {
return true;
}
@Override
public SharedCacheMode getSharedCacheMode() {
return null;
}
@Override
public ValidationMode getValidationMode() {
return null;
}
@Override
public String getPersistenceXMLSchemaVersion() {
return null;
}
@Override
public void addTransformer(ClassTransformer transformer) {
}
@Override
public ClassLoader getNewTempClassLoader() {
return null;
}
}
@SuppressWarnings( "UnstableApiUsage" )
public static void apply(HibernateOrmSpec ormDsl, SourceSet mainSourceSet, Project project) {
final String mainCompileTaskName = mainSourceSet.getCompileJavaTaskName();
final JavaCompile mainCompileTask = (JavaCompile) project.getTasks().getByName( mainCompileTaskName );
final JpaMetamodelGenerationTask genTask = project.getTasks().create(
DSL_NAME,
JpaMetamodelGenerationTask.class,
ormDsl,
mainSourceSet,
mainCompileTask,
project
);
genTask.setGroup( HIBERNATE );
genTask.setDescription( "Generates the JPA 'static metamodel'" );
genTask.dependsOn( mainCompileTask );
final Task compileResourcesTask = project.getTasks().getByName( "processResources" );
genTask.dependsOn( compileResourcesTask );
final JavaCompile compileJpaMetamodelTask = project.getTasks().create( COMPILE_DSL_NAME, JavaCompile.class );
compileJpaMetamodelTask.setGroup( HIBERNATE );
compileJpaMetamodelTask.setDescription( "Compiles the JPA static metamodel generated by `" + DSL_NAME + "`" );
compileJpaMetamodelTask.setSourceCompatibility( mainCompileTask.getSourceCompatibility() );
compileJpaMetamodelTask.setTargetCompatibility( mainCompileTask.getTargetCompatibility() );
genTask.finalizedBy( compileJpaMetamodelTask );
compileJpaMetamodelTask.dependsOn( genTask );
compileJpaMetamodelTask.source( project.files( ormDsl.getJpaMetamodelSpec().getGenerationOutputDirectory() ) );
compileJpaMetamodelTask.getDestinationDirectory().set( ormDsl.getJpaMetamodelSpec().getCompileOutputDirectory() );
compileJpaMetamodelTask.setClasspath(
project.getConfigurations().getByName( "runtimeClasspath" ).plus( mainSourceSet.getRuntimeClasspath() )
);
compileJpaMetamodelTask.doFirst(
(task) -> {
project.getLogger().lifecycle( "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" );
project.getLogger().lifecycle( "compileJpaMetamodel classpath" );
project.getLogger().lifecycle( "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" );
( (JavaCompile) task ).getClasspath().forEach(
entry -> project.getLogger().lifecycle( " > {}", entry.getAbsolutePath() )
);
project.getLogger().lifecycle( "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" );
}
);
}
}

View File

@ -0,0 +1,34 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.tooling.gradle.metamodel.model;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.Collection;
public class AttributeBag extends AttributeSupport {
private final Class<?> elementJavaType;
public AttributeBag(
MetamodelClass metamodelClass,
String attributeName,
Class<?> elementJavaType) {
super( metamodelClass, attributeName, Collection.class );
this.elementJavaType = elementJavaType;
}
@Override
public void renderAttributeType(BufferedWriter writer) throws IOException {
writer.write(
format(
"CollectionAttribute<%s,%s>",
getOwnerDomainClassName(),
elementJavaType.getName()
)
);
}
}

View File

@ -0,0 +1,34 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.tooling.gradle.metamodel.model;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.Set;
public class AttributeList extends AttributeSupport {
private final Class<?> elementJavaType;
public AttributeList(
MetamodelClass metamodelClass,
String attributeName,
Class<?> elementJavaType) {
super( metamodelClass, attributeName, Set.class );
this.elementJavaType = elementJavaType;
}
@Override
public void renderAttributeType(BufferedWriter writer) throws IOException {
writer.write(
format(
"ListAttribute<%s,%s>",
getOwnerDomainClassName(),
elementJavaType.getName()
)
);
}
}

View File

@ -0,0 +1,38 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.tooling.gradle.metamodel.model;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.Set;
public class AttributeMap extends AttributeSupport {
private final Class<?> keyJavaType;
private final Class<?> elementJavaType;
public AttributeMap(
MetamodelClass metamodelClass,
String attributeName,
Class<?> keyJavaType,
Class<?> elementJavaType) {
super( metamodelClass, attributeName, Set.class );
this.keyJavaType = keyJavaType;
this.elementJavaType = elementJavaType;
}
@Override
public void renderAttributeType(BufferedWriter writer) throws IOException {
writer.write(
format(
"MapAttribute<%s,%s,%s>",
getOwnerDomainClassName(),
keyJavaType.getName(),
elementJavaType.getName()
)
);
}
}

View File

@ -0,0 +1,34 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.tooling.gradle.metamodel.model;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.Set;
public class AttributeSet extends AttributeSupport {
private final Class<?> elementJavaType;
public AttributeSet(
MetamodelClass metamodelClass,
String attributeName,
Class<?> elementJavaType) {
super( metamodelClass, attributeName, Set.class );
this.elementJavaType = elementJavaType;
}
@Override
public void renderAttributeType(BufferedWriter writer) throws IOException {
writer.write(
format(
"SetAttribute<%s,%s>",
getOwnerDomainClassName(),
elementJavaType.getName()
)
);
}
}

View File

@ -0,0 +1,29 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.tooling.gradle.metamodel.model;
import java.io.BufferedWriter;
import java.io.IOException;
public class AttributeSingular extends AttributeSupport {
public AttributeSingular(MetamodelClass metamodelClass, String name, Class javaType) {
super( metamodelClass, name, javaType );
}
@Override
public void renderAttributeType(BufferedWriter writer) throws IOException {
// JPA stuff already imported
writer.write(
format(
"SingularAttribute<%s,%s>",
getOwnerDomainClassName(),
getAttributeJavaType().getName()
)
);
}
}

View File

@ -0,0 +1,75 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.tooling.gradle.metamodel.model;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.Locale;
import static java.lang.Character.LINE_SEPARATOR;
public abstract class AttributeSupport implements MetamodelAttribute {
private final MetamodelClass metamodelClass;
private final String name;
private final Class<?> javaType;
public AttributeSupport(
MetamodelClass metamodelClass,
String name,
Class<?> javaType) {
this.metamodelClass = metamodelClass;
this.name = name;
this.javaType = javaType;
}
@Override
public String getName() {
return name;
}
@Override
public Class<?> getAttributeJavaType() {
return javaType;
}
public String getOwnerDomainClassName() {
return metamodelClass.getMetamodelClassName();
}
@Override
public void renderJpaMembers(BufferedWriter writer) {
try {
writer.write( " public static volatile " );
renderAttributeType( writer );
writer.write( " " + name );
writer.write( ';' );
writer.write( LINE_SEPARATOR );
}
catch (IOException e) {
throw new IllegalStateException( "Problem writing attribute `" + metamodelClass.getMetamodelClassName() + "#" + name + "` to output stream", e );
}
}
public abstract void renderAttributeType(BufferedWriter writer) throws IOException;
protected String format(String pattern, Object... args) {
return String.format( Locale.ROOT, pattern, args );
}
@Override
public void renderNameConstant(BufferedWriter writer) {
try {
writer.write( " public static final String " );
writer.write( getName().toUpperCase( Locale.ROOT ) );
writer.write( " = \"" + getName() + "\";" );
writer.write( LINE_SEPARATOR );
}
catch (IOException e) {
throw new IllegalStateException( "Problem writing attribute `" + metamodelClass.getMetamodelClassName() + "#" + name + "` to output stream", e );
}
}
}

View File

@ -0,0 +1,105 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.tooling.gradle.metamodel.model;
import java.io.File;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.persistence.spi.PersistenceUnitInfo;
import org.gradle.api.file.Directory;
import org.gradle.api.file.RegularFile;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.jpa.boot.spi.Bootstrap;
import org.hibernate.jpa.boot.spi.EntityManagerFactoryBuilder;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.MappedSuperclass;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.orm.tooling.gradle.metamodel.JpaMetamodelGenerationSpec;
public class JpaStaticMetamodelGenerator {
public static void processMetamodel(
PersistenceUnitInfo persistenceUnitInfo,
JpaMetamodelGenerationSpec spec) {
EntityManagerFactoryBuilder target = Bootstrap.getEntityManagerFactoryBuilder( persistenceUnitInfo, Collections.emptyMap() );
try {
new JpaStaticMetamodelGenerator( spec, target.metadata() ).process();
}
finally {
target.cancel();
}
}
private final JpaMetamodelGenerationSpec spec;
private final MetadataImplementor metadata;
private final Directory generationOutputDirectory;
private final ObjectFactory objectFactory;
private final Set<String> processedDomainTypeNames = new HashSet<>();
private JpaStaticMetamodelGenerator(JpaMetamodelGenerationSpec spec, MetadataImplementor metadata) {
this.spec = spec;
this.metadata = metadata;
this.generationOutputDirectory = spec.getGenerationOutputDirectory().get();
this.objectFactory = new ObjectFactory( metadata );
}
private void process() {
final Set<MappedSuperclass> mappedSuperclasses = metadata.getMappedSuperclassMappingsCopy();
if ( mappedSuperclasses != null ) {
mappedSuperclasses.forEach( this::handleMappedClass );
}
final java.util.Collection<PersistentClass> entityBindings = metadata.getEntityBindings();
if ( entityBindings != null ) {
entityBindings.forEach( this::handlePersistentClass );
}
}
@SuppressWarnings( "unchecked" )
private void handleMappedClass(MappedSuperclass mappingDescriptor) {
final MetamodelClass metamodelClass = objectFactory.metamodelClass( mappingDescriptor );
handleManagedClass( metamodelClass, mappingDescriptor.getDeclaredPropertyIterator() );
}
@SuppressWarnings( "unchecked" )
private void handlePersistentClass(PersistentClass persistentClass) {
final MetamodelClass metamodelClass = objectFactory.metamodelClass( persistentClass );
handleManagedClass( metamodelClass, persistentClass.getDeclaredPropertyIterator() );
}
private void handleManagedClass(MetamodelClass metamodelClass, Iterator<Property> propertyIterator) {
if ( ! processedDomainTypeNames.add( metamodelClass.getDomainClassName() ) ) {
// already processed
return;
}
propertyIterator.forEachRemaining(
property -> metamodelClass.addAttribute(
objectFactory.attribute( property, property.getValue(), metamodelClass, this::handleEmbeddable )
)
);
final String replaced = metamodelClass.getMetamodelClassName().replace( '.', '/' );
final String metamodelClassJavaFileName = replaced + ".java";
final RegularFile metamodelClassJavaFile = generationOutputDirectory.file( metamodelClassJavaFileName );
final File metamodelClassJavaFileAsFile = metamodelClassJavaFile.getAsFile();
metamodelClass.writeToFile( metamodelClassJavaFileAsFile, spec );
}
@SuppressWarnings( "unchecked" )
private void handleEmbeddable(Component embeddedValueMapping) {
final MetamodelClass metamodelClass = objectFactory.metamodelClass( embeddedValueMapping );
handleManagedClass( metamodelClass, embeddedValueMapping.getPropertyIterator() );
}
}

View File

@ -0,0 +1,19 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.tooling.gradle.metamodel.model;
import java.io.BufferedWriter;
public interface MetamodelAttribute {
String getName();
Class<?> getAttributeJavaType();
void renderJpaMembers(BufferedWriter writer);
void renderNameConstant(BufferedWriter writer);
}

View File

@ -0,0 +1,156 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.tooling.gradle.metamodel.model;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.text.DateFormat;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Locale;
import java.util.Set;
import java.util.TreeSet;
import org.gradle.api.GradleException;
import org.gradle.api.JavaVersion;
import org.hibernate.orm.tooling.gradle.metamodel.JpaMetamodelGenerationSpec;
import static java.lang.Character.LINE_SEPARATOR;
/**
* Descriptor for a class in the JPA static metamodel being generated
*/
public class MetamodelClass {
private final String domainClassName;
private final String metamodelClassName;
private final String metamodelSuperClassName;
private final TreeSet<MetamodelAttribute> attributes = new TreeSet<>( Comparator.comparing( MetamodelAttribute::getName ) );
public MetamodelClass(String domainClassName, String superTypeName) {
this.domainClassName = domainClassName;
this.metamodelClassName = domainClassName + "_";
this.metamodelSuperClassName = superTypeName == null ? null : superTypeName + "_";
}
public String getMetamodelClassName() {
return metamodelClassName;
}
public String getDomainClassName() {
return domainClassName;
}
public void addAttribute(MetamodelAttribute attribute) {
assert attribute != null;
attributes.add( attribute );
}
public void writeToFile(File outputFile, JpaMetamodelGenerationSpec spec) {
prepareOutputFile( outputFile );
final Path path = outputFile.toPath();
try ( final BufferedWriter writer = Files.newBufferedWriter( path, StandardCharsets.UTF_8, StandardOpenOption.WRITE ) ) {
renderClassPreamble( writer, spec );
writer.write( LINE_SEPARATOR );
writer.write( "public abstract class " + metamodelClassName );
if ( metamodelSuperClassName != null ) {
writer.write( " extends " + metamodelSuperClassName );
}
writer.write( " {" );
writer.write( LINE_SEPARATOR );
writer.write( LINE_SEPARATOR );
writer.write( " // Attribute name constants" );
writer.write( LINE_SEPARATOR );
attributes.forEach( attribute -> attribute.renderNameConstant( writer ) );
writer.write( LINE_SEPARATOR );
writer.write( LINE_SEPARATOR );
writer.write( " // JPA static metamodel fields" );
writer.write( LINE_SEPARATOR );
attributes.forEach( attribute -> attribute.renderJpaMembers( writer ) );
writer.write( LINE_SEPARATOR );
writer.write( "}" );
writer.write( LINE_SEPARATOR );
}
catch (IOException e) {
throw new IllegalStateException( "Unable to open file : " + outputFile.getAbsolutePath(), e );
}
}
private void renderClassPreamble(BufferedWriter writer, JpaMetamodelGenerationSpec spec) throws IOException {
final String nowFormatted = DateFormat.getDateInstance().format( new Date() );
writer.write( "// Generated by Hibernate ORM Gradle tooling - " + nowFormatted );
writer.write( LINE_SEPARATOR );
writer.write( LINE_SEPARATOR );
writer.write( "import javax.persistence.*;" );
writer.write( LINE_SEPARATOR );
writer.write( "import javax.persistence.metamodel.*;" );
writer.write( LINE_SEPARATOR );
writer.write( LINE_SEPARATOR );
writer.write( "/** JPA static metamodel descriptor for the `" + domainClassName + "` domain class */" );
writer.write( LINE_SEPARATOR );
// first, the generated annotation
if ( spec.getApplyGeneratedAnnotation().getOrElse( true ) ) {
final JavaVersion javaVersion = spec.getTargetJavaVersionAccess().getOrElse( JavaVersion.current() );
final String qualifiedAnnotationName = javaVersion.isJava9Compatible()
? "javax.annotation.processing.Generated"
: "javax.annotation.Generated";
final String generatedAnnotationFragment = String.format(
Locale.ROOT,
"@%s( value=\"%s\", date=\"%s\", comments=\"%s\" )",
qualifiedAnnotationName,
JpaStaticMetamodelGenerator.class.getName(),
nowFormatted,
"Generated by Hibernate ORM Gradle tooling"
);
writer.write( generatedAnnotationFragment );
writer.write( LINE_SEPARATOR );
}
final Set<String> suppressions = spec.getSuppressions().getOrElse( Collections.emptySet() );
if ( ! suppressions.isEmpty() ) {
writer.write( "@SuppressWarnings( { " );
for ( String suppression : suppressions ) {
writer.write( "\"" + suppression + "\", " );
}
writer.write( " } )" );
writer.write( LINE_SEPARATOR );
}
writer.write( "@StaticMetamodel( " + domainClassName + ".class )" );
}
@SuppressWarnings( "ResultOfMethodCallIgnored" )
public void prepareOutputFile(File outputFile) {
try {
outputFile.getParentFile().mkdirs();
outputFile.createNewFile();
}
catch (IOException e) {
throw new GradleException( "Unable to prepare output file `" + outputFile.getAbsolutePath() + "`", e );
}
}
}

View File

@ -0,0 +1,237 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.tooling.gradle.metamodel.model;
import java.util.Locale;
import java.util.function.Consumer;
import org.gradle.api.GradleException;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.mapping.Any;
import org.hibernate.mapping.Bag;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.DependantValue;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.List;
import org.hibernate.mapping.Map;
import org.hibernate.mapping.MappedSuperclass;
import org.hibernate.mapping.OneToMany;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Set;
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.Value;
/**
* @author Steve Ebersole
*/
public class ObjectFactory {
private final MetadataImplementor metadata;
public ObjectFactory(MetadataImplementor metadata) {
this.metadata = metadata;
}
public MetamodelClass metamodelClass(PersistentClass entityDescriptor) {
return new MetamodelClass( entityDescriptor.getMappedClass().getName(), determineSuperTypeName( entityDescriptor ) );
}
private String determineSuperTypeName(PersistentClass entityDescriptor) {
if ( entityDescriptor.getSuperMappedSuperclass() != null ) {
return entityDescriptor.getSuperMappedSuperclass().getMappedClass().getName();
}
if ( entityDescriptor.getSuperclass() != null ) {
return entityDescriptor.getSuperclass().getMappedClass().getName();
}
return null;
}
public MetamodelClass metamodelClass(MappedSuperclass mappedSuperclassDescriptor) {
return new MetamodelClass( mappedSuperclassDescriptor.getMappedClass().getName(), determineSuperTypeName( mappedSuperclassDescriptor ) );
}
private String determineSuperTypeName(MappedSuperclass mappedSuperclassDescriptor) {
if ( mappedSuperclassDescriptor.getSuperMappedSuperclass() != null ) {
return mappedSuperclassDescriptor.getSuperMappedSuperclass().getMappedClass().getName();
}
if ( mappedSuperclassDescriptor.getSuperPersistentClass() != null ) {
return mappedSuperclassDescriptor.getSuperPersistentClass().getMappedClass().getName();
}
return null;
}
public MetamodelClass metamodelClass(Component embeddedMapping) {
return new MetamodelClass( embeddedMapping.getComponentClassName(), null );
}
public MetamodelAttribute attribute(
Property property,
Value propertyValueMapping,
MetamodelClass metamodelClass,
Consumer<Component> componentConsumer) {
if ( propertyValueMapping instanceof DependantValue ) {
final DependantValue dependantValue = (DependantValue) propertyValueMapping;
final KeyValue wrappedValue = dependantValue.getWrappedValue();
return attribute( property, wrappedValue, metamodelClass, componentConsumer );
}
if ( propertyValueMapping instanceof Collection ) {
return pluralAttribute( property, (Collection) propertyValueMapping, metamodelClass, componentConsumer );
}
final Class<?> propertyJavaType = determineSingularJavaType( property, propertyValueMapping, metamodelClass, componentConsumer );
return new AttributeSingular( metamodelClass, property.getName(), propertyJavaType );
}
private Class<?> determineSingularJavaType(
Property property,
Value propertyValueMapping,
MetamodelClass metamodelClass,
Consumer<Component> componentConsumer) {
if ( propertyValueMapping instanceof BasicValue ) {
final BasicValue basicValue = (BasicValue) propertyValueMapping;
return basicValue.resolve().getDomainJavaDescriptor().getJavaType();
}
if ( propertyValueMapping instanceof Component ) {
final Component component = (Component) propertyValueMapping;
componentConsumer.accept( component );
return component.getComponentClass();
}
if ( propertyValueMapping instanceof Any ) {
return Object.class;
}
if ( propertyValueMapping instanceof ToOne ) {
final ToOne toOne = (ToOne) propertyValueMapping;
final String referencedEntityName = toOne.getReferencedEntityName();
final PersistentClass entityBinding = metadata.getEntityBinding( referencedEntityName );
final Class<?> mappedClass = entityBinding.getMappedClass();
if ( mappedClass == null ) {
throw new GradleException(
String.format(
Locale.ROOT,
"Could not determine ToOne java type : %s#%s",
metamodelClass.getDomainClassName(),
property.getName()
)
);
}
return mappedClass;
}
propertyValueMapping.setTypeUsingReflection( metamodelClass.getDomainClassName(), property.getName() );
return propertyValueMapping.getType().getReturnedClass();
}
private MetamodelAttribute pluralAttribute(
Property property,
Collection collectionMapping,
MetamodelClass metamodelClass,
Consumer<Component> componentConsumer) {
if ( collectionMapping instanceof Set ) {
return new AttributeSet(
metamodelClass,
property.getName(),
determineCollectionPartJavaType( property, collectionMapping.getElement(), metamodelClass, componentConsumer )
);
}
if ( collectionMapping instanceof Bag ) {
return new AttributeBag(
metamodelClass,
property.getName(),
determineCollectionPartJavaType( property, collectionMapping.getElement(), metamodelClass, componentConsumer )
);
}
if ( collectionMapping instanceof List ) {
return new AttributeList(
metamodelClass,
property.getName(),
determineCollectionPartJavaType( property, collectionMapping.getElement(), metamodelClass, componentConsumer )
);
}
if ( collectionMapping instanceof Map ) {
return new AttributeMap(
metamodelClass,
property.getName(),
determineCollectionPartJavaType( property, ( (Map) collectionMapping ).getIndex(), metamodelClass, componentConsumer ),
determineCollectionPartJavaType( property, collectionMapping.getElement(), metamodelClass, componentConsumer )
);
}
throw new UnsupportedOperationException( "Unsupported plural value type : " + collectionMapping.getClass().getName() );
}
private Class<?> determineCollectionPartJavaType(
Property property,
Value partJavaType,
MetamodelClass metamodelClass,
Consumer<Component> componentConsumer) {
if ( partJavaType instanceof DependantValue ) {
final DependantValue dependantValue = (DependantValue) partJavaType;
final KeyValue wrappedValue = dependantValue.getWrappedValue();
return determineCollectionPartJavaType( property, wrappedValue, metamodelClass, componentConsumer );
}
if ( partJavaType instanceof BasicValue ) {
final BasicValue basicValue = (BasicValue) partJavaType;
return basicValue.resolve().getDomainJavaDescriptor().getJavaType();
}
if ( partJavaType instanceof Component ) {
final Component component = (Component) partJavaType;
componentConsumer.accept( component );
return component.getComponentClass();
}
if ( partJavaType instanceof Any ) {
return Object.class;
}
if ( partJavaType instanceof OneToMany ) {
final OneToMany oneToMany = (OneToMany) partJavaType;
final PersistentClass associatedClass = oneToMany.getAssociatedClass();
return associatedClass.getMappedClass();
}
if ( partJavaType instanceof ToOne ) {
final ToOne toOne = (ToOne) partJavaType;
final String referencedEntityName = toOne.getReferencedEntityName();
if ( referencedEntityName != null ) {
final PersistentClass entityBinding = metadata.getEntityBinding( referencedEntityName );
if ( entityBinding != null ) {
final Class<?> mappedClass = entityBinding.getMappedClass();
if ( mappedClass != null ) {
return mappedClass;
}
}
}
throw new GradleException(
String.format(
Locale.ROOT,
"Could not determine ToOne java type : %s#%s",
metamodelClass.getDomainClassName(),
property.getName()
)
);
}
return partJavaType.getType().getReturnedClass();
}
}

View File

@ -1,7 +0,0 @@
#
# Hibernate, Relational Persistence for Idiomatic Java
#
# License: GNU Lesser General Public License (LGPL), version 2.1 or later.
# See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
#
implementation-class=org.hibernate.orm.tooling.gradle.HibernatePlugin

View File

@ -0,0 +1 @@
@hibernateVersion@

View File

@ -1,89 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.orm.tooling.gradle
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.testfixtures.ProjectBuilder
import org.junit.Test
import static org.junit.Assert.assertNotNull
/**
* Test what we can. ProjectBuilder is better than nothing, but still quited limited in what
* you can test (e.g. you cannot test task execution).
*
* @author Steve Ebersole
*/
class HibernatePluginTest {
@Test
public void testHibernatePluginAddsExtension() {
Project project = ProjectBuilder.builder().build()
project.plugins.apply 'org.hibernate.orm'
assertNotNull( project.extensions.findByName( "hibernate" ) )
}
@Test
public void testHibernateExtensionConfig() {
Project project = ProjectBuilder.builder().build()
project.plugins.apply 'org.hibernate.orm'
project.extensions.findByType( HibernateExtension.class ).enhance {
enableLazyInitialization = true
enableDirtyTracking = true
enableAssociationManagement = false
enableExtendedEnhancement = false
}
}
@Test
public void testEnhanceTask() {
Project project = ProjectBuilder.builder().build()
project.plugins.apply 'org.hibernate.orm'
def task = project.tasks.create( "finishHim", EnhanceTask )
task.options {
enableLazyInitialization = true
enableDirtyTracking = true
enableAssociationManagement = false
enableExtendedEnhancement = false
}
task.sourceSets = project.getConvention().getPlugin( JavaPluginConvention ).sourceSets.main
task.enhance()
}
@Test
public void testTaskAction() {
Project project = ProjectBuilder.builder().build()
project.plugins.apply 'org.hibernate.orm'
// the test sourceSet
def sourceSet = project.getConvention().getPlugin( JavaPluginConvention ).sourceSets.test;
// The compile task for the test sourceSet
final JavaCompile compileTestTask = project.getTasks().findByName( sourceSet.getCompileJavaTaskName() );
// Lets add our enhancer to enhance the test classes after the test are compiled
compileTestTask.doLast {
EnhancementHelper.enhance(
sourceSet,
project.extensions.findByType( HibernateExtension.class ).enhance,
project
)
}
// TODO find how to do this in Gradle 5
// the class-level javadoc says it's not possible, and it was there in Gradle 4.x...
//compileTestTask.execute()
}
}

View File

@ -0,0 +1,91 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.tooling.gradle;
import org.gradle.testkit.runner.BuildResult;
import org.gradle.testkit.runner.BuildTask;
import org.gradle.testkit.runner.GradleRunner;
import org.gradle.testkit.runner.TaskOutcome;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import com.github.sebersole.testkit.Project;
import com.github.sebersole.testkit.ProjectScope;
import com.github.sebersole.testkit.TestKit;
import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* Test what we can. TestKit is better than nothing, but still somewhat limited in what
* you can test in my experience
*
* @author Steve Ebersole
*/
@TestKit
class HibernateOrmPluginTest {
@Test
public void testEnhancementTaskAsFinalizer(@Project( "simple" ) ProjectScope projectScope) {
final GradleRunner gradleRunner = projectScope.createGradleRunner( "clean", "compileJava" );
final BuildResult result = gradleRunner.build();
final BuildTask task = result.task( ":hibernateEnhance" );
assert task != null;
assertThat(
task.getOutcome(),
anyOf( is( TaskOutcome.SUCCESS ), is( TaskOutcome.UP_TO_DATE ) )
);
}
@Test
public void testEnhancementTask(@Project( "simple" ) ProjectScope projectScope) {
final GradleRunner gradleRunner = projectScope.createGradleRunner(
"clean",
"hibernateEnhance"
);
final BuildResult result = gradleRunner.build();
final BuildTask task = result.task( ":hibernateEnhance" );
assert task != null;
assertThat( task.getOutcome(), is( TaskOutcome.SUCCESS ) );
}
@Test
public void testEnhancementTaskUpToDate(@Project( "simple" ) ProjectScope projectScope) {
final GradleRunner gradleRunner = projectScope.createGradleRunner(
"clean",
"hibernateEnhance"
);
final BuildResult result = gradleRunner.build();
final BuildTask task = result.task( ":hibernateEnhance" );
assert task != null;
assertThat(
task.getOutcome(),
anyOf( is( TaskOutcome.SUCCESS ), is( TaskOutcome.UP_TO_DATE ) )
);
}
@Test
// @Disabled( "Problem with ClassPathAndModulePathAggregatedServiceLoader and loading Java services" )
public void testJpaMetamodelGen(@Project( "simple" ) ProjectScope projectScope) {
final GradleRunner gradleRunner = projectScope.createGradleRunner(
"clean",
"generateJpaMetamodel"
);
final BuildResult result = gradleRunner.build();
final BuildTask task = result.task( ":generateJpaMetamodel" );
assert task != null;
assertThat(
task.getOutcome(),
anyOf( is( TaskOutcome.SUCCESS ), is( TaskOutcome.UP_TO_DATE ) )
);
}
}

View File

@ -0,0 +1,29 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
plugins {
id 'java'
id 'org.hibernate.orm'
}
repositories {
mavenCentral()
maven {
name 'jboss-snapshots-repository'
url 'https://repository.jboss.org/nexus/content/repositories/snapshots'
}
}
hibernate {
enhancement {
lazyInitialization( true )
dirtyTracking = true
}
jpaMetamodel {
}
}

View File

@ -0,0 +1,23 @@
import javax.persistence.Embeddable;
@Embeddable
public class TheEmbeddable {
private String valueOne;
private String valueTwo;
public String getValueOne() {
return valueOne;
}
public void setValueOne(String valueOne) {
this.valueOne = valueOne;
}
public String getValueTwo() {
return valueTwo;
}
public void setValueTwo(String valueTwo) {
this.valueTwo = valueTwo;
}
}

View File

@ -0,0 +1,79 @@
import javax.persistence.ElementCollection;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import java.util.Set;
@Entity
public class TheEntity {
@Id
private Integer id;
private String name;
@Embedded
private TheEmbeddable theEmbeddable;
@ManyToOne
@JoinColumn
private TheEntity theManyToOne;
@OneToMany( mappedBy = "theManyToOne" )
private Set<TheEntity> theOneToMany;
@ElementCollection
@JoinColumn( name = "owner_id" )
private Set<TheEmbeddable> theEmbeddableCollection;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public TheEmbeddable getTheEmbeddable() {
return theEmbeddable;
}
public void setTheEmbeddable(TheEmbeddable theEmbeddable) {
this.theEmbeddable = theEmbeddable;
}
public TheEntity getTheManyToOne() {
return theManyToOne;
}
public void setTheManyToOne(TheEntity theManyToOne) {
this.theManyToOne = theManyToOne;
}
public Set<TheEntity> getTheOneToMany() {
return theOneToMany;
}
public void setTheOneToMany(Set<TheEntity> theOneToMany) {
this.theOneToMany = theOneToMany;
}
public Set<TheEmbeddable> getTheEmbeddableCollection() {
return theEmbeddableCollection;
}
public void setTheEmbeddableCollection(Set<TheEmbeddable> theEmbeddableCollection) {
this.theEmbeddableCollection = theEmbeddableCollection;
}
}

View File

@ -0,0 +1,6 @@
A template for projects wanting to use Hibernate ORM.
Useful to either:
1. Bootstrap a new user project
2. Bootstrap a test-case project

View File

@ -0,0 +1,112 @@
import org.apache.tools.ant.filters.ReplaceTokens
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
plugins {
id 'base'
// for publishing snapshots
id 'maven-publish'
id 'org.hibernate.build.maven-repo-auth'
// publishing to BinTray
id "com.jfrog.bintray"
id "nu.studer.credentials" version "2.1"
}
// NOTE : Had trouble using the `distribution` plugin, so manually using Jar/Tar tasks
version = '0.1'
ext {
processedTemplateDir = project.layout.buildDirectory.dir('resources/template')
archiveDir = project.layout.buildDirectory.dir('distributions')
}
task processTemplateResources(type:Copy) {
inputs.files( 'src/template/resources' )
outputs.dir( processedTemplateDir )
description = 'Copies the template sources into the build dir, performing some replacements'
from( 'src/template/resources' ) {
filter( ReplaceTokens, tokens: [ 'ormVersion' : project.version.toString() ] )
}
into processedTemplateDir.get().asFile
}
task templateTgz(type:Tar) {
description = 'Bundles the template project into a TGZ archive'
inputs.files( processedTemplateDir )
outputs.dir( archiveDir )
dependsOn project.tasks.processTemplateResources
compression = Compression.GZIP
from processedTemplateDir.get().asFile
destinationDirectory = archiveDir
}
task templateZip(type:Zip) {
description = 'Bundles the template project into a Zip archive'
inputs.files( processedTemplateDir )
outputs.dir( archiveDir )
dependsOn project.tasks.processTemplateResources
from processedTemplateDir.get().asFile
destinationDirectory = archiveDir
}
bintray {
user = credentials.'personal.bintray.user'
key = credentials.'personal.bintray.key'
filesSpec {
from templateTgz
from templateZip
}
pkg {
userOrg = 'hibernate'
repo = 'generic'
name = 'orm-project-template'
}
}
task assembleDist( dependsOn: [tasks.templateTgz, tasks.templateZip] )
task release( dependsOn: tasks.assembleDist )
tasks.publish {
dependsOn tasks.assembleDist
}
tasks.bintrayUpload {
dependsOn tasks.assembleDist
doFirst {
if ( credentials.'personal.bintray.user' == null ) {
throw new GradleException( "BinTray user not known, cannot perform upload" );
}
if ( credentials.'personal.bintray.key' == null ) {
throw new GradleException( "BinTray API key not known, cannot perform upload" );
}
}
}
if ( version.toString().endsWith( "-SNAPSHOT" ) ) {
tasks.bintrayUpload.enabled = false
tasks.release.dependsOn tasks.publish
}
else {
tasks.publish.enabled = false
tasks.release.dependsOn tasks.bintrayUpload
}

View File

@ -0,0 +1,5 @@
A template project to help bootstrap a Gradle project with support for Hibernate
specific extensions.
This might be useful for new users wanting to get started as well as a quick way to
bootstrap project for test cases (bug reports, etc)

View File

@ -0,0 +1,80 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
// ###################################################################################
// again, needed to be able to consume `org.hibernate.orm` plugin SNAPSHOTS
buildscript {
configurations {
classpath {
resolutionStrategy {
cacheChangingModulesFor(0, java.util.concurrent.TimeUnit.SECONDS )
}
}
}
}
// ###################################################################################
plugins {
java
// todo : find a way to inject this version
// - this is yet another example of where lazybones
// (or proper Gradle build-init feature) would be
// incredibly useful. Same with groupId, package-name,
// etc.
id( "org.hibernate.orm" ) version "@ormVersion@"
}
group = "your.org"
version = "the-version"
repositories {
mavenCentral()
}
dependencies {
val ormVersion = "@ormVersion@"
val junit5Version = "5.3.1"
val h2Version = "1.4.199"
implementation( "org.hibernate.orm", "hibernate-core", ormVersion )
testImplementation( "org.hibernate.orm", "hibernate-testing", ormVersion )
testImplementation( "org.junit.jupiter", "junit-jupiter-api", junit5Version )
testImplementation( "org.junit.jupiter", "junit-jupiter-params", junit5Version )
testRuntimeOnly( "org.junit.jupiter", "junit-jupiter-engine", junit5Version )
testRuntimeOnly( "com.h2database", "h2", h2Version )
testRuntimeOnly( "org.jboss.logging", "jboss-logging", "3.3.2.Final" )
testRuntimeOnly( "log4j", "log4j", "1.2.17" )
}
hibernate {
enhancement {
// all false by default
lazyInitialization = true
dirtyTracking = true
}
}
tasks {
test {
useJUnitPlatform()
}
}
task( "compile" ) {
dependsOn( tasks.compileJava )
dependsOn( tasks.compileTestJava )
dependsOn( tasks.processResources )
dependsOn( tasks.processTestResources )
}
configure<JavaPluginConvention> {
sourceCompatibility = JavaVersion.VERSION_1_8
}

View File

@ -0,0 +1,32 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
rootProject.name = "my-hibernate-project"
// ###################################################################################
// A lot of magic to be able to consumer SNAPSHOT versions of the Hibernate ORM plugin...
pluginManagement {
repositories {
gradlePluginPortal()
maven {
name = "jboss-snapshots-repository"
url = uri( "https://repository.jboss.org/nexus/content/repositories/snapshots" )
}
}
resolutionStrategy {
eachPlugin {
if ( requested.id.namespace == "org.hibernate"
&& requested.id.name == "orm"
&& requested.version.orEmpty().endsWith("-SNAPSHOT" ) ) {
val notation = "org.hibernate.orm:hibernate-gradle-plugin:${requested.version}"
logger.lifecycle( "Swapping SNAPSHOT version of plugin : {}", notation )
useModule( notation )
}
}
}
}
// ###################################################################################

View File

@ -0,0 +1,38 @@
package org.your.domain;
import javax.persistence.Entity;
import javax.persistence.Id;
/**
* A simple example entity
*/
@Entity
public class SimpleEntity {
@Id
private Integer id;
private String name;
private SimpleEntity() {
}
public SimpleEntity(Integer id) {
this.id = id;
}
public SimpleEntity(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,53 @@
package org.your.domain;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static com.arjuna.ats.internal.jdbc.recovery.JDBCXARecovery.PASSWORD;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hibernate.cfg.AvailableSettings.URL;
import static org.hibernate.cfg.AvailableSettings.USER;
/**
* Tests for SimpleEntity
*/
@ServiceRegistry(
settings = {
// can define settings here, or in `hibernate.properties` file
@ServiceRegistry.Setting( name = URL, value = "jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;LOCK_TIMEOUT=10000" ),
@ServiceRegistry.Setting( name = USER, value = "sa" ),
@ServiceRegistry.Setting( name = PASSWORD, value = "" )
}
)
@DomainModel( annotatedClasses = SimpleEntity.class )
@SessionFactory()
public class EntityTests {
@Test
public void basicTest(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
final SimpleEntity entity = session.createQuery( "from SimpleEntity", SimpleEntity.class ).uniqueResult();
assertThat( entity, notNullValue() );
}
);
}
@BeforeEach
public void createTestData(SessionFactoryScope scope) {
scope.inTransaction(
session -> session.persist( new SimpleEntity( 1, "the first" ) )
);
}
@BeforeEach
public void dropTestData(SessionFactoryScope scope) {
scope.inTransaction(
session -> session.createQuery( "delete SimpleEntity" ).executeUpdate()
);
}
}

View File

@ -0,0 +1,7 @@
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
log4j.rootLogger=warn, stdout
log4j.logger.org.hibernate=info