diff --git a/README.adoc b/README.adoc index 506a45a5..029f6c85 100644 --- a/README.adoc +++ b/README.adoc @@ -359,6 +359,10 @@ The Criteria API can be used via CriteriaDocumentTemplate. ---- +== Driver for Jakarta Persistence entities + +Provides a driver for Eclipse JNoSQL that supports Jakarta Persistence entities over a Jakarta Persistence provider. This project also contains a runner for the Jakarta Data TCK. + == TCK Runners The Eclipse JNoSQL project provides Technology Compatibility Kit (TCK) runners for Jakarta Data. These runners allow you to run the TCK tests against the Eclipse JNoSQL implementation to verify its compatibility with the Jakarta Data specifications. diff --git a/jnosql-jakarta-persistence/README.adoc b/jnosql-jakarta-persistence/README.adoc new file mode 100644 index 00000000..fa12dbe0 --- /dev/null +++ b/jnosql-jakarta-persistence/README.adoc @@ -0,0 +1,52 @@ += Eclipse JNoSQL driver for Jakarta Persistence +:toc: auto + +This project provides a driver for Eclipse JNoSQL that supports Jakarta Persistence entities over a Jakarta Persistence provider. + +Sub projects: + +* link:jnosql-jakarta-persistence-driver[Jakarta Persistence Driver] - the actual implementation +* link:jnosql-jakarta-persistence-data-tck-runner[Jakarta Data TCK Runner] - the project to run the Jakarta Data TCK + +=== How To Install + +You can use either the Maven or Gradle dependencies: + +[source,xml] +---- + + org.eclipse.jnosql.mapping + jnosql-jakarta-persistence + +---- + +Then you need to provide an EntityManager instance using a CDI producer, e.g.: + +[source,java] +---- +@ApplicationScoped +public class EntityManagerProducer { + @Produces + @ApplicationScoped + public EntityManager createEntityManager() { + return Persistence.createEntityManagerFactory("testPersistenceUnit") + .createEntityManager(); + } + + public void closeEntityManager(@Disposes EntityManager entityManager) { + entityManager.close(); + } +} +---- + +Then you need to configure the persistence unit ("testPersistenceUnit") via standard Jakarta Persistence means, e.g.: + +[source,xml] +---- + + + ... custom configuration ... + + + +For an example configuration, with EclipseLink and DerbyDB, look into the test setup of the link:jnosql-jakarta-persistence-connector[Jakarta Persistence Connector] project. \ No newline at end of file diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/README.adoc b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/README.adoc new file mode 100644 index 00000000..383c8943 --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/README.adoc @@ -0,0 +1,17 @@ += Jakarta Data TCK Eclipse JNoSQL Implementation via Jakarta Persistence +:toc: auto + +This project runs the Jakarta Data Technology Compatibility Kit (TCK) on standalone mode with Eclipse JNoSQL with the Jakarta Persistence connector. It uses EclipseLink and Derby DB as an implementation for Jakarta Persistence. Before running this project it is recommended to read the documentation located in the base link:https://github.com/jakartaee/data/blob/main/tck-dist/src/main/asciidoc/data-tck-reference-guide.adoc[TCK distribution project, _target=_blank]. + +== Overview + +This project is configured specifically to allow the feature developers to run the TCK against the Eclipse JNoSQL implementation with the Jakarta Persistence backend. + +== Running the TCK for Verification + +Run the following command to execute the TCK: + +[source,shell] +---- +mvn clean test -B +---- diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/logging.properties b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/logging.properties new file mode 100644 index 00000000..14c8ba20 --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/logging.properties @@ -0,0 +1,41 @@ +#Handlers we plan to use +handlers=java.util.logging.FileHandler,java.util.logging.ConsoleHandler + +#Global logger - By default only log warnings +.level=WARNING + +#Jakarta Data TCK logger - By default log everything for ee.jakarta.tck.data +ee.jakarta.tck.data.level=ALL + +# Arquillian and JNoSQL - By default log everything, might reduce after development is complete. +org.jboss.level=ALL +org.eclipse.jnosql.level=ALL + +#Formatting for the simple formatter +java.util.logging.SimpleFormatter.class.log=true +java.util.logging.SimpleFormatter.class.full=false +java.util.logging.SimpleFormatter.class.length=10 + +java.util.logging.SimpleFormatter.level.log=true + +java.util.logging.SimpleFormatter.method.log=true +java.util.logging.SimpleFormatter.method.length=30 + +java.util.logging.SimpleFormatter.thread.log=true +java.util.logging.SimpleFormatter.thread.length=3 + +java.util.logging.SimpleFormatter.time.log=true +java.util.logging.SimpleFormatter.time.format=[MM/dd/yyyy HH:mm:ss:SSS z] + +java.util.logging.SimpleFormatter.format=[%1$tF %1$tT] %4$.1s %3$s %5$s %n + +# Log warnings to console +java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter +java.util.logging.ConsoleHandler.level=WARNING + +# Log everything else to file +java.util.logging.FileHandler.pattern=target/DataTCK%g%u.log +java.util.logging.FileHandler.limit = 500000 +java.util.logging.FileHandler.count = 5 +java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter +java.util.logging.FileHandler.level=CONFIG \ No newline at end of file diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/pom.xml b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/pom.xml new file mode 100644 index 00000000..db6b02d8 --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/pom.xml @@ -0,0 +1,201 @@ + + + + + 4.0.0 + Eclipse JNoSQL Jakarta Persistence Runner For Jakarta Data TCK + + + org.eclipse.jnosql.mapping + jnosql-jakarta-persistence-parent + 1.1.2-SNAPSHOT + + jnosql-jakarta-persistence-data-tck-runner + + + + + sonatype-nexus-staging + Sonatype Nexus Staging + https://jakarta.oss.sonatype.org/content/repositories/staging/ + + true + + + true + + + + + + ${project.basedir}/target + + + 1.0.0 + 1.0.1 + 6.1.0 + 4.1.0 + + 1.8.0.Final + 5.10.2 + 1.2.6 + 2.3 + 4.0.3.Final + 1.19.8 + + ${project.basedir}/logging.properties + jdk + + + + + + + + + org.junit + junit-bom + ${junit.version} + pom + import + + + + + + + + + jakarta.data + jakarta.data-tck + ${jakarta.data.tck.version} + + + + jakarta.data + jakarta.data-api + ${jakarta.data.version} + + + + org.eclipse.persistence + org.eclipse.persistence.jpa + 5.0.0-SNAPSHOT + test + + + org.apache.derby + derby + 10.17.1.0 + test + + + org.apache.derby + derbytools + 10.17.1.0 + + + org.apache.derby + derbyclient + 10.17.1.0 + + + org.eclipse.jnosql.mapping + jnosql-jakarta-persistence + ${project.version} + + + + org.junit.jupiter + junit-jupiter + ${junit.version} + + + jakarta.tck + sigtest-maven-plugin + ${sigtest.version} + + + + org.jboss.weld + weld-junit5 + ${weld.junit5.version} + + + + org.jboss.shrinkwrap + shrinkwrap-api + ${shrinkwrap.version} + + + org.jboss.arquillian.junit5 + arquillian-junit5-core + ${arquillian.version} + + + jakarta.servlet + jakarta.servlet-api + ${jakarta.servlet.version} + + + + jakarta.enterprise + jakarta.enterprise.cdi-api + + + + org.testcontainers + testcontainers + ${testcontainers.version} + test + + + + + + ${targetDirectory} + + + maven-surefire-plugin + 3.0.0 + + false + true + + true + true + + + ${logging.config} + target/tck-classes + + ${project.build.directory}/dependency/jakarta.data-api-${jakarta.data.api.version}.jar + + true + + ${included.groups} + standalone + + + + + \ No newline at end of file diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/src/test/java/org/eclipse/jnosql/tck/jakartapersistence/EntityManagerProducer.java b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/src/test/java/org/eclipse/jnosql/tck/jakartapersistence/EntityManagerProducer.java new file mode 100644 index 00000000..93e98914 --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/src/test/java/org/eclipse/jnosql/tck/jakartapersistence/EntityManagerProducer.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Ondro Mihalyi + */ +package org.eclipse.jnosql.tck.jakartapersistence; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Disposes; +import jakarta.enterprise.inject.Produces; +import jakarta.persistence.EntityManager; +import jakarta.persistence.Persistence; + +@ApplicationScoped +public class EntityManagerProducer { + @Produces + @ApplicationScoped + public EntityManager createEntityManager() { + return Persistence.createEntityManagerFactory("testPersistenceUnit") + .createEntityManager(); + } + + public void closeEntityManager(@Disposes EntityManager entityManager) { + entityManager.close(); + } +} diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/src/test/java/org/eclipse/jnosql/tck/jakartapersistence/JNoSqlEntitySelectedTests.java b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/src/test/java/org/eclipse/jnosql/tck/jakartapersistence/JNoSqlEntitySelectedTests.java new file mode 100644 index 00000000..4f1d5c6c --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/src/test/java/org/eclipse/jnosql/tck/jakartapersistence/JNoSqlEntitySelectedTests.java @@ -0,0 +1,520 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Ondro Mihalyi + */ +package org.eclipse.jnosql.tck.jakartapersistence; + +import ee.jakarta.tck.data.standalone.entity.EntityTests; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.document.DocumentTemplate; +import org.eclipse.jnosql.mapping.document.DocumentTemplateProducer; +import org.eclipse.jnosql.mapping.document.spi.DocumentExtension; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; + +import org.eclipse.jnosql.jakartapersistence.communication.PersistenceDatabaseManager; +import org.eclipse.jnosql.jakartapersistence.mapping.PersistenceDocumentTemplate; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +// selected failing tests that can be worked on next +@Disabled +@EnableAutoWeld +@AddPackages(value = {Converters.class, EntityConverter.class, DocumentTemplate.class}) +@AddPackages(DocumentTemplateProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, DocumentExtension.class}) +@AddPackages({PersistenceDocumentTemplate.class, PersistenceDatabaseManager.class}) +@AddPackages({JNoSqlEntitySelectedTests.class, EntityTests.class}) +@ExtendWith(TransactionExtension.class) +public class JNoSqlEntitySelectedTests extends EntityTests { + + @Override +// assertion failed +// @Test + public void testVarargsSort() { + super.testVarargsSort(); + } + + @Override + @Test + public void testUpdateQueryWithWhereClause() { + super.testUpdateQueryWithWhereClause(); + } + + @Override + @Test + public void testUpdateQueryWithoutWhereClause() { + super.testUpdateQueryWithoutWhereClause(); + } + + @Override +// assertion failed +// @Test + public void testTrue() { + super.testTrue(); + } + + @Override +// assertion failed +// @Test + public void testThirdAndFourthSlicesOf5() { + super.testThirdAndFourthSlicesOf5(); + } + + @Override +// assertion failed +// @Test + public void testThirdAndFourthPagesOf10() { + super.testThirdAndFourthPagesOf10(); + } + + @Override + @Test + public void testStreamsFromList() { + super.testStreamsFromList(); + } + + @Override +// EndsWith +// @Test + public void testStaticMetamodelDescendingSortsPreGenerated() { + super.testStaticMetamodelDescendingSortsPreGenerated(); + } + + @Override + @Test + public void testStaticMetamodelDescendingSorts() { + super.testStaticMetamodelDescendingSorts(); + } + + @Override + @Test + public void testStaticMetamodelAttributeNamesPreGenerated() { + super.testStaticMetamodelAttributeNamesPreGenerated(); + } + + @Override + @Test + public void testStaticMetamodelAttributeNames() { + super.testStaticMetamodelAttributeNames(); + } + + @Override +// EndsWith not supported +// @Test + public void testStaticMetamodelAscendingSortsPreGenerated() { + super.testStaticMetamodelAscendingSortsPreGenerated(); + } + + @Override +// assertion failed +// @Test + public void testStaticMetamodelAscendingSorts() { + super.testStaticMetamodelAscendingSorts(); + } + + @Override + @Test + public void testSliceOfNothing() { + super.testSliceOfNothing(); + } + + @Override +// IgnoreCase +// @Test + public void testSingleEntity() { + super.testSingleEntity(); + } + + @Override +// Query not supported +// @Test + public void testQueryWithParenthesis() { + super.testQueryWithParenthesis(); + } + + @Override +// syntax error +// @Test + public void testQueryWithOr() { + super.testQueryWithOr(); + } + + @Override +// query not supported +// @Test + public void testQueryWithNull() { + super.testQueryWithNull(); + } + + @Override +// Query not supported +// @Test + public void testQueryWithNot() { + super.testQueryWithNot(); + } + + @Override +// Not supported by JNoSQL yet +// @Test + public void testPrimaryEntityClassDeterminedByLifeCycleMethods() { + super.testPrimaryEntityClassDeterminedByLifeCycleMethods(); + } + + @Override +// Query not supported +// @Test + public void testPartialQuerySelectAndOrderBy() { + super.testPartialQuerySelectAndOrderBy(); + } + + @Override +// Queyr not supported +// @Test + public void testPartialQueryOrderBy() { + super.testPartialQueryOrderBy(); + } + + @Override +// Not supported by JNoSQL yet +// @Test + public void testPageOfNothing() { + super.testPageOfNothing(); + } + + @Override +// assertion failed +// @Test + public void testOrderByHasPrecedenceOverSorts() { + super.testOrderByHasPrecedenceOverSorts(); + } + + @Override +// assertion failed +// @Test + public void testOrderByHasPrecedenceOverPageRequestSorts() { + super.testOrderByHasPrecedenceOverPageRequestSorts(); + } + + @Override + @Test + public void testOr() { + super.testOr(); + } + + @Override +// ClassCastException +// @Test + public void testNot() { + super.testNot(); + } + + @Override +// non unique result +// @Test + public void testNonUniqueResultException() { + super.testNonUniqueResultException(); + } + + @Override +// ClassCastException +// @Test + public void testMixedSort() { + super.testMixedSort(); + } + + @Override +// Query not supported +// @Test + public void testLiteralTrue() { + super.testLiteralTrue(); + } + + @Override +// Query not supported +// @Test + public void testLiteralString() { + super.testLiteralString(); + } + + @Override +// Query not supported +// @Test + public void testLiteralInteger() { + super.testLiteralInteger(); + } + + @Override +// Query not supported +// @Test + public void testLiteralEnumAndLiteralFalse() { + super.testLiteralEnumAndLiteralFalse(); + } + + @Override +// assertion failed +// @Test + public void testLimitToOneResult() { + super.testLimitToOneResult(); + } + + @Override +// assertion failed +// @Test + public void testLimitedRange() { + super.testLimitedRange(); + } + + @Override +// assertion failed +// @Test + public void testLimit() { + super.testLimit(); + } + + @Override + @Test + public void testLessThanWithCount() { + super.testLessThanWithCount(); + } + + @Override + @Test + public void testCursoredPageWithoutTotalOfNothing() { + super.testCursoredPageWithoutTotalOfNothing(); + } + + @Override + @Test + public void testCursoredPageWithoutTotalOf9FromCursor() { + super.testCursoredPageWithoutTotalOf9FromCursor(); + } + + @Override + @Test + public void testCursoredPageOfNothing() { + super.testCursoredPageOfNothing(); + } + + @Override + @Test + public void testCursoredPageOf7FromCursor() { + super.testCursoredPageOf7FromCursor(); + } + + @Override +// IgnoreCase not supported +// @Test + public void testIgnoreCase() { + super.testIgnoreCase(); + } + + @Override +// assertion failed +// @Test + public void testIn() { + super.testIn(); + } + + @Override + @Test + public void testGreaterThanEqualExists() { + super.testGreaterThanEqualExists(); + } + + @Override +// assertion failed +// @Test + public void testFirstSliceOf5() { + super.testFirstSliceOf5(); + } + + @Override +// assertion failed +// @Test + public void testFirstPageOf10() { + super.testFirstPageOf10(); + } + + @Override + @Test + public void testFirstCursoredPageWithoutTotalOf6AndNextPages() { + super.testFirstCursoredPageWithoutTotalOf6AndNextPages(); + } + + @Override + @Test + public void testFirstCursoredPageOf8AndNextPages() { + super.testFirstCursoredPageOf8AndNextPages(); + } + + @Override +// assertion failed +// @Test + public void testFindPage() { + super.testFindPage(); + } + + @Override + @Test + public void testFindOptional() { + super.testFindOptional(); + } + + @Override + @Test + public void testFindOne() { + super.testFindOne(); + } + + @Override + @Test + public void testFindList() { + super.testFindList(); + } + + @Override +// EndsWith not supported +// @Test + public void testFindFirst3() { + super.testFindFirst3(); + } + + @Override +// Syntax +// @Test + public void testFindFirst() { + super.testFindFirst(); + } + + @Override +// assertion failed +// @Test + public void testFindAllWithPagination() { + super.testFindAllWithPagination(); + } + + @Override +// assertion failed +// @Test + public void testFinalSliceOfUpTo5() { + super.testFinalSliceOfUpTo5(); + } + + @Override +// assertion failed +// @Test + public void testFinalPageOfUpTo10() { + super.testFinalPageOfUpTo10(); + } + + @Override + @Test + public void testFalse() { + super.testFalse(); + } + + @Override +// IgnoreCase not supported +// @Test + public void testEmptyResultException() { + super.testEmptyResultException(); + } + + @Override +// Query not supported +// @Test + public void testEmptyQuery() { + super.testEmptyQuery(); + } + + @Override +// assertion failed +// @Test + public void testDescendingSort() { + super.testDescendingSort(); + } + + @Override +// assertion failed +// @Test + public void testDefaultMethod() { + super.testDefaultMethod(); + } + + @Override +// IgnoreCase +// @Test + public void testDataRepository() { + super.testDataRepository(); + } + + @Override +// Contains not supported by JNoSQL +// @Test + public void testContainsInString() { + super.testContainsInString(); + } + + @Override + @Test + public void testCommonInterfaceQueries() { + super.testCommonInterfaceQueries(); + } + + @Override + @Test + public void testBy() { + super.testBy(); + } + + @Override +// assertion failed +// @Test + public void testBeyondFinalSlice() { + super.testBeyondFinalSlice(); + } + + @Override +// assertion failed +// @Test + public void testBeyondFinalPage() { + super.testBeyondFinalPage(); + } + + @Override + @Test + public void testBasicRepositoryMethods() { + super.testBasicRepositoryMethods(); + } + + @Override + @Test + public void testBasicRepositoryBuiltInMethods() { + super.testBasicRepositoryBuiltInMethods(); + } + + @Override +// assertion failed +// @Test + public void testBasicRepository() { + super.testBasicRepository(); + } + +} diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/src/test/java/org/eclipse/jnosql/tck/jakartapersistence/JNoSqlEntityTests.java b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/src/test/java/org/eclipse/jnosql/tck/jakartapersistence/JNoSqlEntityTests.java new file mode 100644 index 00000000..5550365f --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/src/test/java/org/eclipse/jnosql/tck/jakartapersistence/JNoSqlEntityTests.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Ondro Mihalyi + */ +package org.eclipse.jnosql.tck.jakartapersistence; + +import ee.jakarta.tck.data.standalone.entity.EntityTests; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.document.DocumentTemplate; +import org.eclipse.jnosql.mapping.document.DocumentTemplateProducer; +import org.eclipse.jnosql.mapping.document.spi.DocumentExtension; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; + +import org.eclipse.jnosql.jakartapersistence.communication.PersistenceDatabaseManager; +import org.eclipse.jnosql.jakartapersistence.mapping.PersistenceDocumentTemplate; +import org.junit.jupiter.api.extension.ExtendWith; + +//@Disabled +@EnableAutoWeld +@AddPackages(value = {Converters.class, EntityConverter.class, DocumentTemplate.class}) +@AddPackages(DocumentTemplateProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, DocumentExtension.class}) +@AddPackages({PersistenceDocumentTemplate.class, PersistenceDatabaseManager.class}) +@AddPackages({JNoSqlEntityTests.class, EntityTests.class}) +@ExtendWith(TransactionExtension.class) +public class JNoSqlEntityTests extends EntityTests { + +} diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/src/test/java/org/eclipse/jnosql/tck/jakartapersistence/JNoSqlPersistenceEntityTests.java b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/src/test/java/org/eclipse/jnosql/tck/jakartapersistence/JNoSqlPersistenceEntityTests.java new file mode 100644 index 00000000..48a22be1 --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/src/test/java/org/eclipse/jnosql/tck/jakartapersistence/JNoSqlPersistenceEntityTests.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Ondro Mihalyi + */ +package org.eclipse.jnosql.tck.jakartapersistence; + +import ee.jakarta.tck.data.standalone.persistence.PersistenceEntityTests; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.document.DocumentTemplate; +import org.eclipse.jnosql.mapping.document.DocumentTemplateProducer; +import org.eclipse.jnosql.mapping.document.spi.DocumentExtension; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; + + + +import org.eclipse.jnosql.jakartapersistence.communication.PersistenceDatabaseManager; +import org.eclipse.jnosql.jakartapersistence.mapping.PersistenceDocumentTemplate; +import org.eclipse.jnosql.jakartapersistence.mapping.spi.JakartaPersistenceExtension; +import org.junit.jupiter.api.extension.ExtendWith; + +//@Disabled +@EnableAutoWeld +@AddPackages(value = {Converters.class, EntityConverter.class, DocumentTemplate.class}) +@AddPackages(DocumentTemplateProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, DocumentExtension.class, JakartaPersistenceExtension.class}) +@AddPackages({PersistenceDocumentTemplate.class, PersistenceDatabaseManager.class}) +@AddPackages({JNoSqlPersistenceEntityTests.class, PersistenceEntityTests.class}) +@ExtendWith(TransactionExtension.class) +public class JNoSqlPersistenceEntityTests extends PersistenceEntityTests { + +} diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/src/test/java/org/eclipse/jnosql/tck/jakartapersistence/JNoSqlSignatureTests.java b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/src/test/java/org/eclipse/jnosql/tck/jakartapersistence/JNoSqlSignatureTests.java new file mode 100644 index 00000000..a0bd51f4 --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/src/test/java/org/eclipse/jnosql/tck/jakartapersistence/JNoSqlSignatureTests.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Ondro Mihalyi + */ +package org.eclipse.jnosql.tck.jakartapersistence; + +import ee.jakarta.tck.data.standalone.signature.SignatureTests; + +public class JNoSqlSignatureTests extends SignatureTests { + +} diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/src/test/java/org/eclipse/jnosql/tck/jakartapersistence/TransactionExtension.java b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/src/test/java/org/eclipse/jnosql/tck/jakartapersistence/TransactionExtension.java new file mode 100644 index 00000000..3c5ff22b --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/src/test/java/org/eclipse/jnosql/tck/jakartapersistence/TransactionExtension.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Ondro Mihalyi + */ +package org.eclipse.jnosql.tck.jakartapersistence; + +import jakarta.enterprise.inject.spi.CDI; +import jakarta.persistence.EntityManager; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +public class TransactionExtension implements BeforeEachCallback, AfterEachCallback { + + @Override + public void beforeEach(ExtensionContext context) throws Exception { + getEntityManager().getTransaction().begin(); + } + + private static EntityManager getEntityManager() { + return CDI.current().select(EntityManager.class).get(); + } + + @Override + public void afterEach(ExtensionContext context) throws Exception { + getEntityManager().getTransaction().commit(); + } + +} diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/src/test/resources/META-INF/persistence.xml b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/src/test/resources/META-INF/persistence.xml new file mode 100644 index 00000000..dc6d75b6 --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-data-tck-runner/src/test/resources/META-INF/persistence.xml @@ -0,0 +1,48 @@ + + + + + org.eclipse.persistence.jpa.PersistenceProvider + ee.jakarta.tck.data.core.cdi.Person + ee.jakarta.tck.data.framework.read.only.AsciiCharacter + ee.jakarta.tck.data.framework.read.only.NaturalNumber + ee.jakarta.tck.data.standalone.entity.Box + ee.jakarta.tck.data.standalone.entity.Coordinate + ee.jakarta.tck.data.standalone.persistence.Product + ee.jakarta.tck.data.web.validation.Rectangle + true + + + + + + + + + + + + + + + + + + + + diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/README.adoc b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/README.adoc new file mode 100644 index 00000000..49cbd938 --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/README.adoc @@ -0,0 +1,4 @@ += Eclipse JNoSQL driver implementation for Jakarta Persistence +:toc: auto + +This project provides a driver for Eclipse JNoSQL that supports Jakarta Persistence entities over a Jakarta Persistence provider. \ No newline at end of file diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/pom.xml b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/pom.xml new file mode 100644 index 00000000..d9573878 --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/pom.xml @@ -0,0 +1,131 @@ + + + + 4.0.0 + Eclipse JNoSQL Jakarta Persistence Driver + + org.eclipse.jnosql.mapping + jnosql-jakarta-persistence-parent + 1.1.2-SNAPSHOT + + jnosql-jakarta-persistence + jar + + UTF-8 + 17 + 17 + + + + org.eclipse.jnosql.mapping + jnosql-mapping-document + + + org.eclipse.jnosql.databases + jnosql-database-commons + ${project.version} + + + jakarta.persistence + jakarta.persistence-api + 3.2.0 + + + + + org.jboss.weld.se + weld-se-core + 6.0.0.Beta1 + test + + + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.hamcrest + hamcrest-all + 1.3 + test + + + org.apache.derby + derby + 10.17.1.0 + test + + + org.apache.derby + derbytools + 10.17.1.0 + test + + + org.apache.derby + derbyclient + 10.17.1.0 + test + + + org.eclipse.persistence + eclipselink + 5.0.0-SNAPSHOT + test + + + + + + + + + maven-surefire-plugin + + + default-test + + + ${project.build.directory}${file.separator}derbydb + + + + + + + + + \ No newline at end of file diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/communication/PersistenceClassScanner.java b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/communication/PersistenceClassScanner.java new file mode 100644 index 00000000..5bc95d03 --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/communication/PersistenceClassScanner.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Ondro Mihalyi + */ +package org.eclipse.jnosql.jakartapersistence.communication; + +import jakarta.data.repository.DataRepository; +import java.util.Set; +import org.eclipse.jnosql.mapping.metadata.ClassScanner; + +public class PersistenceClassScanner implements ClassScanner { + + @Override + public Set> entities() { + return PersistenceClassScannerSingleton.INSTANCE.entities(); + } + + @Override + public Set> repositories() { + return PersistenceClassScannerSingleton.INSTANCE.repositories(); + } + + @Override + public Set> embeddables() { + return PersistenceClassScannerSingleton.INSTANCE.embeddables(); + } + + @Override + public > Set> repositories(Class filter) { + return PersistenceClassScannerSingleton.INSTANCE.repositories(filter); + } + + @Override + public Set> repositoriesStandard() { + return PersistenceClassScannerSingleton.INSTANCE.repositoriesStandard(); + } + + @Override + public Set> customRepositories() { + return PersistenceClassScannerSingleton.INSTANCE.customRepositories(); + } + +} diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/communication/PersistenceClassScannerSingleton.java b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/communication/PersistenceClassScannerSingleton.java new file mode 100644 index 00000000..f9932088 --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/communication/PersistenceClassScannerSingleton.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + * Ondro Mihalyi + */ +package org.eclipse.jnosql.jakartapersistence.communication; + + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; +import jakarta.data.repository.BasicRepository; +import jakarta.data.repository.CrudRepository; +import jakarta.data.repository.DataRepository; +import jakarta.data.repository.Repository; +import jakarta.nosql.Embeddable; +import jakarta.nosql.Entity; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import org.eclipse.jnosql.mapping.NoSQLRepository; +import org.eclipse.jnosql.mapping.metadata.ClassScanner; + +/** + * Scanner classes that will load entities with both Entity and Embeddable + * annotations and repositories: interfaces that extend DataRepository + * and has the Repository annotation. + */ +enum PersistenceClassScannerSingleton implements ClassScanner { + + INSTANCE; + + private final Set> entities; + private final Set> repositories; + private final Set> embeddables; + private final Set> customRepositories; + + + PersistenceClassScannerSingleton() { + entities = new HashSet<>(); + embeddables = new HashSet<>(); + repositories = new HashSet<>(); + customRepositories = new HashSet<>(); + + Logger logger = Logger.getLogger(PersistenceClassScannerSingleton.class.getName()); + logger.fine("Starting scan class to find entities, embeddable and repositories."); + try (ScanResult result = new ClassGraph().enableAllInfo().scan()) { + var notSupportedRepositories = loadNotSupportedRepositories(result); + logger.warning(() -> "The following repositories are not supported: " + notSupportedRepositories); + this.entities.addAll(loadEntities(result)); + this.embeddables.addAll(loadEmbeddable(result)); + this.repositories.addAll(loadRepositories(result)); + this.customRepositories.addAll(loadCustomRepositories(result)); + notSupportedRepositories.forEach(this.repositories::remove); + } + logger.fine(String.format("Finished the class scan with entities %d, embeddables %d and repositories: %d" + , entities.size(), embeddables.size(), repositories.size())); + + } + + + @Override + public Set> entities() { + return Collections.unmodifiableSet(entities); + } + + @Override + public Set> repositories() { + return Collections.unmodifiableSet(repositories); + } + + + @Override + public Set> embeddables() { + return Collections.unmodifiableSet(embeddables); + } + + @Override + public > Set> repositories(Class filter) { + Objects.requireNonNull(filter, "filter is required"); + return repositories.stream().filter(filter::isAssignableFrom) + .filter(c -> Arrays.asList(c.getInterfaces()).contains(filter)) + .collect(Collectors.toUnmodifiableSet()); + } + + + @Override + public Set> repositoriesStandard() { + return repositories.stream() + .filter(c -> { + List> interfaces = Arrays.asList(c.getInterfaces()); + return interfaces.contains(CrudRepository.class) + || interfaces.contains(BasicRepository.class) + || interfaces.contains(NoSQLRepository.class) + || interfaces.contains(DataRepository.class); + }).collect(Collectors.toUnmodifiableSet()); + } + + @Override + public Set> customRepositories() { + return customRepositories; + } + + + @SuppressWarnings("rawtypes") + private static List> loadRepositories(ScanResult scan) { + return scan.getClassesWithAnnotation(Repository.class) + .getInterfaces() + .filter(c -> c.implementsInterface(DataRepository.class)) + .loadClasses(DataRepository.class) + .stream().filter(PersistenceRepositoryFilter.INSTANCE) + .toList(); + } + + private static List> loadCustomRepositories(ScanResult scan) { + return scan.getClassesWithAnnotation(Repository.class) + .getInterfaces() + .filter(c -> !c.implementsInterface(DataRepository.class)) + .loadClasses().stream().toList(); + } + + @SuppressWarnings("rawtypes") + private static List> loadNotSupportedRepositories(ScanResult scan) { + return scan.getClassesWithAnnotation(Repository.class) + .getInterfaces() + .filter(c -> c.implementsInterface(DataRepository.class)) + .loadClasses(DataRepository.class) + .stream().filter(PersistenceRepositoryFilter.INSTANCE.negate()) + .toList(); + } + + private static List> loadEmbeddable(ScanResult scan) { + return scan.getClassesWithAnnotation(Embeddable.class).loadClasses(); + } + + private static List> loadEntities(ScanResult scan) { + return scan.getClassesWithAnnotation(Entity.class).loadClasses(); + } +} diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/communication/PersistenceDatabaseManager.java b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/communication/PersistenceDatabaseManager.java new file mode 100644 index 00000000..5c11e5c1 --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/communication/PersistenceDatabaseManager.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Ondro Mihalyi + */ +package org.eclipse.jnosql.jakartapersistence.communication; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.persistence.EntityManager; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Root; +import jakarta.persistence.metamodel.EntityType; +import java.util.HashMap; +import java.util.Map; + +@ApplicationScoped +public class PersistenceDatabaseManager { + + private final EntityManager em; + + private final Map> entityTypesByName = new HashMap<>(); + + record QueryContext(CriteriaQuery query, Root root, CriteriaBuilder builder) { + } + + @Inject + public PersistenceDatabaseManager(EntityManager em) { + this.em = em; + cacheEntityTypes(); + } + + PersistenceDatabaseManager() { + em = null; + } + + public EntityManager getEntityManager() { + return em; + } + + public void close() { + } + + public EntityType findEntityType(String entityName) { + try { + return (EntityType) em.getMetamodel().entity(entityName); + } catch (IllegalArgumentException e) { + // EclipseLink expects full class name in MM.entity() method. We need to find out the type otherwise + EntityType entityType = entityTypesByName.get(entityName); + if (entityType != null) { + return (EntityType)entityType; + } else { + final IllegalArgumentException ex = new IllegalArgumentException("Entity with name " + entityName + " not found in the list of known entities"); + ex.addSuppressed(e); + throw ex; + } + } + } + + private void cacheEntityTypes() { + em.getMetamodel().getEntities().forEach(type -> { + entityTypesByName.put(type.getName(), type); + }); + } + +} diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/communication/PersistenceRepositoryFilter.java b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/communication/PersistenceRepositoryFilter.java new file mode 100644 index 00000000..2911e683 --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/communication/PersistenceRepositoryFilter.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2023, 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + * Ondro Mihalyi + */ +package org.eclipse.jnosql.jakartapersistence.communication; + +import java.lang.annotation.Annotation; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Optional; +import java.util.function.Predicate; + +/** + * A filter to validate Repository that either Eclipse JNoSQL or the Jakarta Persistence extension support. It will + * check the first parameter on the repository, and if the entity has not had an unsupported annotation, it will return + * false and true to supported Repository. + */ +enum PersistenceRepositoryFilter implements Predicate> { + + INSTANCE; + + @Override + public boolean test(Class type) { + Optional> entity = getEntityClass(type); + return entity.map(this::toSupportedAnnotation) + .isPresent(); + } + + private Annotation toSupportedAnnotation(Class c) { + final Annotation annotation = c.getAnnotation(jakarta.persistence.Entity.class); + return annotation != null ? annotation : c.getAnnotation(jakarta.nosql.Entity.class); + } + + private Optional> getEntityClass(Class repository) { + Type[] interfaces = repository.getGenericInterfaces(); + if (interfaces.length == 0) { + return Optional.empty(); + } + if (interfaces[0] instanceof ParameterizedType interfaceType) { + return Optional.ofNullable(getEntityFromInterface(interfaceType)); + } else { + return Optional.empty(); + } + } + + private Class getEntityFromInterface(ParameterizedType param) { + Type[] arguments = param.getActualTypeArguments(); + if (arguments.length == 0) { + return null; + } + Type argument = arguments[0]; + if (argument instanceof Class entity) { + return entity; + } + return null; + } + +} diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/mapping/BaseQueryParser.java b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/mapping/BaseQueryParser.java new file mode 100644 index 00000000..efb58ea1 --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/mapping/BaseQueryParser.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Ondro Mihalyi + */ +package org.eclipse.jnosql.jakartapersistence.mapping; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.metamodel.EntityType; +import org.eclipse.jnosql.jakartapersistence.communication.PersistenceDatabaseManager; + +class BaseQueryParser { + + protected final PersistenceDatabaseManager manager; + + protected BaseQueryParser(PersistenceDatabaseManager manager) { + this.manager = manager; + } + + protected EntityType findEntityType(String entityName) { + return manager.findEntityType(entityName); + } + + protected EntityManager entityManager() { + return manager.getEntityManager(); + } + +} diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/mapping/DeleteQueryParser.java b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/mapping/DeleteQueryParser.java new file mode 100644 index 00000000..87f67424 --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/mapping/DeleteQueryParser.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Ondro Mihalyi + */ +package org.eclipse.jnosql.jakartapersistence.mapping; + +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaDelete; +import jakarta.persistence.criteria.Root; +import jakarta.persistence.metamodel.EntityType; +import jakarta.persistence.metamodel.SingularAttribute; +import org.eclipse.jnosql.jakartapersistence.communication.PersistenceDatabaseManager; + +class DeleteQueryParser extends BaseQueryParser { + + + public DeleteQueryParser(PersistenceDatabaseManager manager) { + super(manager); + } + + public void delete(Class type, K key) { + CriteriaBuilder criteriaBuilder = entityManager().getCriteriaBuilder(); + CriteriaDelete deleteCriteria = criteriaBuilder.createCriteriaDelete(type); + Root root = deleteCriteria.from(type); + String entityIdName = getEntityIdName(type); + deleteCriteria.where(criteriaBuilder.equal(root.get(entityIdName), key)); + entityManager().createQuery(deleteCriteria).executeUpdate(); + } + + private String getEntityIdName(Class type) { + EntityType entityType = entityManager().getMetamodel().entity(type); + SingularAttribute idAttribute = entityType.getId(entityType.getIdType().getJavaType()); + String entityIdName = idAttribute.getName(); + return entityIdName; + } + + +} diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/mapping/PersistenceDocumentTemplate.java b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/mapping/PersistenceDocumentTemplate.java new file mode 100644 index 00000000..acc6a253 --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/mapping/PersistenceDocumentTemplate.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Ondro Mihalyi + */ +package org.eclipse.jnosql.jakartapersistence.mapping; + + +import org.eclipse.jnosql.jakartapersistence.communication.PersistenceDatabaseManager; +import jakarta.annotation.Priority; +import jakarta.data.page.CursoredPage; +import jakarta.data.page.PageRequest; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Alternative; +import jakarta.enterprise.inject.Default; +import jakarta.inject.Inject; +import jakarta.interceptor.Interceptor; +import jakarta.nosql.QueryMapper; +import jakarta.persistence.EntityManager; +import java.time.Duration; +import java.util.Optional; +import java.util.stream.Stream; +import org.eclipse.jnosql.communication.semistructured.DeleteQuery; +import org.eclipse.jnosql.communication.semistructured.SelectQuery; +import org.eclipse.jnosql.mapping.Database; +import org.eclipse.jnosql.mapping.DatabaseType; +import org.eclipse.jnosql.mapping.PreparedStatement; +import org.eclipse.jnosql.mapping.document.DocumentTemplate; + +@Alternative +@Priority(Interceptor.Priority.APPLICATION) +@Default +@ApplicationScoped +@Database(DatabaseType.DOCUMENT) +public class PersistenceDocumentTemplate implements DocumentTemplate { + + private final PersistenceDatabaseManager manager; + private final SelectQueryParser selectParser; + private final DeleteQueryParser deleteParser; + + @Inject + PersistenceDocumentTemplate(PersistenceDatabaseManager manager) { + this.manager = manager; + this.selectParser = new SelectQueryParser(manager); + this.deleteParser = new DeleteQueryParser(manager); + } + + PersistenceDocumentTemplate() { + manager = null; + selectParser = null; + deleteParser = null; + } + + private EntityManager entityManager() { + return manager.getEntityManager(); + } + + @Override + public long count(String entity) { + return selectParser.count(entity); + } + + @Override + public long count(Class type) { + return selectParser.count(type); + } + + @Override + public Stream findAll(Class type) { + return selectParser.findAll(type); + } + + @Override + public Stream query(String query) { + return selectParser.query(query); + } + + @Override + public Stream query(String query, String entity) { + return selectParser.query(query, entity); + } + + @Override + public Optional singleResult(String query) { + return selectParser.singleResult(query); + } + + @Override + public Optional singleResult(String query, String entity) { + return selectParser.singleResult(query, entity); + } + + @Override + public Optional find(Class type, K k) { + return selectParser.find(type, k); + } + + @Override + public T insert(T t) { + entityManager().persist(t); + return t; + } + + @Override + public T update(T t) { + return entityManager().merge(t); + } + + @Override + public PreparedStatement prepare(String query, String entity) { + return prepare(query); + } + + @Override + public PreparedStatement prepare(String queryString) { + return new PersistencePreparedStatement(queryString, selectParser); + } + + @Override + public void delete(DeleteQuery query) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Stream select(SelectQuery selectQuery) { + return selectParser.select(selectQuery); + } + + @Override + public Optional singleResult(SelectQuery selectQuery) { + return selectParser.singleResult(selectQuery); + } + + @Override + public long count(SelectQuery selectQuery) { + return selectParser.count(selectQuery); + } + + @Override + public boolean exists(SelectQuery query) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public void deleteAll(Class type) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CursoredPage selectCursor(SelectQuery query, PageRequest pageRequest) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public T insert(T t, Duration drtn) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Iterable insert(Iterable itrbl) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Iterable insert(Iterable itrbl, Duration drtn) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Iterable update(Iterable itrbl) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public void delete(Class type, K key) { + deleteParser.delete(type, key); + } + + @Override + public QueryMapper.MapperFrom select(Class type) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public QueryMapper.MapperDeleteFrom delete(Class type) { + throw new UnsupportedOperationException("Not supported yet."); + } + +} diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/mapping/PersistencePreparedStatement.java b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/mapping/PersistencePreparedStatement.java new file mode 100644 index 00000000..022c6af6 --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/mapping/PersistencePreparedStatement.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Ondro Mihalyi + */ +package org.eclipse.jnosql.jakartapersistence.mapping; + +import jakarta.persistence.Query; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import org.eclipse.jnosql.mapping.PreparedStatement; + +/** + * + * @author Ondro Mihalyi + */ +class PersistencePreparedStatement implements PreparedStatement { + + private final String queryString; + private final SelectQueryParser selectParser; + private Map parameters = new HashMap<>(); + + public PersistencePreparedStatement(String queryString, final SelectQueryParser selectParser) { + this.selectParser = selectParser; + this.queryString = queryString; + } + + private void applyParameters(Query query) { + parameters.forEach((name, value) -> query.setParameter(name, value)); + } + + @Override + public PreparedStatement bind(String name, Object value) { + parameters.put(name, value); + return this; + } + + @Override + public Stream result() { + Query query = createQuery(); + try { + return query.getResultStream(); + } catch (IllegalStateException e) { + return IntStream.rangeClosed(1, query.executeUpdate()) + .mapToObj(i -> (T)Integer.valueOf(i)); + } + } + + @Override + public Optional singleResult() { + Query query = createQuery(); + return Optional.ofNullable((T) query.getSingleResultOrNull()); + } + + private Query createQuery() { + Query query = selectParser.buildQuery(queryString); + applyParameters(query); + return query; + } + + @Override + public long count() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean isCount() { + return false; + } + +} diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/mapping/SelectQueryParser.java b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/mapping/SelectQueryParser.java new file mode 100644 index 00000000..a67a873b --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/mapping/SelectQueryParser.java @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Ondro Mihalyi + */ +package org.eclipse.jnosql.jakartapersistence.mapping; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Path; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; +import jakarta.persistence.metamodel.EntityType; +import java.util.Collection; +import java.util.Iterator; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; +import org.eclipse.jnosql.communication.Value; +import org.eclipse.jnosql.communication.semistructured.CriteriaCondition; +import org.eclipse.jnosql.communication.semistructured.Element; +import org.eclipse.jnosql.communication.semistructured.SelectQuery; +import org.eclipse.jnosql.jakartapersistence.communication.PersistenceDatabaseManager; + +class SelectQueryParser extends BaseQueryParser { + + record QueryContext(CriteriaQuery query, Root root, CriteriaBuilder builder) { + + } + + public SelectQueryParser(PersistenceDatabaseManager manager) { + super(manager); + } + + public long count(String entity) { + EntityType entityType = findEntityType(entity); + return count(entityType.getJavaType()); + } + + public long count(Class type) { + TypedQuery query = buildQuery(type, Long.class, ctx -> ctx.query.select(ctx.builder.count(ctx.root))); + return query.getSingleResult(); + } + + public Stream findAll(Class type) { + TypedQuery query = buildQuery(type, type, ctx -> ctx.query.select((Root) ctx.root)); + return query.getResultStream(); + } + + public Stream query(String query) { + return buildQuery(query).getResultStream(); + } + + public Stream query(String query, String entity) { + return query(query); + } + + public Optional singleResult(String query) { + return Optional.ofNullable((T) buildQuery(query).getSingleResultOrNull()); + } + + public Optional singleResult(String query, String entity) { + return singleResult(query); + } + + public Optional find(Class type, K k) { + return Optional.ofNullable(entityManager().find(type, k)); + } + + public Stream select(SelectQuery selectQuery) { + final String entityName = selectQuery.name(); + final EntityType entityType = findEntityType(entityName); + if (selectQuery.condition().isEmpty()) { + return findAll(entityType.getJavaType()); + } else { + final CriteriaCondition criteria = selectQuery.condition().get(); + TypedQuery query = buildQuery(entityType.getJavaType(), entityType.getJavaType(), ctx -> { + CriteriaQuery q = ctx.query.select(ctx.root); + q = q.where(parseCriteria(criteria, ctx)); + return q; + }); + return query.getResultStream(); + } + } + + public Optional singleResult(SelectQuery selectQuery) { + final String entityName = selectQuery.name(); + final EntityType entityType = findEntityType(entityName); + final Class type = entityType.getJavaType(); + if (selectQuery.condition().isEmpty()) { + TypedQuery query = buildQuery(type, type, ctx -> ctx.query.select((Root) ctx.root)); + return Optional.ofNullable(query.getSingleResultOrNull()); + } else { + final CriteriaCondition criteria = selectQuery.condition().get(); + TypedQuery query = buildQuery(type, type, ctx -> { + CriteriaQuery q = ctx.query.select(ctx.root); + q = q.where(parseCriteria(criteria, ctx)); + return q; + }); + return Optional.ofNullable(query.getSingleResultOrNull()); + } + } + + public long count(SelectQuery selectQuery) { + final String entityName = selectQuery.name(); + if (selectQuery.condition().isEmpty()) { + return count(entityName); + } else { + final EntityType entityType = findEntityType(entityName); + final CriteriaCondition criteria = selectQuery.condition().get(); + TypedQuery query = buildQuery(entityType.getJavaType(), Long.class, ctx -> { + CriteriaQuery q = ctx.query.select(ctx.builder.count(ctx.root)); + q = q.where(parseCriteria(criteria, ctx)); + return q; + }); + return query.getSingleResult(); + } + } + + record ComparableContext(Path field, Comparable fieldValue) { + + public static ComparableContext from(Root root, CriteriaCondition criteria) { + Element element = (Element) criteria.element(); + Path field = root.get(getName(element)); + Comparable fieldValue = element.value().get(Comparable.class); + return new ComparableContext(field, fieldValue); + } + } + + record BiComparableContext(Path field, Comparable fieldValue1, Comparable fieldValue2) { + + public static BiComparableContext from(Root root, CriteriaCondition criteria) { + Element element = (Element) criteria.element(); + final Path field = root.get(getName(element)); + Iterator iterator = elementCollection(criteria).iterator(); + final Comparable fieldValue1 = ((Value) iterator.next()).get(Comparable.class); + final Comparable fieldValue2 = ((Value) iterator.next()).get(Comparable.class); + return new BiComparableContext(field, fieldValue1, fieldValue2); + } + + } + + record MultiValueContext(Path field, Collection fieldValues) { + + public static MultiValueContext from(Root root, CriteriaCondition criteria) { + Element element = (Element) criteria.element(); + Path field = root.get(getName(element)); + return new MultiValueContext(field, elementCollection(criteria)); + } + } + + public Query buildQuery(String query) { + EntityManager em = entityManager(); + return em.createQuery(query); + } + + public TypedQuery buildQuery(Class fromType, Class resultType, + Function, CriteriaQuery> queryModifier) { + EntityManager em = entityManager(); + CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder(); + CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(resultType); + Root from = criteriaQuery.from(fromType); + criteriaQuery = queryModifier.apply(new QueryContext(criteriaQuery, from, criteriaBuilder)); + return em.createQuery(criteriaQuery); + } + + private static String getName(Element element) { + String name = element.name(); + // NoSQL DBs translate id field into "_id" but we don't want it + return name.equals("_id") ? "id" : name; + } + + private Predicate parseCriteria(Object value, QueryContext ctx) { + if (value instanceof CriteriaCondition criteria) { + return switch (criteria.condition()) { + case NOT -> + ctx.builder().not(parseCriteria(criteria.element(), ctx)); + case EQUALS -> { + Element element = (Element) criteria.element(); + if (element.value().isNull()) { + yield ctx.builder().isNull(ctx.root().get(getName(element))); + } else { + yield ctx.builder().equal(ctx.root().get(getName(element)), element.value().get()); + } + } + case AND -> { + Iterator iterator = elementCollection(criteria).iterator(); + yield ctx.builder().and(parseCriteria(iterator.next(), ctx), parseCriteria(iterator.next(), ctx)); + } + case LESSER_THAN -> { + ComparableContext comparableContext = ComparableContext.from(ctx.root(), criteria); + yield ctx.builder().lessThan(comparableContext.field(), comparableContext.fieldValue()); + } + case LESSER_EQUALS_THAN -> { + ComparableContext comparableContext = ComparableContext.from(ctx.root(), criteria); + yield ctx.builder().lessThanOrEqualTo(comparableContext.field(), comparableContext.fieldValue()); + } + case GREATER_THAN -> { + ComparableContext comparableContext = ComparableContext.from(ctx.root(), criteria); + yield ctx.builder().greaterThan(comparableContext.field(), comparableContext.fieldValue()); + } + case GREATER_EQUALS_THAN -> { + ComparableContext comparableContext = ComparableContext.from(ctx.root(), criteria); + yield ctx.builder().greaterThanOrEqualTo(comparableContext.field(), comparableContext.fieldValue()); + } + case BETWEEN -> { + BiComparableContext comparableContext = BiComparableContext.from(ctx.root(), criteria); + yield ctx.builder().between(comparableContext.field(), comparableContext.fieldValue1(), comparableContext.fieldValue2()); + } + case IN -> { + MultiValueContext valueContext = MultiValueContext.from(ctx.root(), criteria); + CriteriaBuilder.In inExpr = ctx.builder().in(valueContext.field()); + valueContext.fieldValues().forEach(v -> inExpr.value(v)); + yield inExpr; + } + + default -> + throw new UnsupportedOperationException("Not supported yet."); + }; + } else if (value instanceof Element element) { + return parseCriteria(element.value().get(), ctx); + } + throw new UnsupportedOperationException("Not supported yet."); + } + + private static Collection elementCollection(CriteriaCondition criteria) { + Element element = (Element) criteria.element(); + return (Collection) element.value().get(); + } + +} diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/mapping/repository/JakartaPersistenceRepositoryProxy.java b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/mapping/repository/JakartaPersistenceRepositoryProxy.java new file mode 100644 index 00000000..f8954eb3 --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/mapping/repository/JakartaPersistenceRepositoryProxy.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Ondro Mihalyi + */ +package org.eclipse.jnosql.jakartapersistence.mapping.repository; + +import java.lang.reflect.Method; +import org.eclipse.jnosql.jakartapersistence.mapping.PersistenceDocumentTemplate; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +import org.eclipse.jnosql.mapping.semistructured.query.SemiStructuredRepositoryProxy; + +public class JakartaPersistenceRepositoryProxy extends SemiStructuredRepositoryProxy { + + public JakartaPersistenceRepositoryProxy(PersistenceDocumentTemplate template, EntitiesMetadata entities, Class repositoryType, Converters converters) { + super(template, entities, repositoryType, converters); + } + + @Override + protected Object executeCursorPagination(Object instance, Method method, Object[] params) { + // We need to override this because SemiStructuredRepositoryProxy + // expects the semistructured.PreparedStatement template + throw new UnsupportedOperationException("Not supported yet."); + } + +} diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/mapping/repository/RepositoryPersistenceBean.java b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/mapping/repository/RepositoryPersistenceBean.java new file mode 100644 index 00000000..ed8ced19 --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/mapping/repository/RepositoryPersistenceBean.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.jakartapersistence.mapping.repository; + +import org.eclipse.jnosql.jakartapersistence.mapping.PersistenceDocumentTemplate; +import jakarta.data.repository.DataRepository; +import jakarta.enterprise.context.spi.CreationalContext; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.DatabaseType; +import org.eclipse.jnosql.mapping.core.spi.AbstractBean; +import org.eclipse.jnosql.mapping.core.util.AnnotationLiteralUtil; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Proxy; +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; + + +/** + * This class serves as a JNoSQL discovery bean for CDI extension, responsible for registering Repository instances for Jakarta Persistence entities. + * It extends {@link AbstractBean} and is parameterized with type {@code T} representing the repository type. + *

+ * Upon instantiation, it initializes with the provided repository type, provider name, and qualifiers. + * The provider name specifies the database provider for the repository. + *

+ * + * @param the type of the repository + * @see AbstractBean + */ +public class RepositoryPersistenceBean> extends AbstractBean { + + private final Class type; + + private final Set types; + + private final String provider; + + private final Set qualifiers; + + /** + * Constructor + * + * @param type the tye + * @param provider the provider name, that must be a + */ + @SuppressWarnings("unchecked") + public RepositoryPersistenceBean(Class type, String provider) { + this.type = (Class) type; + this.types = Collections.singleton(type); + this.provider = provider; + this.qualifiers = new HashSet<>(); + qualifiers.add(AnnotationLiteralUtil.DEFAULT_ANNOTATION); + qualifiers.add(AnnotationLiteralUtil.ANY_ANNOTATION); + } + + @Override + public Class getBeanClass() { + return type; + } + + @Override + @SuppressWarnings("unchecked") + public T create(CreationalContext context) { + EntitiesMetadata entities = getInstance(EntitiesMetadata.class); + var template = getInstance(PersistenceDocumentTemplate.class); + + Converters converters = getInstance(Converters.class); + + var handler = new JakartaPersistenceRepositoryProxy<>(template, + entities, type, converters); + return (T) Proxy.newProxyInstance(type.getClassLoader(), + new Class[]{type}, + handler); + } + + + @Override + public Set getTypes() { + return types; + } + + @Override + public Set getQualifiers() { + return qualifiers; + } + + @Override + public String getId() { + return type.getName() + '@' + DatabaseType.DOCUMENT + "-" + provider; + } + +} \ No newline at end of file diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/mapping/spi/JakartaPersistenceExtension.java b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/mapping/spi/JakartaPersistenceExtension.java new file mode 100644 index 00000000..c6fca41e --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/java/org/eclipse/jnosql/jakartapersistence/mapping/spi/JakartaPersistenceExtension.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Otavio Santana + */ +package org.eclipse.jnosql.jakartapersistence.mapping.spi; + +import org.eclipse.jnosql.jakartapersistence.communication.PersistenceClassScanner; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.inject.spi.AfterBeanDiscovery; +import jakarta.enterprise.inject.spi.Extension; +import org.eclipse.jnosql.mapping.document.query.RepositoryDocumentBean; +import org.eclipse.jnosql.mapping.metadata.ClassScanner; + +import java.util.Set; +import java.util.logging.Logger; + +/** + * This CDI extension, {@code JakartaPersistenceExtension}, observes the CDI container lifecycle events to perform tasks + * related to Jakarta Persistence repository beans. + *

+ */ +public class JakartaPersistenceExtension implements Extension { + + private static final Logger LOGGER = Logger.getLogger(JakartaPersistenceExtension.class.getName()); + + void onAfterBeanDiscovery(@Observes final AfterBeanDiscovery afterBeanDiscovery) { + + ClassScanner scanner = new PersistenceClassScanner(); + + Set> crudTypes = scanner.repositoriesStandard(); + + LOGGER.info(() -> "Processing Jakarta Persistence extension: crud " + + crudTypes.size() + " found"); + LOGGER.fine(() -> "Processing repositories as a Jakarta Persistence implementation: " + crudTypes); + + crudTypes.forEach(type -> { + afterBeanDiscovery.addBean(new RepositoryDocumentBean<>(type, "")); + }); + + } +} \ No newline at end of file diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/resources/META-INF/beans.xml b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/resources/META-INF/beans.xml new file mode 100644 index 00000000..029572ac --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/resources/META-INF/beans.xml @@ -0,0 +1,23 @@ + + + + \ No newline at end of file diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension new file mode 100644 index 00000000..f3c4099d --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension @@ -0,0 +1,15 @@ +# +# Copyright (c) 2022 Contributors to the Eclipse Foundation +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# and Apache License v2.0 which accompanies this distribution. +# The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html +# and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. +# +# You may elect to redistribute this code under either of these licenses. +# +# Contributors: +# +# Ondro Mihalyi +# +org.eclipse.jnosql.jakartapersistence.mapping.spi.JakartaPersistenceExtension \ No newline at end of file diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/test/java/ee/omnifish/jnosql/jakartapersistence/EntityManagerProducer.java b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/test/java/ee/omnifish/jnosql/jakartapersistence/EntityManagerProducer.java new file mode 100644 index 00000000..5c2ffed1 --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/test/java/ee/omnifish/jnosql/jakartapersistence/EntityManagerProducer.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Ondro Mihalyi + */ +package ee.omnifish.jnosql.jakartapersistence; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Disposes; +import jakarta.enterprise.inject.Produces; +import jakarta.persistence.EntityManager; +import jakarta.persistence.Persistence; + +@ApplicationScoped +public class EntityManagerProducer { + @Produces + @ApplicationScoped + public EntityManager createEntityManager() { + return Persistence.createEntityManagerFactory("testPersistenceUnit") + .createEntityManager(); + } + + public void closeEntityManager(@Disposes EntityManager entityManager) { + entityManager.close(); + } +} diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/test/java/ee/omnifish/jnosql/jakartapersistence/Person.java b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/test/java/ee/omnifish/jnosql/jakartapersistence/Person.java new file mode 100644 index 00000000..ce53f8ca --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/test/java/ee/omnifish/jnosql/jakartapersistence/Person.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Ondro Mihalyi + */ +package ee.omnifish.jnosql.jakartapersistence; + +import static jakarta.persistence.GenerationType.AUTO; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import java.util.List; + +@Entity(name = "Person") +public class Person { + + @Id + @GeneratedValue(strategy = AUTO) + private long id; + + @Column + private String name; + + @Column + private long age; + + @Column + private List phones; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getPhones() { + return phones; + } + + public void setPhones(List phones) { + this.phones = phones; + } + + public long getAge() { + return age; + } + + public void setAge(long age) { + this.age = age; + } + + +} diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/test/java/ee/omnifish/jnosql/jakartapersistence/PersonRepository.java b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/test/java/ee/omnifish/jnosql/jakartapersistence/PersonRepository.java new file mode 100644 index 00000000..e457b064 --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/test/java/ee/omnifish/jnosql/jakartapersistence/PersonRepository.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Ondro Mihalyi + */ +package ee.omnifish.jnosql.jakartapersistence; + +import jakarta.data.repository.CrudRepository; +import jakarta.data.repository.Repository; +import java.util.List; +import java.util.Set; + +@Repository +public interface PersonRepository extends CrudRepository { + long countAll(); + long countByNameNotNull(); + List findByNameAndAgeLessThanEqual(String name, long age); + List findByNameIn(Set names); +} + diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/test/java/ee/omnifish/jnosql/jakartapersistence/PersonRepositoryTest.java b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/test/java/ee/omnifish/jnosql/jakartapersistence/PersonRepositoryTest.java new file mode 100644 index 00000000..dcb07f32 --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/test/java/ee/omnifish/jnosql/jakartapersistence/PersonRepositoryTest.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Ondro Mihalyi + */ +package ee.omnifish.jnosql.jakartapersistence; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.not; + +import jakarta.enterprise.inject.se.SeContainer; +import jakarta.enterprise.inject.se.SeContainerInitializer; +import jakarta.persistence.EntityManager; +import java.util.List; +import java.util.Set; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class PersonRepositoryTest { + + private SeContainer cdiContainer; + private PersonRepository personRepo; + + @BeforeEach + void init() { + cdiContainer = SeContainerInitializer.newInstance() + .addBeanClasses(EntityManagerProducer.class) + .initialize(); + assertThat("repository can be resolved", cdiContainer.select(PersonRepository.class).isResolvable()); + personRepo = cdiContainer.select(PersonRepository.class).get(); + + // cleanup + getEntityManager().getTransaction().begin(); + getEntityManager().createQuery("delete from Person p").executeUpdate(); + getEntityManager().getTransaction().commit(); + + getEntityManager().getTransaction().begin(); + } + + private EntityManager getEntityManager() { + return cdiContainer.select(EntityManager.class).get(); + } + + @AfterEach + void cleanup() { + getEntityManager().getTransaction().commit(); + cdiContainer.close(); + } + + @Test + void findAll() { + final List persons = personRepo.findAll().toList(); + assertThat("queryResult", persons, is(empty())); + System.out.println("All persons: " + persons); + } + + @Test + void count() { + personRepo.insert(new Person()); + final long count = personRepo.countAll(); + assertThat(count, greaterThan(0L)); + } + + @Test + void countByNotNull() { + new PersonBuilder().name("Jakarta").insert(personRepo); + final long count = personRepo.countByNameNotNull(); + assertThat(count, greaterThan(0L)); + } + + @Test + void findByXAndYLessThanEqual() { + final String NAME = "Jakarta"; + new PersonBuilder().name(NAME).age(35).insert(personRepo); + + final List persons = personRepo.findByNameAndAgeLessThanEqual(NAME, 50); + assertThat(persons, is(not(empty()))); + } + + @Test + void findByXIn() { + final String NAME1 = "Jakarta"; + final String NAME2 = "JNoSQL"; + final String NAME3 = "Data"; + new PersonBuilder().name(NAME1).insert(personRepo); + new PersonBuilder().name(NAME2).insert(personRepo); + new PersonBuilder().name(NAME3).insert(personRepo); + new PersonBuilder().name("No name").insert(personRepo); + + final List persons = personRepo.findByNameIn(Set.of(NAME1, NAME2, NAME3)); + assertThat(persons, hasSize(3)); + } + + @Test + void hermesParser() { + getEntityManager().createQuery("UPDATE Person SET length = age + 1"); + } + + private class PersonBuilder { + + Person p = new Person(); + + public PersonBuilder name(String name) { + p.setName(name); + return this; + } + + public PersonBuilder age(long age) { + p.setAge(age); + return this; + } + + public Person build() { + return p; + } + + public void insert(PersonRepository personRepo) { + personRepo.insert(p); + } + } + +} diff --git a/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/test/resources/META-INF/persistence.xml b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/test/resources/META-INF/persistence.xml new file mode 100644 index 00000000..442bfb96 --- /dev/null +++ b/jnosql-jakarta-persistence/jnosql-jakarta-persistence-driver/src/test/resources/META-INF/persistence.xml @@ -0,0 +1,40 @@ + + + + + org.eclipse.persistence.jpa.PersistenceProvider + ee.omnifish.jnosql.jakartapersistence.Person + true + + + + + + + + + + + + + + + + + + diff --git a/jnosql-jakarta-persistence/pom.xml b/jnosql-jakarta-persistence/pom.xml new file mode 100644 index 00000000..6ef16097 --- /dev/null +++ b/jnosql-jakarta-persistence/pom.xml @@ -0,0 +1,32 @@ + + + + 4.0.0 + Eclipse JNoSQL Jakarta Persistence Parent + + org.eclipse.jnosql.mapping + jnosql-mapping-extensions + 1.1.2-SNAPSHOT + + jnosql-jakarta-persistence-parent + pom + + jnosql-jakarta-persistence-driver + jnosql-jakarta-persistence-data-tck-runner + + \ No newline at end of file