diff --git a/jpalite-quarkus-extension/deployment/lombok.config b/jpalite-quarkus-extension/deployment/lombok.config new file mode 100644 index 0000000..2066d75 --- /dev/null +++ b/jpalite-quarkus-extension/deployment/lombok.config @@ -0,0 +1 @@ +lombok.log.fieldName=LOG diff --git a/jpalite-quarkus-extension/deployment/pom.xml b/jpalite-quarkus-extension/deployment/pom.xml index 4c26882..4741807 100644 --- a/jpalite-quarkus-extension/deployment/pom.xml +++ b/jpalite-quarkus-extension/deployment/pom.xml @@ -16,7 +16,8 @@ ~ limitations under the License. --> - 4.0.0 @@ -59,6 +60,10 @@ jpalite-extension ${project.version} + + org.projectlombok + lombok + diff --git a/jpalite-quarkus-extension/deployment/src/main/java/io/jpalite/extension/deployment/JPALiteExtensionProcessor.java b/jpalite-quarkus-extension/deployment/src/main/java/io/jpalite/extension/deployment/JPALiteExtensionProcessor.java index fb58fc6..babc5b6 100644 --- a/jpalite-quarkus-extension/deployment/src/main/java/io/jpalite/extension/deployment/JPALiteExtensionProcessor.java +++ b/jpalite-quarkus-extension/deployment/src/main/java/io/jpalite/extension/deployment/JPALiteExtensionProcessor.java @@ -17,15 +17,38 @@ package io.jpalite.extension.deployment; -import io.jpalite.agroal.AgroalDataSourceProvider; -import io.jpalite.extension.PropertyPersistenceUnitProvider; +import io.jpalite.*; +import io.jpalite.extension.JPALiteConfigMapping; +import io.jpalite.extension.JPALiteRecorder; +import io.quarkus.arc.deployment.SyntheticBeanBuildItem; +import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Default; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.transaction.TransactionManager; +import jakarta.transaction.TransactionSynchronizationRegistry; +import org.jboss.jandex.ClassType; +import org.jboss.jandex.DotName; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT; class JPALiteExtensionProcessor { + private static final DotName ENTITY_MANAGER_FACTORY = DotName.createSimple("jakarta.persistence.EntityManagerFactory"); + public static final DotName ENTITY_MANAGER = DotName.createSimple("jakarta.persistence.EntityManager"); + private static final Logger LOG = LoggerFactory.getLogger(JPALiteExtensionProcessor.class); + + private static final String DEFAULT_NAME = ""; private static final String FEATURE = "jpalite-extension"; @@ -46,8 +69,102 @@ NativeImageResourceBuildItem nativeImageResourceBuildItem() @BuildStep ReflectiveClassBuildItem reflection() { - return ReflectiveClassBuildItem.builder(AgroalDataSourceProvider.class, - PropertyPersistenceUnitProvider.class) + return ReflectiveClassBuildItem.builder(DataSourceProvider.class, + PersistenceUnitProvider.class, + FieldConvertType.class, + EntityMetaDataManager.class) .build(); } + + @Record(RUNTIME_INIT) + @BuildStep + void generateJPABeans(JPALiteRecorder recorder, + JPALiteConfigMapping jpaConfigMapping, + BuildProducer syntheticBeanBuildItemBuildProducer) + { + if (jpaConfigMapping.defaultPersistenceUnit() != null) { + //Define the default EntityManagerFactory producer + syntheticBeanBuildItemBuildProducer + .produce(createSyntheticBean(DEFAULT_NAME, + false, + EntityManagerFactory.class, + List.of(ENTITY_MANAGER_FACTORY) + , true) + .createWith(recorder.entityManagerFactorySupplier(DEFAULT_NAME)) + .done()); + + //Define the default EntityManager producer + syntheticBeanBuildItemBuildProducer + .produce(createSyntheticBean(DEFAULT_NAME, + false, + EntityManager.class, + List.of(ENTITY_MANAGER) + , true) + .createWith(recorder.entityManagerSupplier(DEFAULT_NAME)) + .addInjectionPoint(ClassType.create(DotName.createSimple(TransactionManager.class))) + .addInjectionPoint(ClassType.create(DotName.createSimple(TransactionSynchronizationRegistry.class))) + .done()); + }//if + + + if (jpaConfigMapping.namedPersistenceUnits() != null) { + for (String unitName : jpaConfigMapping.namedPersistenceUnits().keySet()) { + //Define the named EntityManagerFactory producer + syntheticBeanBuildItemBuildProducer + .produce(createSyntheticBean(unitName, + true, + EntityManagerFactory.class, + List.of(ENTITY_MANAGER_FACTORY) + , false) + .createWith(recorder.entityManagerFactorySupplier(unitName)) + .done()); + + //Define the named EntityManager producer + syntheticBeanBuildItemBuildProducer + .produce(createSyntheticBean(unitName, + true, + EntityManager.class, + List.of(ENTITY_MANAGER) + , false) + .createWith(recorder.entityManagerSupplier(unitName)) + .addInjectionPoint(ClassType.create(DotName.createSimple(TransactionManager.class))) + .addInjectionPoint(ClassType.create(DotName.createSimple(TransactionSynchronizationRegistry.class))) + .done()); + + } + }//if + } + + private static SyntheticBeanBuildItem.ExtendedBeanConfigurator createSyntheticBean(String persistenceUnitName, + boolean isNamedPersistenceUnit, + Class type, + List allExposedTypes, + boolean defaultBean) + { + LOG.info("Creating Synthetic Bean for {}(\"{}\")", type.getSimpleName(), persistenceUnitName); + SyntheticBeanBuildItem.ExtendedBeanConfigurator configurator = SyntheticBeanBuildItem + .configure(type) + .scope(ApplicationScoped.class) + .unremovable() + .setRuntimeInit(); + + for (DotName exposedType : allExposedTypes) { + configurator.addType(exposedType); + } + + if (defaultBean) { + configurator.defaultBean(); + } + + if (isNamedPersistenceUnit) { +// configurator.addQualifier(Default.class); + configurator.addQualifier().annotation(PersistenceUnit.class).addValue("value", persistenceUnitName).done(); + } + else { + configurator.addQualifier(Default.class); + configurator.addQualifier().annotation(PersistenceUnit.class).done(); + } + + return configurator; + } } diff --git a/jpalite-quarkus-extension/runtime/src/main/java/io/jpalite/PersistenceUnit.java b/jpalite-quarkus-extension/runtime/src/main/java/io/jpalite/PersistenceUnit.java index 511e3ff..db5f4e6 100644 --- a/jpalite-quarkus-extension/runtime/src/main/java/io/jpalite/PersistenceUnit.java +++ b/jpalite-quarkus-extension/runtime/src/main/java/io/jpalite/PersistenceUnit.java @@ -38,7 +38,7 @@ @SuppressWarnings("ClassExplicitlyAnnotation") - class PersistenceUnitLiteral extends AnnotationLiteral implements PersistenceUnit + final class PersistenceUnitLiteral extends AnnotationLiteral implements PersistenceUnit { private final String name; diff --git a/jpalite-quarkus-extension/runtime/src/main/java/io/jpalite/extension/JPALiteConfigMapping.java b/jpalite-quarkus-extension/runtime/src/main/java/io/jpalite/extension/JPALiteConfigMapping.java index 8a2b327..878a346 100644 --- a/jpalite-quarkus-extension/runtime/src/main/java/io/jpalite/extension/JPALiteConfigMapping.java +++ b/jpalite-quarkus-extension/runtime/src/main/java/io/jpalite/extension/JPALiteConfigMapping.java @@ -32,7 +32,7 @@ import java.util.Map; @ConfigMapping(prefix = "jpalite.persistenceUnit") -@ConfigRoot(phase = ConfigPhase.RUN_TIME) +@ConfigRoot(phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED) public interface JPALiteConfigMapping { /** diff --git a/jpalite-quarkus-extension/runtime/src/main/java/io/jpalite/extension/JPALiteRecorder.java b/jpalite-quarkus-extension/runtime/src/main/java/io/jpalite/extension/JPALiteRecorder.java new file mode 100644 index 0000000..d25730f --- /dev/null +++ b/jpalite-quarkus-extension/runtime/src/main/java/io/jpalite/extension/JPALiteRecorder.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.jpalite.extension; + +import io.quarkus.arc.SyntheticCreationalContext; +import io.quarkus.runtime.annotations.Recorder; +import jakarta.inject.Inject; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.Persistence; +import jakarta.transaction.TransactionManager; +import jakarta.transaction.TransactionSynchronizationRegistry; +import lombok.extern.slf4j.Slf4j; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +@Slf4j +@Recorder +public class JPALiteRecorder +{ + private final Map entityManagerFactoryList = new ConcurrentHashMap<>(); + + @Inject + TransactionManager transactionManager; + + @Inject + TransactionSynchronizationRegistry transactionSynchronizationRegistry; + + public Function, EntityManagerFactory> entityManagerFactorySupplier(String persistenceUnitName) + { + return context -> entityManagerFactoryList.computeIfAbsent(persistenceUnitName, Persistence::createEntityManagerFactory); + } + + public EntityManager getEntityManager(String persistenceUnit) + { + EntityManagerFactory factory = entityManagerFactoryList.computeIfAbsent(persistenceUnit, Persistence::createEntityManagerFactory); + return new TransactionScopedEntityManagerImpl(factory, + transactionManager, + transactionSynchronizationRegistry); + }//getEntityManager + + public Function, EntityManager> entityManagerSupplier(String persistenceUnitName) + { + return context -> { + TransactionManager transactionManager = context.getInjectedReference(TransactionManager.class); + TransactionSynchronizationRegistry transactionSynchronizationRegistry = context.getInjectedReference(TransactionSynchronizationRegistry.class); + EntityManagerFactory factory = entityManagerFactoryList.computeIfAbsent(persistenceUnitName, Persistence::createEntityManagerFactory); + return new TransactionScopedEntityManagerImpl(factory, + transactionManager, + transactionSynchronizationRegistry); + }; + } +} diff --git a/jpalite-quarkus-extension/runtime/src/main/java/io/jpalite/extension/PersistenceProducer.java b/jpalite-quarkus-extension/runtime/src/main/java/io/jpalite/extension/PersistenceProducer.java deleted file mode 100644 index a00783f..0000000 --- a/jpalite-quarkus-extension/runtime/src/main/java/io/jpalite/extension/PersistenceProducer.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.jpalite.extension; - -import io.jpalite.PersistenceUnit; -import io.quarkus.arc.Unremovable; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.inject.Produces; -import jakarta.enterprise.inject.spi.InjectionPoint; -import jakarta.inject.Inject; -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityManagerFactory; -import jakarta.persistence.Persistence; -import jakarta.transaction.TransactionManager; -import jakarta.transaction.TransactionSynchronizationRegistry; -import lombok.extern.slf4j.Slf4j; -import org.eclipse.microprofile.config.Config; -import org.eclipse.microprofile.config.ConfigProvider; - -import java.lang.annotation.Annotation; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; - -@Unremovable -@ApplicationScoped -@Slf4j -public class PersistenceProducer -{ - private final Map entityManagerFactoryList = new ConcurrentHashMap<>(); - - @Inject - TransactionManager transactionManager; - - @Inject - TransactionSynchronizationRegistry transactionSynchronizationRegistry; - - public EntityManagerFactory getEntityManagerFactory(String persistenceUnit) - { - - String persistenceUnitName = ""; - if (persistenceUnit != null && !persistenceUnit.isBlank()) { - if (persistenceUnit.startsWith("${")) { - String key = persistenceUnit.substring(2, persistenceUnit.lastIndexOf('}')); - Config configProvider = ConfigProvider.getConfig(); - Optional value = configProvider.getOptionalValue(key, String.class); - if (value.isPresent()) { - LOG.debug("Persistence unit name is defined using a variable {} = {} ", persistenceUnitName, value.get()); - persistenceUnitName = value.get(); - }//if - else { - LOG.debug("Persistence unit name defined using a variable {} but variable is not defined", persistenceUnitName); - }//else - }//if - else { - persistenceUnitName = persistenceUnit; - } - }//if - - LOG.debug("Producing new Entity Manager Factory using @PersistenceUnit(\"{}\")", persistenceUnitName); - return entityManagerFactoryList.computeIfAbsent(persistenceUnitName, Persistence::createEntityManagerFactory); - } - - public EntityManager getEntityManager(String persistenceUnit) - { - return new TransactionScopedEntityManagerImpl(getEntityManagerFactory(persistenceUnit), - transactionManager, - transactionSynchronizationRegistry); - }//getEntityManager - - @Unremovable - @Produces - @PersistenceUnit - public EntityManagerFactory injectEntityManagerFactory(InjectionPoint injectionPoint) - { - String persistenceUnitName = ""; - - Annotation qualifier = null; - if (injectionPoint.getAnnotated() != null) { - qualifier = injectionPoint.getAnnotated().getAnnotations() - .stream() - .filter(PersistenceUnit.class::isInstance) - .findFirst() - .orElse(null); - }//if - - if (qualifier == null) { - /** - * If PersistenceUnit annotation is not found, check if it was not provided - * as a qualifier - */ - qualifier = injectionPoint.getQualifiers() - .stream() - .filter(PersistenceUnit.class::isInstance) - .findFirst() - .orElse(null); - }//if - - if (qualifier instanceof PersistenceUnit persistenceUnit) { - persistenceUnitName = persistenceUnit.value(); - }//if - - return getEntityManagerFactory(persistenceUnitName); - }//getEntityManagerFactory - - @Unremovable - @Produces - @PersistenceUnit - public EntityManager injectEntityManager(InjectionPoint injectionPoint) - { - return new TransactionScopedEntityManagerImpl(injectEntityManagerFactory(injectionPoint), - transactionManager, - transactionSynchronizationRegistry); - }//getEntityManager - - @Unremovable - @Produces - public EntityManagerFactory injectDefaultEntityManagerFactory(InjectionPoint injectionPoint) - { - return injectEntityManagerFactory(injectionPoint); - } - - @Unremovable - @Produces - public EntityManager injectDefaultEntityManager(InjectionPoint injectionPoint) - { - return new TransactionScopedEntityManagerImpl(injectEntityManagerFactory(injectionPoint), - transactionManager, - transactionSynchronizationRegistry); - }//getEntityManager -} diff --git a/jpalite-repository/lombok.config b/jpalite-repository/lombok.config new file mode 100644 index 0000000..2066d75 --- /dev/null +++ b/jpalite-repository/lombok.config @@ -0,0 +1 @@ +lombok.log.fieldName=LOG diff --git a/jpalite-repository/pom.xml b/jpalite-repository/pom.xml index a6b94e6..cba57b7 100644 --- a/jpalite-repository/pom.xml +++ b/jpalite-repository/pom.xml @@ -67,5 +67,9 @@ auto-service 1.1.1 + + org.projectlombok + lombok + diff --git a/jpalite-repository/src/main/java/io/jpalite/repository/JPALiteRepositoryProcessor.java b/jpalite-repository/src/main/java/io/jpalite/repository/JPALiteRepositoryProcessor.java index b913659..c6acda4 100644 --- a/jpalite-repository/src/main/java/io/jpalite/repository/JPALiteRepositoryProcessor.java +++ b/jpalite-repository/src/main/java/io/jpalite/repository/JPALiteRepositoryProcessor.java @@ -146,12 +146,6 @@ private void addJpaRepository(PrintWriter out, DeclaredType jpaRepository) out.println("}"); out.println(""); - out.println("public " + argType + " clone(" + argType + " entity) {"); - out.println(" EntityManager em = getEntityManager();"); - out.println(" return ((JPALiteEntityManager)em).clone(entity);"); - out.println("}"); - out.println(""); - out.println("public void delete(" + argType + " entity) {"); out.println(" EntityManager em = getEntityManager();"); out.println(" em.remove(entity);"); @@ -408,15 +402,10 @@ private void generateRepo(TypeElement repoElement, Repository annotation) throws out.println("import jakarta.transaction.Transactional;"); out.println("import jakarta.annotation.Generated;"); out.println("import jakarta.persistence.*;"); - out.println("import java.util.Collections;"); - out.println("import java.util.List;"); - out.println("import java.util.HashMap;"); - out.println("import java.util.Map;"); - out.println("import io.jpalite.extension.PersistenceProducer;"); - out.println("import io.jpalite.JPALiteEntityManager;"); + out.println("import java.util.*;"); out.println("import io.jpalite.PersistenceUnit;"); out.println("import io.jpalite.repository.*;"); - out.println("import io.jpalite.EntityState;"); +// out.println("import io.jpalite.EntityState;"); out.println(); SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT); @@ -435,18 +424,25 @@ private void generateRepo(TypeElement repoElement, Repository annotation) throws out.print(repoElement.getQualifiedName()); out.println(" {"); -// out.println(" @Inject"); -// out.print(" @PersistenceUnit(\""); -// out.print(annotation.persistenceUnit()); -// out.println("\")"); -// out.println(" EntityManager em;"); + + if (!annotation.persistenceUnit().startsWith("${")) { + out.println(" @Inject"); + out.print(" @PersistenceUnit(\""); + out.print(annotation.persistenceUnit()); + out.println("\")"); + out.println(" EntityManager em;"); + }//if out.println("public EntityManager getEntityManager() {"); - out.println(" PersistenceProducer producer = Arc.container().instance(PersistenceProducer.class).get();"); - out.print(" return producer.getEntityManager(\""); - out.print(annotation.persistenceUnit()); - out.println("\");"); -// out.println(" return em;"); + if (annotation.persistenceUnit().startsWith("${")) { + out.println(" io.jpalite.extension.JPALiteRecorder producer = Arc.container().instance(io.jpalite.extension.JPALiteRecorder.class).get();"); + out.print(" return producer.getEntityManager(JpaRepositoryUtil.getPersistenceUnitName(\""); + out.print(annotation.persistenceUnit()); + out.println("\"));"); + }//if + else { + out.println(" return em;"); + }//else out.println("}"); repoElement.getInterfaces() diff --git a/jpalite-repository/src/main/java/io/jpalite/repository/JpaRepository.java b/jpalite-repository/src/main/java/io/jpalite/repository/JpaRepository.java index c9eaaa9..37c5b54 100644 --- a/jpalite-repository/src/main/java/io/jpalite/repository/JpaRepository.java +++ b/jpalite-repository/src/main/java/io/jpalite/repository/JpaRepository.java @@ -97,14 +97,6 @@ public interface JpaRepository extends RepositoryBase */ E getReference(I id); - /** - * Create a clone of an existing entity. - * - * @param entity - * @return The new entity - */ - E clone(E entity); - /** * Delete an attached entity from the repository. * diff --git a/jpalite-repository/src/main/java/io/jpalite/repository/JpaRepositoryUtil.java b/jpalite-repository/src/main/java/io/jpalite/repository/JpaRepositoryUtil.java new file mode 100644 index 0000000..e200ab4 --- /dev/null +++ b/jpalite-repository/src/main/java/io/jpalite/repository/JpaRepositoryUtil.java @@ -0,0 +1,39 @@ +package io.jpalite.repository; + +import lombok.extern.slf4j.Slf4j; +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.ConfigProvider; + +import java.util.Optional; + +@Slf4j +public class JpaRepositoryUtil +{ + private JpaRepositoryUtil() + { + //Prevent the class from being instantiated + } + + public static String getPersistenceUnitName(String persistenceUnit) + { + String persistenceUnitName = ""; + if (persistenceUnit != null && !persistenceUnit.isBlank()) { + if (persistenceUnit.startsWith("${")) { + String key = persistenceUnit.substring(2, persistenceUnit.lastIndexOf('}')); + Config configProvider = ConfigProvider.getConfig(); + Optional value = configProvider.getOptionalValue(key, String.class); + if (value.isPresent()) { + LOG.debug("Persistence unit name is defined using a variable {} = {} ", persistenceUnitName, value.get()); + persistenceUnitName = value.get(); + }//if + else { + LOG.warn("Persistence unit name defined using a variable {} but variable is not defined", persistenceUnitName); + }//else + }//if + else { + persistenceUnitName = persistenceUnit; + } + }//if + return persistenceUnitName; + } +}