diff --git a/jpalite-core/pom.xml b/jpalite-core/pom.xml index 461d58f..c4bfb69 100644 --- a/jpalite-core/pom.xml +++ b/jpalite-core/pom.xml @@ -46,6 +46,14 @@ com.github.jsqlparser jsqlparser + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + io.quarkus quarkus-opentelemetry diff --git a/jpalite-core/src/main/java/io/jpalite/CachingException.java b/jpalite-core/src/main/java/io/jpalite/CachingException.java new file mode 100644 index 0000000..62ac045 --- /dev/null +++ b/jpalite-core/src/main/java/io/jpalite/CachingException.java @@ -0,0 +1,26 @@ +package io.jpalite; + +import jakarta.persistence.PersistenceException; + +public class CachingException extends PersistenceException +{ + public CachingException() + { + super(); + } + + public CachingException(String message) + { + super(message); + } + + public CachingException(String message, Throwable cause) + { + super(message, cause); + } + + public CachingException(Throwable cause) + { + super(cause); + } +} diff --git a/jpalite-core/src/main/java/io/jpalite/ConverterClass.java b/jpalite-core/src/main/java/io/jpalite/ConverterClass.java index 0abc352..b3d62cd 100644 --- a/jpalite-core/src/main/java/io/jpalite/ConverterClass.java +++ b/jpalite-core/src/main/java/io/jpalite/ConverterClass.java @@ -26,13 +26,6 @@ public interface ConverterClass */ String getName(); - /** - * The class of the converter - * - * @return The class - */ - Class getConvertClass(); - /** * A check to see if the converter should be auto applied * @@ -47,7 +40,7 @@ public interface ConverterClass */ //We need to use the raw type here as the converter is not generic @SuppressWarnings("java:S1452") - TradeSwitchConvert getConverter(); + FieldConvertType getConverter(); /** * The attribute type @@ -55,11 +48,4 @@ public interface ConverterClass * @return The attribute type */ Class getAttributeType(); - - /** - * The database type - * - * @return The database type - */ - Class getDbType(); } diff --git a/jpalite-core/src/main/java/io/jpalite/EntityCache.java b/jpalite-core/src/main/java/io/jpalite/EntityCache.java index b99fec7..4945ac4 100644 --- a/jpalite-core/src/main/java/io/jpalite/EntityCache.java +++ b/jpalite-core/src/main/java/io/jpalite/EntityCache.java @@ -19,60 +19,38 @@ import jakarta.annotation.Nonnull; import jakarta.persistence.Cache; -import jakarta.transaction.*; +import jakarta.transaction.SystemException; -import java.util.List; +import java.time.Instant; public interface EntityCache extends Cache { /** * Create a new transaction and associate it with the current thread. * - * @throws jakarta.transaction.NotSupportedException Thrown if the thread is already - * associated with a transaction and the Transaction Manager - * implementation does not support nested transactions. - * @throws jakarta.transaction.SystemException Thrown if the transaction manager - * encounters an unexpected error condition. + * @throws jakarta.transaction.SystemException Thrown if the transaction manager + * encounters an unexpected error condition. */ - public void begin() throws NotSupportedException, SystemException; + public void begin() throws SystemException; /** * Complete the transaction associated with the current thread. When this * method completes, the thread is no longer associated with a transaction. * - * @throws jakarta.transaction.RollbackException Thrown to indicate that - * the transaction has been rolled back rather than committed. - * @throws jakarta.transaction.HeuristicMixedException Thrown to indicate that a heuristic - * decision was made and that some relevant updates have been committed - * while others have been rolled back. - * @throws jakarta.transaction.HeuristicRollbackException Thrown to indicate that a - * heuristic decision was made and that all relevant updates have been - * rolled back. - * @throws SecurityException Thrown to indicate that the thread is - * not allowed to commit the transaction. - * @throws IllegalStateException Thrown if the current thread is - * not associated with a transaction. - * @throws SystemException Thrown if the transaction manager - * encounters an unexpected error condition. + * @throws SystemException Thrown if the transaction manager + * encounters an unexpected error condition. */ - void commit() throws RollbackException, - HeuristicMixedException, HeuristicRollbackException, SecurityException, - IllegalStateException, SystemException; + void commit() throws SystemException; /** * Roll back the transaction associated with the current thread. When this * method completes, the thread is no longer associated with a * transaction. * - * @throws SecurityException Thrown to indicate that the thread is - * not allowed to roll back the transaction. - * @throws IllegalStateException Thrown if the current thread is - * not associated with a transaction. - * @throws SystemException Thrown if the transaction manager - * encounters an unexpected error condition. + * @throws SystemException Thrown if the transaction manager + * encounters an unexpected error condition. */ - void rollback() throws IllegalStateException, SecurityException, - SystemException; + void rollback() throws SystemException; /** * Search the cache for an entity using the primary key. @@ -83,17 +61,6 @@ void rollback() throws IllegalStateException, SecurityException, */ T find(Class entityType, Object primaryKey); - /** - * Search for the entity in the cache using the where clause - * - * @param entityType - * @param query - * @param - * @return - */ - @Nonnull - List search(Class entityType, String query); - /** * Add an entity to the cache. * @@ -106,21 +73,28 @@ void rollback() throws IllegalStateException, SecurityException, * * @param entity The entity to update or add */ - void update(JPAEntity entity); + void replace(JPAEntity entity); + /** - * Detach an entity from the cache and mark the entity as DETACHED + * Return the time for when an entity-type was last updated * - * @param entity the entity to detach + * @param entityType The entity type + * @return The instance when the entity was last updated */ - void remove(JPAEntity entity); + @Nonnull + Instant getLastModified(Class entityType); /** * Return the time for when an entity-type was last updated * * @param entityType The entity type - * @param * @return The time since epoch the entity was updated + * @deprecated Replaced by {{@link #getLastModified(Class)}} */ - long lastModified(Class entityType); + @Deprecated(forRemoval = true, since = "3.0.0") + default long lastModified(Class entityType) + { + return getLastModified(entityType).toEpochMilli(); + } } diff --git a/jpalite-core/src/main/java/io/jpalite/EntityField.java b/jpalite-core/src/main/java/io/jpalite/EntityField.java index e94bcc8..9639784 100644 --- a/jpalite-core/src/main/java/io/jpalite/EntityField.java +++ b/jpalite-core/src/main/java/io/jpalite/EntityField.java @@ -179,7 +179,7 @@ public interface EntityField * * @return The attribute converter class. If no converter is set, null is returned */ - @SuppressWarnings("java:S3740") + @SuppressWarnings({"java:S3740", "rawtypes"}) // Suppress warning for generic types - TradeSwitchConvert getConverterClass(); + FieldConvertType getConverter(); } diff --git a/jpalite-core/src/main/java/io/jpalite/EntityMetaData.java b/jpalite-core/src/main/java/io/jpalite/EntityMetaData.java index f3adba6..b098901 100644 --- a/jpalite-core/src/main/java/io/jpalite/EntityMetaData.java +++ b/jpalite-core/src/main/java/io/jpalite/EntityMetaData.java @@ -128,20 +128,20 @@ public interface EntityMetaData @Nullable @SuppressWarnings("java:S1452") //generic wildcard is required - EntityMetaData getIPrimaryKeyMetaData(); + EntityMetaData getPrimaryKeyMetaData(); /** * Return a list of all the defined id fields * - * @return Set of id fields + * @return List of id fields */ @Nonnull List getIdFields(); /** - * True if the entity have more than one ID field + * True if the entity has more than one ID field * - * @return true if there are more than one if fields in the entity + * @return true if there are more than one if field in the entity */ boolean hasMultipleIdFields(); @@ -160,14 +160,24 @@ public interface EntityMetaData */ boolean isCacheable(); + /** + * Retrieves the idle time of the entity. The units are dependent the value in {@link #getCacheTimeUnit} + * + * @return The idle time in. + */ long getIdleTime(); + /** + * Retrieves the time unit used for caching. + * + * @return The {@link TimeUnit} used for caching. + */ TimeUnit getCacheTimeUnit(); /** - * True if the entity have a version field + * Checks if the entity has a version field. * - * @return + * @return True if the entity has a version field, false otherwise. */ boolean hasVersionField(); @@ -179,14 +189,12 @@ public interface EntityMetaData EntityField getVersionField(); /** - * Return a comma delimited string with columns - */ - String getColumns(); - - /** - * The protobuf protocal file for the entity + * Deprecated since version 3.0.0 and marked for removal. + * Returns the columns associated with the entity. * - * @return The proto file as a string + * @return The columns as a comma-delimited string. + * @deprecated This method is deprecated and will be removed in a future version. */ - String getProtoFile(); + @Deprecated(since = "3.0.0", forRemoval = true) + String getColumns(); }//EntityMetaData diff --git a/jpalite-core/src/main/java/io/jpalite/EntityMetaDataManager.java b/jpalite-core/src/main/java/io/jpalite/EntityMetaDataManager.java index 3edfcba..dd7b475 100644 --- a/jpalite-core/src/main/java/io/jpalite/EntityMetaDataManager.java +++ b/jpalite-core/src/main/java/io/jpalite/EntityMetaDataManager.java @@ -24,14 +24,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.net.URL; import java.util.Enumeration; import java.util.Map; import java.util.Properties; +import java.util.ServiceLoader; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantLock; @@ -49,33 +48,27 @@ public class EntityMetaDataManager loadEntities(); } - private static int loadEntities() + @SuppressWarnings({"rawtypes", "unchecked"}) + private static void loadEntities() { lock.lock(); try { if (!registryLoaded) { try { + REGISTRY_CONVERTERS.clear(); + ClassLoader loader = Thread.currentThread().getContextClassLoader(); long start = System.currentTimeMillis(); - Enumeration urls = loader.getResources("META-INF/io.jpalite.converters"); - while (urls.hasMoreElements()) { - URL location = urls.nextElement(); - try (InputStream inputStream = location.openStream(); - BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { - - String line = reader.readLine(); - while (line != null) { - ConverterClass convertClass = new ConverterClassImpl(loader.loadClass(line)); - REGISTRY_CONVERTERS.put(convertClass.getAttributeType(), convertClass); - line = reader.readLine(); - }//while - }//try - }//while + ServiceLoader converterLoader = ServiceLoader.load(FieldConvertType.class); + for (FieldConvertType converter : converterLoader) { + registerConverter(converter); + } + LOG.info("Loaded {} converters in {}ms", REGISTRY_CONVERTERS.size(), System.currentTimeMillis() - start); start = System.currentTimeMillis(); - urls = loader.getResources("META-INF/persistenceUnits.properties"); + Enumeration urls = loader.getResources("META-INF/persistenceUnits.properties"); while (urls.hasMoreElements()) { URL location = urls.nextElement(); try (InputStream inputStream = location.openStream()) { @@ -98,11 +91,8 @@ private static int loadEntities() }//while LOG.info("Loaded {} entities in {}ms", REGISTRY_ENTITY_CLASSES.size(), System.currentTimeMillis() - start); }//try - catch (ClassNotFoundException ex) { - throw new PersistenceException("Error loading converter class", ex); - }//catch catch (IOException ex) { - throw new PersistenceException("Error reading persistenceUnits.properties or org.tradeswitch.converters", ex); + throw new PersistenceException("Error reading persistenceUnits.properties", ex); }//catch registryLoaded = true; @@ -111,8 +101,6 @@ private static int loadEntities() finally { lock.unlock(); } - - return REGISTRY_ENTITY_NAMES.size(); }//loadEntities public static int getEntityCount() @@ -131,6 +119,14 @@ public static EntityMetaData getMetaData(Class entityName) return metaData; }//getMetaData + public static void registerConverter(@Nonnull FieldConvertType converter) + { + ConverterClass convertClass = new ConverterClassImpl(converter); + if (convertClass.isAutoApply()) { + REGISTRY_CONVERTERS.put(convertClass.getAttributeType(), convertClass); + } + } + public static void register(@Nonnull EntityMetaData metaData) { if (REGISTRY_ENTITY_NAMES.containsKey(metaData.getName())) { diff --git a/jpalite-core/src/main/java/io/jpalite/TradeSwitchConvert.java b/jpalite-core/src/main/java/io/jpalite/FieldConvertType.java similarity index 56% rename from jpalite-core/src/main/java/io/jpalite/TradeSwitchConvert.java rename to jpalite-core/src/main/java/io/jpalite/FieldConvertType.java index 585889a..7727ea3 100644 --- a/jpalite-core/src/main/java/io/jpalite/TradeSwitchConvert.java +++ b/jpalite-core/src/main/java/io/jpalite/FieldConvertType.java @@ -17,22 +17,32 @@ package io.jpalite; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonNode; import jakarta.persistence.AttributeConverter; -import org.infinispan.protostream.GeneratedSchema; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; import java.sql.ResultSet; import java.sql.SQLException; -public interface TradeSwitchConvert extends AttributeConverter +public interface FieldConvertType extends AttributeConverter { - String getFieldType(); + Class getAttributeType(); - String prototypeLib(); + void toJson(JsonGenerator jsonGenerator, X value) throws IOException; - GeneratedSchema getSchema(); + X fromJson(JsonNode json); - default X convertToEntityAttribute(ResultSet pResultSet, int pColumn) throws SQLException + void writeField(Object value, DataOutputStream out) throws IOException; + + X readField(DataInputStream in) throws IOException; + + + @SuppressWarnings("unchecked") + default X convertToEntityAttribute(ResultSet resultSet, int column) throws SQLException { - return convertToEntityAttribute((Y) pResultSet.getObject(pColumn)); + return (resultSet.wasNull() ? null : convertToEntityAttribute((Y) resultSet.getObject(column))); } } diff --git a/jpalite-core/src/main/java/io/jpalite/JPACache.java b/jpalite-core/src/main/java/io/jpalite/JPACache.java new file mode 100644 index 0000000..a083680 --- /dev/null +++ b/jpalite-core/src/main/java/io/jpalite/JPACache.java @@ -0,0 +1,23 @@ +package io.jpalite; + +import java.time.Instant; +import java.util.concurrent.TimeUnit; + +public interface JPACache +{ + T find(String cacheRegion, String key); + + boolean containsKey(String cacheRegion, String key); + + void add(String cacheRegion, String key, T value, long expireTime, TimeUnit expireTimeUnit); + + void replace(String cacheRegion, String key, T value, long expireTime, TimeUnit expireTimeUnit); + + void evict(String cacheRegion, String key); + + void evictAll(String cacheRegion); + + void evictAllRegions(); + + Instant getLastModified(String cacheRegion); +} diff --git a/jpalite-core/src/main/java/io/jpalite/JPAEntity.java b/jpalite-core/src/main/java/io/jpalite/JPAEntity.java index 17e99f6..85c9643 100644 --- a/jpalite-core/src/main/java/io/jpalite/JPAEntity.java +++ b/jpalite-core/src/main/java/io/jpalite/JPAEntity.java @@ -156,7 +156,7 @@ public interface JPAEntity extends Serializable void _setPendingAction(PersistenceAction pendingAction); /** - * Get the current value of a specific field. + * Get the value of a specific field converted to database format * * @param fieldName The field name * @return The value assigned to the value @@ -302,6 +302,19 @@ public interface JPAEntity extends Serializable */ byte[] _serialize(); + /** + * Retrieve content of the JPAEntity as a JSON formatted string* + */ + String _toJson(); + + /** + * Load the entity from a JSON string + * + * @param jsonStr The json string + */ + void _fromJson(String jsonStr); + + /** * Compare the primary keys of the to entities * diff --git a/jpalite-core/src/main/java/io/jpalite/JPALiteEntityManager.java b/jpalite-core/src/main/java/io/jpalite/JPALiteEntityManager.java index f0edb7b..b8efcd8 100644 --- a/jpalite-core/src/main/java/io/jpalite/JPALiteEntityManager.java +++ b/jpalite-core/src/main/java/io/jpalite/JPALiteEntityManager.java @@ -28,8 +28,6 @@ /** * The JPALite implementation - * - * @see TradeSwitch Persistence Manager in Confluence */ public interface JPALiteEntityManager extends EntityManager, Closeable { @@ -43,7 +41,7 @@ public interface JPALiteEntityManager extends EntityManager, Closeable String PERSISTENCE_QUERY_LOG_SLOWTIME = "jpalite.persistence.log.slowQueries"; /** * The javax.persistence.query.timeout hint defines, in seconds, how long a query is allowed to run before it gets - * cancelled. TradeSwitch doesn’t handle this timeout itself but provides it to the JDBC driver via the JDBC + * cancelled. The JPA stack does’t handle this timeout itself but provides it to the JDBC driver via the JDBC * Statement.setTimeout method. */ String PERSISTENCE_QUERY_TIMEOUT = "jakarta.persistence.query.timeout"; @@ -52,45 +50,40 @@ public interface JPALiteEntityManager extends EntityManager, Closeable */ String PERSISTENCE_LOCK_TIMEOUT = "jakarta.persistence.lock.timeout"; /** - * Valid values are USE or BYPASS. If setting is not recognized it defaults to USE. + * Valid values are USE or BYPASS. If the setting is not recognised, it defaults to USE. *

- * The retrieveMode hint supports the values USE and BYPASS and tells TradeSwitch if it shall USE the second-level + * The retrieveMode hint supports the values USE and BYPASS and tell Query implementation if it shall USE the second-level * cache to retrieve an entity or if it shall BYPASS it and get it directly from the database. */ String PERSISTENCE_CACHE_RETRIEVEMODE = "jakarta.persistence.cache.retrieveMode"; /** * If set to true entities retrieved in {@link Query#getResultList()} is also cached */ - String TRADESWITCH_CACHE_RESULTLIST = "org.tradeswitch.cache.resultList"; - /** - * Used to hint persistence layer that the provided name should be used as the connection name, in the case of a - * JDBC type connection it will be used as the cursor name. - */ - String TRADESWITCH_CONNECTION_NAME = "org.tradeswitch.connectionName"; + String PERSISTENCE_CACHE_RESULTLIST = "jpalite.cache.resultList"; /** * Hint the JQPL parser to ignore fetchtype setting on basic fields effectively setting all basic fields to be * EAGERly fetched. */ - String TRADESWITCH_OVERRIDE_BASIC_FETCHTYPE = "org.tradeswitch.override.basicFetchType"; + String PERSISTENCE_OVERRIDE_BASIC_FETCHTYPE = "jpalite.override.basicFetchType"; /** * Valid values are EAGER or LAZY. If the setting is not recognised it is ignored. *

* Hint the JQPL parser to ignore fetchtype settings on all fields and effectively setting all fields to be EAGERly * or LAZYly fetched. */ - String TRADESWITCH_OVERRIDE_FETCHTYPE = "org.tradeswitch.override.FetchType"; + String PERSISTENCE_OVERRIDE_FETCHTYPE = "jpalite.override.FetchType"; /** * Valid values are TRUE or FALSE. If the setting is not recognized it is ignored. A hint that can be passed to the * Entity Manager or any Query to log the actual query that is executed. */ - String JPALITE_SHOW_SQL = "jpalite.showSql"; + String PERSISTENCE_SHOW_SQL = "jpalite.showSql"; /** * Valid values are EAGER or LAZY. If the setting is not recognised it is ignored. *

- * A hint that can be passed to a Native Query that selection is done on the primary key. This will allow the query + * A hint that can be passed to a Native Query that query is done using only the primary key. This will allow the query * executor to use L2 caching. */ - String TRADESWITCH_ONLY_PRIMARYKEY_USED = "org.tradeswitch.primarykey.used"; + String PERSISTENCE_PRIMARYKEY_USED = "jpalite.primarykey.used"; /** * Synchronize the entity to the underlying database. diff --git a/jpalite-core/src/main/java/io/jpalite/JPALitePersistenceUnit.java b/jpalite-core/src/main/java/io/jpalite/JPALitePersistenceUnit.java index 31b329a..5581396 100644 --- a/jpalite-core/src/main/java/io/jpalite/JPALitePersistenceUnit.java +++ b/jpalite-core/src/main/java/io/jpalite/JPALitePersistenceUnit.java @@ -17,18 +17,55 @@ package io.jpalite; +import io.jpalite.impl.CacheFormat; import jakarta.persistence.spi.PersistenceUnitInfo; -import org.infinispan.commons.configuration.BasicConfiguration; public interface JPALitePersistenceUnit extends PersistenceUnitInfo { + /** + * The name of the datasource to associate with persistence unit + * + * @return The datasource name + */ String getDataSourceName(); - String getCacheName(); + /** + * The cache region prefix. If null or blank no prefix will be applied + * + * @return The prefix + */ + String getCacheRegionPrefix(); + /** + * Retrieves the cache provider for the persistence unit. + * + * @return The cache provider as a string + */ String getCacheProvider(); - BasicConfiguration getCacheConfig(); + /** + * Gets the cache configuration for the persistence unit. + * + * @return The cache configuration as a string + */ + String getCacheConfig(); + + + /** + * Gets the cache client for the persistence unit. + * + * @return The cache client as a string + */ + String getCacheClient(); + + /** + * Retrieves the cache format for the persistence unit. + * + * @return The cache format for the persistence unit. The possible values are: + * - {@link CacheFormat#BINARY}: Indicates that the cache format is binary. + * - {@link CacheFormat#JSON}: Indicates that the cache format is JSON. + */ + CacheFormat getCacheFormat(); Boolean getMultiTenantMode(); } diff --git a/jpalite-core/src/main/java/io/jpalite/PersistenceContext.java b/jpalite-core/src/main/java/io/jpalite/PersistenceContext.java index 74a4ff6..9d86c51 100644 --- a/jpalite-core/src/main/java/io/jpalite/PersistenceContext.java +++ b/jpalite-core/src/main/java/io/jpalite/PersistenceContext.java @@ -30,9 +30,9 @@ public interface PersistenceContext extends EntityTransaction, AutoCloseable { /** - * The tradeswitch.persistence.jta hint is used to indicated that the transaction management is done via JTA. + * The jpalite.persistence.jta hint is used to signal the transaction management under JTA control. */ - String PERSISTENCE_JTA_MANAGED = "tradeswitch.persistence.jta"; + String PERSISTENCE_JTA_MANAGED = "jpalite.persistence.jta"; /** * The method is used to retrieve the persistence unit used to create the context diff --git a/jpalite-core/src/main/java/io/jpalite/PersistenceUnitNotFoundException.java b/jpalite-core/src/main/java/io/jpalite/PersistenceUnitNotFoundException.java new file mode 100644 index 0000000..3f95ae4 --- /dev/null +++ b/jpalite-core/src/main/java/io/jpalite/PersistenceUnitNotFoundException.java @@ -0,0 +1,26 @@ +package io.jpalite; + +import jakarta.persistence.PersistenceException; + +public class PersistenceUnitNotFoundException extends PersistenceException +{ + public PersistenceUnitNotFoundException() + { + super(); + } + + public PersistenceUnitNotFoundException(String message) + { + super(message); + } + + public PersistenceUnitNotFoundException(String message, Throwable cause) + { + super(message, cause); + } + + public PersistenceUnitNotFoundException(Throwable cause) + { + super(cause); + } +} diff --git a/jpalite-core/src/main/java/io/jpalite/impl/CacheFormat.java b/jpalite-core/src/main/java/io/jpalite/impl/CacheFormat.java new file mode 100644 index 0000000..63b8430 --- /dev/null +++ b/jpalite-core/src/main/java/io/jpalite/impl/CacheFormat.java @@ -0,0 +1,7 @@ +package io.jpalite.impl; + +public enum CacheFormat +{ + BINARY, + JSON +} diff --git a/jpalite-core/src/main/java/io/jpalite/impl/ConverterClassImpl.java b/jpalite-core/src/main/java/io/jpalite/impl/ConverterClassImpl.java index d81fab2..f0b2fcb 100644 --- a/jpalite-core/src/main/java/io/jpalite/impl/ConverterClassImpl.java +++ b/jpalite-core/src/main/java/io/jpalite/impl/ConverterClassImpl.java @@ -18,65 +18,38 @@ package io.jpalite.impl; import io.jpalite.ConverterClass; -import io.jpalite.TradeSwitchConvert; +import io.jpalite.FieldConvertType; import jakarta.persistence.Converter; -import jakarta.persistence.PersistenceException; -import lombok.Data; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -@Data +@Slf4j +@Getter public class ConverterClassImpl implements ConverterClass { - private static final Logger LOG = LoggerFactory.getLogger(ConverterClassImpl.class); - private Class convertClass; - private boolean autoApply; - private TradeSwitchConvert converter; - private Class attributeType; - private Class dbType; + private final boolean autoApply; + private final FieldConvertType converter; + private final Class attributeType; + - public ConverterClassImpl(Class convertClass) + public ConverterClassImpl(FieldConvertType converter) { - this.convertClass = convertClass; - Converter converter = this.convertClass.getAnnotation(Converter.class); - if (converter == null) { - LOG.warn("Missing @Converter annotation on {}", this.convertClass.getSimpleName()); + this.converter = converter; + + Converter converterAnnotation = this.converter.getClass().getAnnotation(Converter.class); + if (converterAnnotation == null) { autoApply = false; }//if else { - autoApply = converter.autoApply(); + autoApply = converterAnnotation.autoApply(); }//else - for (Method method : this.convertClass.getDeclaredMethods()) { - //Not a SYNTHETIC (generated method) - if (method.getName().equals("convertToDatabaseColumn") && - ((method.getModifiers() & 0x00001000) != 0x00001000) && - method.getParameterTypes().length == 1) { - attributeType = method.getParameterTypes()[0]; - dbType = method.getReturnType(); - break; - }//if - }//for - if (attributeType == null) { - LOG.warn("Error detecting the attribute type in {}", this.convertClass.getSimpleName()); - attributeType = Object.class; - dbType = Object.class; - }//if - - try { - this.converter = (TradeSwitchConvert) this.convertClass.getConstructor().newInstance(); - }//try - catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException ex) { - throw new PersistenceException(this.convertClass.getSimpleName() + " failed to instantiate", ex); - }//catch + attributeType = converter.getAttributeType(); }//ConverterClass @Override public String getName() { - return convertClass.getCanonicalName(); + return converter.getClass().getCanonicalName(); } }//ConverterClass diff --git a/jpalite-core/src/main/java/io/jpalite/impl/CustomPersistenceUnit.java b/jpalite-core/src/main/java/io/jpalite/impl/CustomPersistenceUnit.java index 6d8dfea..bbcd093 100644 --- a/jpalite-core/src/main/java/io/jpalite/impl/CustomPersistenceUnit.java +++ b/jpalite-core/src/main/java/io/jpalite/impl/CustomPersistenceUnit.java @@ -24,7 +24,6 @@ import jakarta.persistence.spi.ClassTransformer; import jakarta.persistence.spi.PersistenceUnitTransactionType; import lombok.Setter; -import org.infinispan.commons.configuration.BasicConfiguration; import javax.sql.DataSource; import java.net.URL; @@ -40,9 +39,11 @@ public class CustomPersistenceUnit implements JPALitePersistenceUnit private Boolean multiTenantMode = false; private String dataSourceName; - private String cacheName; + private String cacheRegionPrefix; + private String cacheClient; private String cacheProvider; - private BasicConfiguration cacheConfig; + private String cacheConfig; + private CacheFormat cacheFormat; private String providerClass = JPALitePersistenceProviderImpl.class.getName(); private PersistenceUnitTransactionType transactionType = PersistenceUnitTransactionType.RESOURCE_LOCAL; private ValidationMode validationMode = ValidationMode.NONE; @@ -61,9 +62,9 @@ public String getDataSourceName() } @Override - public String getCacheName() + public String getCacheRegionPrefix() { - return cacheName; + return cacheRegionPrefix; } @Override @@ -73,11 +74,23 @@ public String getCacheProvider() } @Override - public BasicConfiguration getCacheConfig() + public String getCacheClient() + { + return cacheClient; + } + + @Override + public String getCacheConfig() { return cacheConfig; } + @Override + public CacheFormat getCacheFormat() + { + return cacheFormat; + } + @Override public Boolean getMultiTenantMode() { diff --git a/jpalite-core/src/main/java/io/jpalite/impl/EntityFieldImpl.java b/jpalite-core/src/main/java/io/jpalite/impl/EntityFieldImpl.java index e89c451..864cd16 100644 --- a/jpalite-core/src/main/java/io/jpalite/impl/EntityFieldImpl.java +++ b/jpalite-core/src/main/java/io/jpalite/impl/EntityFieldImpl.java @@ -18,6 +18,9 @@ package io.jpalite.impl; import io.jpalite.*; +import io.jpalite.impl.fieldtypes.EnumFieldType; +import io.jpalite.impl.fieldtypes.ObjectFieldType; +import io.jpalite.impl.fieldtypes.OrdinalEnumFieldType; import jakarta.persistence.*; import lombok.Data; import lombok.extern.slf4j.Slf4j; @@ -45,87 +48,58 @@ public class EntityFieldImpl implements EntityField private static final boolean NATIVE_IMAGE = ImageInfo.inImageCode(); /** * The entity class - * - * @return the entity class */ - private Class enityClass; + private final Class enityClass; /** * Identifier of the entity - * - * @param name changes the name of the entity - * @return name of the client */ private final String name; /** * A unique field number assigned to the field. - * - * @return the field number */ private final int fieldNr; /** * The java class of the field - * - * @return the java class */ private Class type; /** * The type of field as per {@link FieldType} - * - * @return The field type */ private FieldType fieldType; /** * The SQL column linked to the field - * - * @return The SQL Column name */ private String column; /** * The mapping type specified by the field. See {@link MappingType}. - * - * @return The {@link MappingType} */ private MappingType mappingType; /** * True if the field is to be unique in the table - * - * @return the unique setting */ private boolean unique; /** * True of the field can be null - * - * @return the nullable setting */ private boolean nullable; /** * True if the field is insertable - * - * @return the insertable setting */ private boolean insertable; /** * True if the field is updatable. - * - * @return the updatable setting */ private boolean updatable; /** * True if the field is an ID Field - * - * @return the idField setting */ private boolean idField; /** * True if the field is a Version Field - * - * @return the version field setting */ private boolean versionField; /** * The getter for the field - * - * @return the setter method for the field */ private MethodHandle getter; /** @@ -142,39 +116,29 @@ public class EntityFieldImpl implements EntityField private Method setterMethod; /** * The {@link CascadeType} assigned to the field. - * - * @return the {@link CascadeType} setting */ private Set cascade; /** * The {@link FetchType} assigned to the field. - * - * @return the {@link FetchType} setting */ private FetchType fetchType; /** * Only applicable to non-Basic fields and indicates that the field is linked the field specified in mappedBy in the * entity represented by the field. - * - * @return the mappedBy setting */ private String mappedBy; /** * The columnDefinition value defined in the JoinColumn annotation linked to the field - * - * @return the columnDefinition setting */ private String columnDefinition; /** * The table value defined in the JoinColumn annotation linked to the field - * - * @return the table setting */ private String table; /** * The converter class used to convert the field to a SQL type */ - private TradeSwitchConvert converterClass; + private FieldConvertType converter; /** * Create a new entity field definition @@ -206,8 +170,11 @@ public EntityFieldImpl(Class enitityClass, Field field, int fieldNr) idField = false; versionField = false; - checkForConvert(field); + //The order below is important processMappingType(field); + + findConverter(field); + findGetterSetter(field); }//EntityField @@ -311,7 +278,7 @@ private void prosesBasicField(Field field) }//if insertable = false; updatable = false; - }//ifated + }//if nullable = false; }//if @@ -398,7 +365,7 @@ private boolean checkManyToManyField(Field field) return false; }//checkManyToManyField - private void checkForConvert(Field field) + private void findConverter(Field field) { Convert customType = field.getAnnotation(Convert.class); if (customType != null) { @@ -406,7 +373,7 @@ private void checkForConvert(Field field) //Check if the converter class was explicitly overridden if (customType.converter() != null) { fieldType = FieldType.TYPE_CUSTOMTYPE; - converterClass = (TradeSwitchConvert) customType.converter().getConstructor().newInstance(); + converter = (FieldConvertType) customType.converter().getConstructor().newInstance(); return; }//if }//try @@ -424,8 +391,31 @@ private void checkForConvert(Field field) ConverterClass converterClass = EntityMetaDataManager.getConvertClass(type); if (converterClass != null) { fieldType = FieldType.TYPE_CUSTOMTYPE; - this.converterClass = converterClass.getConverter(); + converter = converterClass.getConverter(); }//if + else { + switch (fieldType) { + case TYPE_ENUM -> { + converter = new EnumFieldType((Class>) type); + fieldType = FieldType.TYPE_CUSTOMTYPE; + } + case TYPE_ORDINAL_ENUM -> { + converter = new OrdinalEnumFieldType((Class>) type); + fieldType = FieldType.TYPE_CUSTOMTYPE; + } + case TYPE_OBJECT -> { + converter = new ObjectFieldType(); + fieldType = FieldType.TYPE_CUSTOMTYPE; + } + case TYPE_ENTITY -> { + //ignore + } + default -> LOG.warn("No converter class found for field {} type {}", name, fieldType); + + } + }//else + + }//checkForConvert @Override diff --git a/jpalite-core/src/main/java/io/jpalite/impl/EntityMetaDataImpl.java b/jpalite-core/src/main/java/io/jpalite/impl/EntityMetaDataImpl.java index 87f7959..87aa73a 100644 --- a/jpalite-core/src/main/java/io/jpalite/impl/EntityMetaDataImpl.java +++ b/jpalite-core/src/main/java/io/jpalite/impl/EntityMetaDataImpl.java @@ -96,10 +96,10 @@ public EntityMetaDataImpl(Class entityClass) IdClass idClass = entityClass.getAnnotation(IdClass.class); if (idClass != null) { - if (!EntityMetaDataManager.isRegistered(idClass.value())) { + //TODO: Added support for @EmbeddedId and fix implementation of @IdClass primaryKey = new EntityMetaDataImpl(idClass.value()); - ((EntityMetaDataImpl) primaryKey).entityType = EntityType.ENTITY_IDCLASS; + ((EntityMetaDataImpl) primaryKey).entityType = EntityType.ENTITY_IDCLASS; EntityMetaDataManager.register(primaryKey); }//if @@ -109,7 +109,7 @@ public EntityMetaDataImpl(Class entityClass) }//if versionField = null; - StringBuilder stringBuilder = new StringBuilder(""); + StringBuilder stringBuilder = new StringBuilder(); for (Field vField : entityClass.getDeclaredFields()) { if (!Modifier.isStatic(vField.getModifiers()) && !Modifier.isFinal(vField.getModifiers()) && @@ -196,64 +196,6 @@ public String toString() return "[" + entityName + "] Metadata -> Type:" + entityType + ", Entity Class:" + entityClass.getName() + ", Primary Key Class:" + primKeyClass; }//toString - @Override - public String getProtoFile() - { - StringBuilder protoFile = new StringBuilder("// File name: ") - .append(getName()).append(".proto\n") - .append("// Generated from : ") - .append(getClass().getName()) - .append("\n") - .append("syntax = \"proto2\";\n") - .append("package org.tradeswitch;\n"); - - Set protoLibs = new HashSet<>(); - entityFields.values().stream() - .filter(f -> f.getFieldType() == FieldType.TYPE_CUSTOMTYPE && - f.getConverterClass().prototypeLib() != null && - !f.getConverterClass().prototypeLib().isBlank()) - .forEach(f -> protoLibs.add(f.getConverterClass().prototypeLib())); - - protoLibs.forEach(lib -> protoFile.append("import \"").append(lib).append("\";\n")); - - protoFile.append("message ") - .append(entityClass.getSimpleName()) - .append("{\n"); - - for (EntityField field : entityFields.values()) { - protoFile.append("\t") - .append(field.isNullable() ? "optional " : "required "); - - switch (field.getFieldType()) { - case TYPE_ENTITY -> { - EntityMetaData vMetaData = EntityMetaDataManager.getMetaData(field.getType()); - if (vMetaData.getEntityType() == EntityType.ENTITY_EMBEDDABLE) { - if (!vMetaData.isCacheable()) { - throw new EntityMapException("Entity " + getName() + " is marked as cacheable but embeddable " + vMetaData.getName() + " to not marked as cacheable"); - }//if - protoFile.append(field.getType().getSimpleName()); - }//if - else { - protoFile.append(vMetaData.getIdField().getFieldType().getProtoType()); - }//else - }//case - case TYPE_ENUM -> protoFile.append("string"); - case TYPE_ORDINAL_ENUM -> protoFile.append("uint32"); - case TYPE_CUSTOMTYPE -> protoFile.append(field.getConverterClass().getFieldType()); - default -> protoFile.append(field.getFieldType().getProtoType()); - }//switch - - protoFile.append(" ") - .append(field.getName()) - .append(" = ") - .append(field.getFieldNr()) - .append(";\n"); - }//for - protoFile.append("}\n"); - - return protoFile.toString(); - }//getProtoFile - @Override public EntityType getEntityType() { @@ -411,7 +353,7 @@ public EntityField getVersionField() @Override @Nullable - public EntityMetaData getIPrimaryKeyMetaData() + public EntityMetaData getPrimaryKeyMetaData() { return primaryKey; }//getIPrimaryKeyMetaData @@ -430,6 +372,7 @@ public List getIdFields() }//getIdFields @Override + @Deprecated public String getColumns() { return columns; diff --git a/jpalite-core/src/main/java/io/jpalite/impl/JPAEntityImpl.java b/jpalite-core/src/main/java/io/jpalite/impl/JPAEntityImpl.java index 80491d2..32d9a93 100644 --- a/jpalite-core/src/main/java/io/jpalite/impl/JPAEntityImpl.java +++ b/jpalite-core/src/main/java/io/jpalite/impl/JPAEntityImpl.java @@ -17,18 +17,20 @@ package io.jpalite.impl; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import io.jpalite.PersistenceContext; import io.jpalite.*; import io.jpalite.impl.queries.JPALiteQueryImpl; import io.jpalite.impl.queries.QueryImpl; -import io.jpalite.impl.serializers.JPAEntityMarshaller; import io.jpalite.queries.QueryLanguage; import jakarta.annotation.Nonnull; import jakarta.persistence.*; import jakarta.persistence.spi.LoadState; -import org.infinispan.protostream.FileDescriptorSource; -import org.infinispan.protostream.GeneratedSchema; -import org.infinispan.protostream.SerializationContext; import org.slf4j.LoggerFactory; import java.io.*; @@ -43,7 +45,7 @@ import static jakarta.persistence.LockModeType.*; /** - * This class will be made the super class of all entity classes defined and managed by the TradeSwitch Entity Manager. + * This class will be made the super class of all entity classes defined and managed by the Entity Manager. *

* The JPA Maven plugin class will modify the bytecode of all entity classes change the super class to piont to * this class. @@ -54,7 +56,7 @@ * using here ;-) ) */ @SuppressWarnings({"java:S100", "java:S116"}) -public class JPAEntityImpl implements JPAEntity, GeneratedSchema +public class JPAEntityImpl implements JPAEntity { public static final String SELECT_CLAUSE = "select "; public static final String FROM_CLAUSE = " from "; @@ -428,7 +430,6 @@ public void _makeReference(Object primaryKey) _setPrimaryKey(primaryKey); _markLazyLoaded(); _clearModified(); - _setPendingAction(PersistenceAction.NONE); }//_makeReference @Override @@ -632,7 +633,7 @@ public X _getField(@Nonnull String fieldName) }//if return (X) switch (entityField.getFieldType()) { - case TYPE_CUSTOMTYPE -> entityField.getConverterClass().convertToDatabaseColumn(value); + case TYPE_CUSTOMTYPE -> entityField.getConverter().convertToDatabaseColumn(value); case TYPE_ENUM -> ((Enum) value).name(); case TYPE_ORDINAL_ENUM -> ((Enum) value).ordinal(); @@ -693,7 +694,7 @@ public Object _getPrimaryKey() }//if if ($$metadata.getIdFields().size() > 1) { - EntityMetaData primaryKey = $$metadata.getIPrimaryKeyMetaData(); + EntityMetaData primaryKey = $$metadata.getPrimaryKeyMetaData(); Object primKey = null; if (primaryKey != null) { primKey = primaryKey.getNewEntity(); @@ -722,7 +723,7 @@ public void _setPrimaryKey(Object primaryKey) if ($$metadata.getIdFields().size() > 1) { - EntityMetaData primaryKeyMetaData = $$metadata.getIPrimaryKeyMetaData(); + EntityMetaData primaryKeyMetaData = $$metadata.getPrimaryKeyMetaData(); if (primaryKeyMetaData == null) { throw new IllegalStateException("Missing IDClass for Entity [" + $$metadata.getName() + "]"); }//if @@ -776,7 +777,7 @@ protected LocalDateTime _JPAReadLocalDateTime(ResultSet resultSet, int column) t protected Object _JPAReadCustomType(ResultSet resultSet, EntityField field, int column) throws SQLException { - return field.getConverterClass().convertToEntityAttribute(resultSet, column); + return field.getConverter().convertToEntityAttribute(resultSet, column); }//_JPAReadCustomType private Object _JPAReadENUM(EntityField field, ResultSet row, int column) throws SQLException @@ -799,33 +800,37 @@ private Object _JPAReadOrdinalENUM(EntityField field, ResultSet row, int column) public JPAEntity _JPAReadEntity(EntityField field, ResultSet resultSet, String colPrefix, int col) throws SQLException { - EntityMetaData fieldMetaData = EntityMetaDataManager.getMetaData(field.getType()); - - //Read the primary key of the field and then check if the entity is not already managed - JPAEntity entity = (JPAEntity) fieldMetaData.getNewEntity(); - entity._setPersistenceContext(_getPersistenceContext()); - ((JPAEntityImpl) entity)._JPAReadField(resultSet, fieldMetaData.getIdField(), colPrefix, col); - JPAEntity managedEntity = null; - if (entity._getPrimaryKey() != null) { - if (_getPersistenceContext() != null) { - managedEntity = (JPAEntity) _getPersistenceContext().l1Cache().find(fieldMetaData.getEntityClass(), entity._getPrimaryKey(), true); - }//if - if (managedEntity == null) { - if (field.getFetchType() == FetchType.LAZY && (colPrefix == null || colPrefix.equals(resultSet.getMetaData().getColumnName(col)))) { - entity._markLazyLoaded(); - }//if - else { - entity._mapResultSet(colPrefix, resultSet); - }//else + //Read the field so that wasNull() can be used + resultSet.getObject(col); + if (!field.isNullable() || !resultSet.wasNull()) { + EntityMetaData fieldMetaData = EntityMetaDataManager.getMetaData(field.getType()); + //Read the primary key of the field and then check if the entity is not already managed + JPAEntity entity = (JPAEntity) fieldMetaData.getNewEntity(); + entity._setPersistenceContext(_getPersistenceContext()); + ((JPAEntityImpl) entity)._JPAReadField(resultSet, fieldMetaData.getIdField(), colPrefix, col); + if (entity._getPrimaryKey() != null) { if (_getPersistenceContext() != null) { - _getPersistenceContext().l1Cache().manage(entity); + managedEntity = (JPAEntity) _getPersistenceContext().l1Cache().find(fieldMetaData.getEntityClass(), entity._getPrimaryKey(), true); + }//if + + if (managedEntity == null) { + if (field.getFetchType() == FetchType.LAZY && (colPrefix == null || colPrefix.equals(resultSet.getMetaData().getColumnName(col)))) { + entity._markLazyLoaded(); + }//if + else { + entity._mapResultSet(colPrefix, resultSet); + }//else + + if (_getPersistenceContext() != null) { + _getPersistenceContext().l1Cache().manage(entity); + }//if + return entity; }//if - return entity; }//if - }//if + } return managedEntity; }//_JPAReadEntity @@ -840,18 +845,20 @@ public void _JPAReadField(ResultSet row, EntityField field, String colPrefix, in case TYPE_INTEGER -> field.invokeSetter(this, _JPAReadInteger(row, columnNr)); case TYPE_LONGLONG -> field.invokeSetter(this, _JPAReadLong(row, columnNr)); case TYPE_DOUBLEDOUBLE -> field.invokeSetter(this, _JPAReadDouble(row, columnNr)); + case TYPE_BOOL -> field.invokeSetter(this, row.getBoolean(columnNr)); case TYPE_INT -> field.invokeSetter(this, row.getInt(columnNr)); case TYPE_LONG -> field.invokeSetter(this, row.getLong(columnNr)); case TYPE_DOUBLE -> field.invokeSetter(this, row.getDouble(columnNr)); + case TYPE_STRING -> field.invokeSetter(this, _JPAReadString(row, columnNr)); case TYPE_TIMESTAMP -> field.invokeSetter(this, row.getTimestamp(columnNr)); case TYPE_LOCALTIME -> field.invokeSetter(this, _JPAReadLocalDateTime(row, columnNr)); - case TYPE_CUSTOMTYPE -> field.invokeSetter(this, _JPAReadCustomType(row, field, columnNr)); case TYPE_ENUM -> field.invokeSetter(this, _JPAReadENUM(field, row, columnNr)); case TYPE_ORDINAL_ENUM -> field.invokeSetter(this, _JPAReadOrdinalENUM(field, row, columnNr)); case TYPE_BYTES -> field.invokeSetter(this, row.getBytes(columnNr)); case TYPE_OBJECT -> field.invokeSetter(this, row.getObject(columnNr)); + case TYPE_CUSTOMTYPE -> field.invokeSetter(this, _JPAReadCustomType(row, field, columnNr)); case TYPE_ENTITY -> { if (field.getMappingType() == MappingType.ONE_TO_ONE || field.getMappingType() == MappingType.MANY_TO_ONE || field.getMappingType() == MappingType.EMBEDDED) { field.invokeSetter(this, _JPAReadEntity(field, row, colPrefix, columnNr)); @@ -905,27 +912,193 @@ public void _mapResultSet(String colPrefix, ResultSet resultSet) catch (Exception ex) { throw new EntityMapException("Error extracting the ResultSet Metadata", ex); }//catch - }//_maresultSet + }//_mapResultSet - private void writeObjects(ObjectOutput outStream) throws IOException + private void writeFields(DataOutputStream out) throws IOException { Collection fieldList = $$metadata.getEntityFields(); - outStream.writeShort(fieldList.size()); for (EntityField field : fieldList) { Object value = field.invokeGetter(this); - outStream.writeUTF(field.getName()); - outStream.writeObject(value); + if (value != null) { + out.writeShort(field.getFieldNr()); + if (field.getFieldType() == FieldType.TYPE_ENTITY) { + EntityMetaData metaData = ((JPAEntity) value)._getMetaData(); + if (metaData.getEntityType() == EntityType.ENTITY_EMBEDDABLE) { + ((JPAEntityImpl) value).writeFields(out); + }//if + else { + Object primaryKey = ((JPAEntity) value)._getPrimaryKey(); + //If the entity has multiple keys, then that primary key will be stored in an embedded object + if (primaryKey instanceof JPAEntity primaryKeyEntity) { + ((JPAEntityImpl) primaryKeyEntity).writeFields(out); + }//if + else { + EntityField keyField = metaData.getIdField(); + out.writeShort(keyField.getFieldNr()); + keyField.getConverter().writeField(primaryKey, out); + out.writeShort(0); //End of entity + }//else + }//else + } + else { + field.getConverter().writeField(value, out); + }//else + }//if + }//for + out.writeShort(0); //End of stream indicator + }//writeFields + + + private void readFields(DataInputStream in) throws IOException + { + int fieldNr = in.readShort(); + while (fieldNr > 0) { + EntityField field = $$metadata.getEntityFieldByNr(fieldNr); + + if (field.getFieldType() == FieldType.TYPE_ENTITY) { + EntityMetaData metaData = EntityMetaDataManager.getMetaData(field.getType()); + + JPAEntityImpl entity = (JPAEntityImpl) metaData.getNewEntity(); + entity.readFields(in); + if (metaData.getEntityType() == EntityType.ENTITY_DATABASE) { + entity._markLazyLoaded(); + } + field.invokeSetter(this, entity); + } + else { + field.invokeSetter(this, field.getConverter().readField(in)); + } + + fieldNr = in.readShort(); + }//while + + _clearModified(); + }//readFields + + + @SuppressWarnings("unchecked") + private void generateJson(JsonGenerator jsonGenerator) throws IOException + { + jsonGenerator.writeStartObject(); + + Collection fieldList = $$metadata.getEntityFields(); + for (EntityField field : fieldList) { + Object value = field.invokeGetter(this); + if (!field.isNullable() || value != null) { + if (field.getFieldType() == FieldType.TYPE_ENTITY) { + if (field.getMappingType() == MappingType.ONE_TO_ONE || field.getMappingType() == MappingType.MANY_TO_ONE || field.getMappingType() == MappingType.EMBEDDED) { + jsonGenerator.writeFieldName(field.getName()); + if (value == null) { + jsonGenerator.writeNull(); + } + else { + + EntityMetaData metaData = ((JPAEntity) value)._getMetaData(); + if (metaData.getEntityType() == EntityType.ENTITY_EMBEDDABLE) { + ((JPAEntityImpl) value).generateJson(jsonGenerator); + }//if + else { + Object primaryKey = ((JPAEntity) value)._getPrimaryKey(); + //If the entity has multiple keys, then that primary key will be stored in an embedded object + if (primaryKey instanceof JPAEntity primaryKeyEntity) { + ((JPAEntityImpl) primaryKeyEntity).generateJson(jsonGenerator); + }//if + else { + EntityField keyField = metaData.getIdField(); + jsonGenerator.writeStartObject(); + jsonGenerator.writeFieldName(keyField.getName()); + keyField.getConverter().toJson(jsonGenerator, primaryKey); + jsonGenerator.writeEndObject(); + }//else + }//else + }//else + }//if + }//if + else { + jsonGenerator.writeFieldName(field.getName()); + if (value == null) { + jsonGenerator.writeNull(); + } + else { + field.getConverter().toJson(jsonGenerator, value); + } + }//else + }//if }//for - }//writeObjects + jsonGenerator.writeEndObject(); + }//_toJson + + @Override + public String _toJson() + { + try { + ObjectMapper mapper = new ObjectMapper(JsonFactory.builder().build()); + mapper.registerModule(new JavaTimeModule()); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + JsonGenerator jsonGenerator = mapper + .writerWithDefaultPrettyPrinter() + .createGenerator(outputStream); + + generateJson(jsonGenerator); + jsonGenerator.close(); + return outputStream.toString(); + } + catch (IOException ex) { + throw new CachingException("Error generating json structure for entity [" + this._getMetaData().getName() + "]", ex); + } + } + + private void _fromJson(JsonNode jsonNode) + { + Iterator> iter = jsonNode.fields(); + + while (iter.hasNext()) { + Map.Entry node = iter.next(); + + EntityField field = $$metadata.getEntityField(node.getKey()); + if (node.getValue().isNull()) { + field.invokeSetter(this, null); + } + else { + if (field.getFieldType() == FieldType.TYPE_ENTITY) { + EntityMetaData metaData = EntityMetaDataManager.getMetaData(field.getType()); + + JPAEntityImpl entity = (JPAEntityImpl) metaData.getNewEntity(); + entity._fromJson(node.getValue()); + + if (metaData.getEntityType() == EntityType.ENTITY_DATABASE) { + entity._markLazyLoaded(); + } + field.invokeSetter(this, entity); + } + else { + field.invokeSetter(this, field.getConverter().fromJson(node.getValue())); + } + }//else + } + _clearModified(); + } + + public void _fromJson(String jsonStr) + { + try { + ObjectMapper mapper = new ObjectMapper(); + JsonNode nodes = mapper.readTree(jsonStr); + _fromJson(nodes); + } + catch (JsonProcessingException ex) { + throw new PersistenceException("Error parsing json text string", ex); + } + } @Override public byte[] _serialize() { try { ByteArrayOutputStream recvOut = new ByteArrayOutputStream(); - ObjectOutputStream stream = new ObjectOutputStream(recvOut); - writeObjects(stream); - stream.flush(); + DataOutputStream out = new DataOutputStream(recvOut); + writeFields(out); + out.flush(); return recvOut.toByteArray(); }//try @@ -934,29 +1107,15 @@ public byte[] _serialize() }//catch }//_serialise - @SuppressWarnings("java:S112") // Throwable is correct - private void readObjects(ObjectInput inputStream) throws Throwable - { - int nrItems = inputStream.readShort(); - while (nrItems > 0) { - nrItems--; - String fieldName = inputStream.readUTF(); - EntityField field = $$metadata.getEntityField(fieldName); - field.invokeSetter(this, inputStream.readObject()); - }//while - }//readObjects - @Override public void _deserialize(byte[] bytes) { try { ByteArrayInputStream recvOut = new ByteArrayInputStream(bytes); - ObjectInputStream stream = new ObjectInputStream(recvOut); - readObjects(stream); - - _clearModified(); + DataInputStream in = new DataInputStream(recvOut); + readFields(in); }//try - catch (Throwable ex) { + catch (IOException ex) { throw new PersistenceException("Error de-serialising the entity", ex); }//catch }//_deserialize @@ -967,41 +1126,4 @@ public boolean _entityEquals(JPAEntity entity) return (entity._getMetaData().getEntityClass().equals(_getMetaData().getEntityClass()) && entity._getPrimaryKey().equals(_getPrimaryKey())); } - - @Override - public String getProtoFileName() - { - return getClass().getSimpleName() + ".proto"; - } - - @Override - public String getProtoFile() throws UncheckedIOException - { - return _getMetaData().getProtoFile(); - } - - @Override - public void registerSchema(SerializationContext serCtx) - { - /* - * Register the marshals and schemas for converter classes if not already registered - */ - _getMetaData().getEntityFields().stream() - .filter(f -> f.getFieldType() == FieldType.TYPE_CUSTOMTYPE && - f.getConverterClass().prototypeLib() != null && - !f.getConverterClass().prototypeLib().isBlank() && - !serCtx.canMarshall(f.getConverterClass().prototypeLib())) - .forEach(f -> { - f.getConverterClass().getSchema().registerSchema(serCtx); - f.getConverterClass().getSchema().registerMarshallers(serCtx); - }); - - serCtx.registerProtoFiles(FileDescriptorSource.fromString(getProtoFileName(), getProtoFile())); - } - - @Override - public void registerMarshallers(SerializationContext serCtx) - { - serCtx.registerMarshaller(new JPAEntityMarshaller<>(getClass())); - } }//JPAEntityImpl diff --git a/jpalite-core/src/main/java/io/jpalite/impl/JPALiteEntityManagerImpl.java b/jpalite-core/src/main/java/io/jpalite/impl/JPALiteEntityManagerImpl.java index 4ba49ef..fa28958 100755 --- a/jpalite-core/src/main/java/io/jpalite/impl/JPALiteEntityManagerImpl.java +++ b/jpalite-core/src/main/java/io/jpalite/impl/JPALiteEntityManagerImpl.java @@ -79,7 +79,7 @@ public JPALiteEntityManagerImpl(PersistenceContext persistenceContext, EntityMan else { opened = null; }//else - }//TradeSwitchEntityManagerImpl + }//JPALiteEntityManagerImpl // @Override @@ -793,6 +793,6 @@ public T unwrap(Class cls) throw new IllegalArgumentException("Could not unwrap this [" + this + "] as requested Java type [" + cls.getName() + "]"); } -}//TradeSwitchEntityManagerImpl +}//JPALiteEntityManagerImpl //--------------------------------------------------------------------[ End ]--- diff --git a/jpalite-core/src/main/java/io/jpalite/impl/caching/EntityCacheImpl.java b/jpalite-core/src/main/java/io/jpalite/impl/caching/EntityCacheImpl.java new file mode 100644 index 0000000..96ec234 --- /dev/null +++ b/jpalite-core/src/main/java/io/jpalite/impl/caching/EntityCacheImpl.java @@ -0,0 +1,303 @@ +/* + * 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.impl.caching; + +import io.jpalite.*; +import io.jpalite.impl.CacheFormat; +import io.jpalite.impl.JPAConfig; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Scope; +import jakarta.annotation.Nonnull; +import jakarta.persistence.SharedCacheMode; +import jakarta.transaction.SystemException; +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.InvocationTargetException; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +@SuppressWarnings("java:S3740")//Have to work without generics +@Slf4j +public class EntityCacheImpl implements EntityCache +{ + private static final int ACTION_REPLACE = 1; + private static final int ACTION_REMOVE = 2; + + private static final Tracer TRACER = GlobalOpenTelemetry.get().getTracer(EntityCacheImpl.class.getName()); + public static final String NO_TRANSACTION_ACTIVE = "No Transaction active"; + public static final String ENTITY_ATTR = "entity"; + public static final String ENTITY_KEY = "key"; + private static final boolean CACHING_ENABLED = JPAConfig.getValue("jpalite.persistence.l2cache", true); + + private final CacheFormat cacheFormat; + private final List batchQueue = new ArrayList<>(); + private boolean inTransaction; + private JPACache jpaCache = null; + + private record CacheEntry(int action, JPAEntity entity) + { + } + + @SuppressWarnings("unchecked") + public EntityCacheImpl(JPALitePersistenceUnit persistenceUnit) + { + cacheFormat = persistenceUnit.getCacheFormat(); + inTransaction = false; + if (CACHING_ENABLED && !persistenceUnit.getSharedCacheMode().equals(SharedCacheMode.NONE)) { + try { + Class jpaCacheClass = (Class) Thread.currentThread().getContextClassLoader().loadClass(persistenceUnit.getCacheProvider()); + jpaCache = jpaCacheClass.getConstructor(String.class, String.class, String.class).newInstance(persistenceUnit.getCacheClient(), persistenceUnit.getCacheConfig(), persistenceUnit.getCacheRegionPrefix()); + } + catch (ClassNotFoundException | InvocationTargetException | InstantiationException | + IllegalAccessException | NoSuchMethodException ex) { + throw new CachingException("Error loading cache provider class [" + persistenceUnit.getCacheProvider() + "]", ex); + } + }//if + }//EntityCacheImpl + + public T find(Class entityType, Object primaryKey) + { + Span span = TRACER.spanBuilder("EntityCache::find").setSpanKind(SpanKind.SERVER).startSpan(); + try (Scope ignored = span.makeCurrent()) { + long start = System.currentTimeMillis(); + if (jpaCache != null) { + EntityMetaData metaData = EntityMetaDataManager.getMetaData(entityType); + if (metaData.isCacheable()) { + String key = primaryKey.toString(); + span.setAttribute(ENTITY_KEY, key); + span.setAttribute(ENTITY_ATTR, entityType.getName()); + if (cacheFormat == CacheFormat.BINARY) { + byte[] bytes = jpaCache.find(metaData.getName(), key); + if (bytes != null) { + LOG.debug("Searching L2 cache (Binary) for key [{}] - Hit in {}ms", key, System.currentTimeMillis() - start); + T entity = metaData.getNewEntity(); + ((JPAEntity) entity)._deserialize(bytes); + return entity; + }//if + }//if + else { + String jsonStr = jpaCache.find(metaData.getName(), key); + if (jsonStr != null) { + LOG.debug("Searching L2 cache (JSON) for key [{}] - Hit in {}ms", key, System.currentTimeMillis() - start); + T entity = metaData.getNewEntity(); + ((JPAEntity) entity)._fromJson(jsonStr); + return entity; + }//if + } + LOG.debug("Searching L2 cache for key [{}] - Missed in {}ms", key, System.currentTimeMillis() - start); + }//if + else { + LOG.debug("Entity {} is not cacheable", metaData.getName()); + }//else + }//if + }//try + finally { + span.end(); + }//finally + + return null; + }//find + + @Override + public void replace(JPAEntity entity) + { + if (jpaCache != null && entity._getMetaData().isCacheable() && inTransaction) { + batchQueue.add(new CacheEntry(ACTION_REPLACE, entity)); + }//if + }//replace + + @Override + public void add(JPAEntity entity) + { + Span span = TRACER.spanBuilder("EntityCache::add").setSpanKind(SpanKind.SERVER).startSpan(); + try (Scope ignored = span.makeCurrent()) { + if (jpaCache != null && entity._getMetaData().isCacheable()) { + long start = System.currentTimeMillis(); + String key = entity._getPrimaryKey().toString(); + span.setAttribute(ENTITY_KEY, key); + span.setAttribute(ENTITY_ATTR, entity._getMetaData().getName()); + + jpaCache.add(entity._getMetaData().getName(), key, (cacheFormat.equals(CacheFormat.BINARY) ? entity._serialize() : entity._toJson()), entity._getMetaData().getIdleTime(), entity._getMetaData().getCacheTimeUnit()); + LOG.debug("Adding/Replacing Entity with key [{}] in L2 cache in {}ms", key, System.currentTimeMillis() - start); + }//if + }//try + finally { + span.end(); + } + }//add + + @Override + @Nonnull + public Instant getLastModified(Class entityType) + { + Span span = TRACER.spanBuilder("EntityCache::getLastModified").setSpanKind(SpanKind.SERVER).startSpan(); + try (Scope ignored = span.makeCurrent()) { + EntityMetaData metaData = EntityMetaDataManager.getMetaData(entityType); + if (jpaCache != null && metaData.isCacheable()) { + return jpaCache.getLastModified(metaData.getName()); + }//if + + return Instant.now(); + }//try + finally { + span.end(); + }//finally + }//getLastModified + + @Override + public boolean contains(Class entityType, Object primaryKey) + { + Span span = TRACER.spanBuilder("EntityCache::contains").setSpanKind(SpanKind.SERVER).startSpan(); + try (Scope ignored = span.makeCurrent()) { + EntityMetaData metaData = EntityMetaDataManager.getMetaData(entityType); + if (jpaCache != null && metaData.isCacheable()) { + return jpaCache.containsKey(metaData.getName(), primaryKey.toString()); + }//if + return false; + }//try + finally { + span.end(); + }//finally + }//contains + + @Override + public void evict(Class entityType, Object primaryKey) + { + Span span = TRACER.spanBuilder("EntityCache::evict using Primary key").setSpanKind(SpanKind.SERVER).startSpan(); + try (Scope ignored = span.makeCurrent()) { + EntityMetaData metaData = EntityMetaDataManager.getMetaData(entityType); + if (jpaCache != null && metaData.isCacheable()) { + jpaCache.evict(metaData.getName(), primaryKey.toString()); + }//if + }//try + finally { + span.end(); + }//finally + }//evict + + @Override + public void evict(Class entityType) + { + Span span = TRACER.spanBuilder("EntityCache::evict by type").setSpanKind(SpanKind.SERVER).startSpan(); + try (Scope ignored = span.makeCurrent()) { + EntityMetaData metaData = EntityMetaDataManager.getMetaData(entityType); + if (jpaCache != null && metaData.isCacheable()) { + jpaCache.evictAll(metaData.getName()); + }//if + }//try + finally { + span.end(); + } + }//evict + + @Override + public void evictAll() + { + Span span = TRACER.spanBuilder("EntityCache::evictAll").setSpanKind(SpanKind.SERVER).startSpan(); + try (Scope ignored = span.makeCurrent()) { + if (jpaCache != null) { + jpaCache.evictAllRegions(); + }//if + }//try + finally { + span.end(); + }//finally + }//evictAll + + @Override + public void begin() throws SystemException + { + Span span = TRACER.spanBuilder("EntityCache::begin").setSpanKind(SpanKind.SERVER).startSpan(); + try (Scope ignored = span.makeCurrent()) { + if (CACHING_ENABLED) { + if (inTransaction) { + throw new SystemException("Transaction already in progress"); + }//if + inTransaction = true; + }//if + }//try + finally { + span.end(); + }//finally + }//begin + + @Override + public void commit() throws SystemException + { + Span span = TRACER.spanBuilder("EntityCache::commit").setSpanKind(SpanKind.SERVER).startSpan(); + try (Scope ignored = span.makeCurrent()) { + if (jpaCache != null) { + if (!inTransaction) { + throw new SystemException(NO_TRANSACTION_ACTIVE); + }//if + + inTransaction = false; + batchQueue.forEach(e -> { + if (e.action == ACTION_REMOVE) { + jpaCache.evict(e.entity()._getMetaData().getName(), + e.entity._getPrimaryKey().toString()); + }//if + else { + jpaCache.replace(e.entity()._getMetaData().getName(), + e.entity._getPrimaryKey().toString(), + (cacheFormat.equals(CacheFormat.BINARY) ? e.entity()._serialize() : e.entity()._toJson()), + e.entity()._getMetaData().getIdleTime(), + e.entity()._getMetaData().getCacheTimeUnit()); + }//if + }); + batchQueue.clear(); + }//if + }//try + finally { + span.end(); + }//finally + }//commit + + + @Override + public void rollback() throws SystemException + { + if (CACHING_ENABLED) { + if (!inTransaction) { + throw new SystemException(NO_TRANSACTION_ACTIVE); + }//if + + inTransaction = false; + batchQueue.clear(); + }//if + }//rollback + + @Override + @SuppressWarnings("unchecked") + public T unwrap(Class cls) + { + if (cls.isAssignableFrom(this.getClass())) { + return (T) this; + } + + if (cls.isAssignableFrom(EntityCache.class)) { + return (T) this; + } + + throw new IllegalArgumentException("Could not unwrap this [" + this + "] as requested Java type [" + cls.getName() + "]"); + } +} diff --git a/jpalite-core/src/main/java/io/jpalite/impl/EntityL2CacheImpl.java b/jpalite-core/src/main/java/io/jpalite/impl/caching/EntityL2CacheImpl.java similarity index 75% rename from jpalite-core/src/main/java/io/jpalite/impl/EntityL2CacheImpl.java rename to jpalite-core/src/main/java/io/jpalite/impl/caching/EntityL2CacheImpl.java index 0020492..328c0cf 100644 --- a/jpalite-core/src/main/java/io/jpalite/impl/EntityL2CacheImpl.java +++ b/jpalite-core/src/main/java/io/jpalite/impl/caching/EntityL2CacheImpl.java @@ -15,9 +15,10 @@ * limitations under the License. */ -package io.jpalite.impl; +package io.jpalite.impl.caching; import io.jpalite.*; +import io.jpalite.impl.JPAConfig; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanKind; @@ -29,16 +30,16 @@ import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; import jakarta.persistence.SharedCacheMode; -import jakarta.transaction.*; +import jakarta.transaction.SystemException; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.infinispan.client.hotrod.RemoteCache; import org.infinispan.client.hotrod.RemoteCacheManager; -import org.infinispan.client.hotrod.exceptions.HotRodClientException; import org.infinispan.commons.api.query.Query; +import org.infinispan.commons.configuration.StringConfiguration; +import java.time.Instant; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; @@ -51,12 +52,12 @@ public class EntityL2CacheImpl implements EntityCache public static final String ENTITY_ATTR = "entity"; private RemoteCacheManager remoteCacheManager; private final JPALitePersistenceUnit persistenceUnit; - private static final boolean CACHING_ENABLED = JPAConfig.getValue("tradeswitch.persistence.l2cache", true); + private static final boolean CACHING_ENABLED = JPAConfig.getValue("jpalite.persistence.l2cache", true); private boolean inTransaction; private final List batchQueue = new ArrayList<>(); private static final int ACTION_ADD = 0; - private static final int ACTION_UPDATE = 1; + private static final int ACTION_REPLACE = 1; private static final int ACTION_REMOVE = 2; @Getter @@ -106,9 +107,9 @@ private RemoteCache getCache() }//if }//if - RemoteCache cache = remoteCacheManager.getCache(persistenceUnit.getCacheName()); + RemoteCache cache = remoteCacheManager.getCache(persistenceUnit.getCacheRegionPrefix()); if (cache == null) { - cache = remoteCacheManager.administration().getOrCreateCache(persistenceUnit.getCacheName(), persistenceUnit.getCacheConfig()); + cache = remoteCacheManager.administration().getOrCreateCache(persistenceUnit.getCacheRegionPrefix(), new StringConfiguration(persistenceUnit.getCacheConfig())); }//if return cache; } @@ -165,39 +166,9 @@ public T find(Class entityType, Object primaryKey) return null; }//find - @Override - @Nonnull - public List search(Class entityType, String query) - { - Span span = TRACER.spanBuilder("EntityL2CacheImpl::search").setSpanKind(SpanKind.SERVER).startSpan(); - try (Scope ignored = span.makeCurrent()) { - checkEntityType(entityType); - RemoteCache cache = getCache(); - if (cache != null) { - String queryText = "from org.tradeswitch." + entityType.getSimpleName() + " " + query; - try { - span.setAttribute("query", queryText); - span.setAttribute(ENTITY_ATTR, entityType.getName()); - - LOG.debug("Querying L2 cache : {}", queryText); - Query q = cache.query(queryText); - List result = q.execute().list(); - LOG.debug("Querying L2 cache - Found {} records", result.size()); - return result; - }//try - catch (HotRodClientException ex) { - LOG.debug("Search error:{}", ex.getMessage(), ex); - }//catch - }//if - return Collections.emptyList(); - }//try - finally { - span.end(); - }//finally - }//search @Override - public void update(JPAEntity entity) + public void replace(JPAEntity entity) { checkEntityInstance(entity); @@ -205,11 +176,11 @@ public void update(JPAEntity entity) RemoteCache cache = getCache(); if (cache != null) { String key = makeCacheKey(entity.getClass(), entity._getPrimaryKey()); - batchQueue.add(new CacheEntry(ACTION_UPDATE, key, entity, -1, TimeUnit.SECONDS, entity._getMetaData().getIdleTime(), entity._getMetaData().getCacheTimeUnit())); + batchQueue.add(new CacheEntry(ACTION_REPLACE, key, entity, -1, TimeUnit.SECONDS, entity._getMetaData().getIdleTime(), entity._getMetaData().getCacheTimeUnit())); batchQueue.add(new CacheEntry(ACTION_ADD, entity.getClass().getName(), System.currentTimeMillis(), -1, TimeUnit.SECONDS, -1, TimeUnit.SECONDS)); }//if }//if - }//update + }//replace @Override public void add(JPAEntity entity) @@ -237,61 +208,33 @@ public void add(JPAEntity entity) }//add @Override - public void remove(JPAEntity entity) - { - Span span = TRACER.spanBuilder("EntityL2CacheImpl::remove").setSpanKind(SpanKind.SERVER).startSpan(); - try (Scope ignored = span.makeCurrent()) { - checkEntityInstance(entity); - - if (CACHING_ENABLED) { - long start = System.currentTimeMillis(); - RemoteCache cache = getCache(); - if (cache != null) { - String key = makeCacheKey(entity.getClass(), entity._getPrimaryKey()); - span.setAttribute("key", key); - span.setAttribute(ENTITY_ATTR, entity._getMetaData().getName()); - if (inTransaction) { - batchQueue.add(new CacheEntry(ACTION_REMOVE, key, entity, -1, TimeUnit.SECONDS, -1, TimeUnit.SECONDS)); - batchQueue.add(new CacheEntry(ACTION_ADD, entity.getClass().getName(), System.currentTimeMillis(), -1, TimeUnit.SECONDS, -1, TimeUnit.SECONDS)); - }//if - else { - cache.remove(key); - //Write a timestamp for the update - cache.put(entity.getClass().getName(), System.currentTimeMillis(), -1, TimeUnit.SECONDS, -1, TimeUnit.SECONDS); - LOG.debug("Removed Entity with key [{}] from L2 cache in {}m", key, System.currentTimeMillis() - start); - }//else - }//if - }//if - }//try - finally { - span.end(); - }//finally - }//remove - - public long lastModified(Class entityType) + @Nonnull + public Instant getLastModified(Class entityType) { - Long time = -1L; - Span span = TRACER.spanBuilder("EntityL2CacheImpl::lastModified").setSpanKind(SpanKind.SERVER).startSpan(); + Span span = TRACER.spanBuilder("EntityL2CacheImpl::getLastModified").setSpanKind(SpanKind.SERVER).startSpan(); try (Scope ignored = span.makeCurrent()) { checkEntityType(entityType); + + Long time; RemoteCache cache = getCache(); if (cache != null) { span.setAttribute(ENTITY_ATTR, entityType.getName()); time = cache.get(entityType.getName()); - if (time != null) { - return time; - }//if - - time = System.currentTimeMillis(); - cache.put(entityType.getName(), time, -1, TimeUnit.SECONDS, -1, TimeUnit.SECONDS); + if (time == null) { + time = System.currentTimeMillis(); + cache.put(entityType.getName(), time, -1, TimeUnit.SECONDS, -1, TimeUnit.SECONDS); + } }//if + else { + time = System.currentTimeMillis(); + } + + return Instant.ofEpochMilli(time); }//try finally { span.end(); }//finally - - return time; - }//lastModified + }//getLastModified @Override public boolean contains(Class entityType, Object primaryKey) @@ -362,13 +305,13 @@ public void evictAll() }//evictAll @Override - public void begin() throws NotSupportedException, SystemException + public void begin() throws SystemException { Span span = TRACER.spanBuilder("EntityL2CacheImpl::begin").setSpanKind(SpanKind.SERVER).startSpan(); try (Scope ignored = span.makeCurrent()) { if (CACHING_ENABLED) { if (inTransaction) { - throw new NotSupportedException("Transaction already in progress"); + throw new SystemException("Transaction already in progress"); }//if inTransaction = true; }//if @@ -379,7 +322,7 @@ public void begin() throws NotSupportedException, SystemException }//begin @Override - public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException + public void commit() throws SystemException { Span span = TRACER.spanBuilder("EntityL2CacheImpl::commit").setSpanKind(SpanKind.SERVER).startSpan(); try (Scope ignored = span.makeCurrent()) { @@ -415,7 +358,7 @@ public void commit() throws RollbackException, HeuristicMixedException, Heuristi @Override - public void rollback() throws IllegalStateException, SecurityException, SystemException + public void rollback() throws SystemException { if (CACHING_ENABLED) { if (!inTransaction) { diff --git a/jpalite-core/src/main/java/io/jpalite/impl/caching/JPALiteInfinispanCache.java b/jpalite-core/src/main/java/io/jpalite/impl/caching/JPALiteInfinispanCache.java new file mode 100644 index 0000000..33e61c3 --- /dev/null +++ b/jpalite-core/src/main/java/io/jpalite/impl/caching/JPALiteInfinispanCache.java @@ -0,0 +1,120 @@ +package io.jpalite.impl.caching; + +import io.jpalite.CachingException; +import io.jpalite.JPACache; +import io.quarkus.arc.Arc; +import io.quarkus.arc.InstanceHandle; +import io.quarkus.infinispan.client.runtime.InfinispanClientProducer; +import org.infinispan.client.hotrod.RemoteCache; +import org.infinispan.client.hotrod.RemoteCacheManager; +import org.infinispan.commons.configuration.StringConfiguration; + +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.concurrent.TimeUnit; + + +public class JPALiteInfinispanCache implements JPACache +{ + private static final String REGION_TIMESTAMP_NAME = "io.jpalite.region.$timestamps$"; + + private final String cacheClientName; + private final String configuration; + private final String regionPrefix; + + private RemoteCacheManager remoteCacheManager; + + public JPALiteInfinispanCache(String cacheClientName, String configuration, String regionPrefix) + { + this.regionPrefix = (regionPrefix == null || "".equals(regionPrefix)) ? "" : regionPrefix + " - "; + this.cacheClientName = cacheClientName; + this.configuration = configuration; + } + + private RemoteCache getCache(String cacheRegion) + { + if (remoteCacheManager == null) { + InstanceHandle infinispanClientProducer = Arc.container().instance(InfinispanClientProducer.class); + if (infinispanClientProducer.isAvailable()) { + remoteCacheManager = infinispanClientProducer.get().getNamedRemoteCacheManager(cacheClientName); + }//if + if (remoteCacheManager == null || !remoteCacheManager.isStarted()) { + remoteCacheManager = null; + throw new CachingException("Error loading cache provider"); + }//if + }//if + + + RemoteCache cache = remoteCacheManager.getCache(regionPrefix + cacheRegion); + if (cache == null) { + cache = remoteCacheManager.administration().getOrCreateCache(regionPrefix + cacheRegion, new StringConfiguration(configuration)); + }//if + + return cache; + } + + @Override + public T find(String cacheRegion, String key) + { + RemoteCache cache = getCache(cacheRegion); + return cache.get(key); + } + + @Override + public boolean containsKey(String cacheRegion, String key) + { + RemoteCache cache = getCache(cacheRegion); + return cache.containsKey(key); + } + + @Override + public void add(String cacheRegion, String key, T value, long expireTime, TimeUnit expireTimeUnit) + { + getCache(cacheRegion).put(key, value, -1, TimeUnit.SECONDS, expireTime, expireTimeUnit); + getCache(REGION_TIMESTAMP_NAME).put(cacheRegion, DateTimeFormatter.ISO_INSTANT.format(Instant.now().truncatedTo(ChronoUnit.MILLIS))); + } + + @Override + public void replace(String cacheRegion, String key, T value, long expireTime, TimeUnit expireTimeUnit) + { + getCache(cacheRegion).replace(key, value, -1, TimeUnit.SECONDS, expireTime, expireTimeUnit); + getCache(REGION_TIMESTAMP_NAME).put(cacheRegion, DateTimeFormatter.ISO_INSTANT.format(Instant.now().truncatedTo(ChronoUnit.MILLIS))); + } + + @Override + public void evict(String cacheRegion, String key) + { + getCache(cacheRegion).remove(key); + getCache(REGION_TIMESTAMP_NAME).put(cacheRegion, DateTimeFormatter.ISO_INSTANT.format(Instant.now().truncatedTo(ChronoUnit.MILLIS))); + } + + @Override + public void evictAll(String cacheRegion) + { + getCache(cacheRegion).clear(); + getCache(REGION_TIMESTAMP_NAME).put(cacheRegion, DateTimeFormatter.ISO_INSTANT.format(Instant.now().truncatedTo(ChronoUnit.MILLIS))); + } + + @Override + public void evictAllRegions() + { + RemoteCache cache = getCache(REGION_TIMESTAMP_NAME); + cache.keySet().forEach(region -> getCache(region).clear()); + cache.clear(); + } + + @Override + public Instant getLastModified(String cacheRegion) + { + RemoteCache cache = getCache(REGION_TIMESTAMP_NAME); + String lastModified = cache.get(cacheRegion); + if (lastModified == null) { + Instant time = Instant.now(); + cache.put(cacheRegion, DateTimeFormatter.ISO_INSTANT.format(time.truncatedTo(ChronoUnit.MILLIS))); + return time; + } + + return Instant.parse(lastModified); + } +} diff --git a/jpalite-core/src/main/java/io/jpalite/impl/db/DatabasePoolFactory.java b/jpalite-core/src/main/java/io/jpalite/impl/db/DatabasePoolFactory.java index 09c14e6..d3d9927 100644 --- a/jpalite-core/src/main/java/io/jpalite/impl/db/DatabasePoolFactory.java +++ b/jpalite-core/src/main/java/io/jpalite/impl/db/DatabasePoolFactory.java @@ -29,8 +29,6 @@ /** * The DatabasePoolFactory class is part of the JPALite implementation - * - * @see TradeSwitch Persistence Manager in Confluence */ public class DatabasePoolFactory { diff --git a/jpalite-core/src/main/java/io/jpalite/impl/db/DatabasePoolImpl.java b/jpalite-core/src/main/java/io/jpalite/impl/db/DatabasePoolImpl.java index f983629..474272c 100644 --- a/jpalite-core/src/main/java/io/jpalite/impl/db/DatabasePoolImpl.java +++ b/jpalite-core/src/main/java/io/jpalite/impl/db/DatabasePoolImpl.java @@ -35,9 +35,7 @@ import static io.jpalite.PersistenceContext.PERSISTENCE_JTA_MANAGED; /** - * The DatabasePoolImpl class is part of the TradeSwitch JPA implementation - * - * @see TradeSwitch Persistence Manager in Confluence + * The DatabasePoolImpl class is part of the JPA implementation */ public class DatabasePoolImpl implements DatabasePool { diff --git a/jpalite-core/src/main/java/io/jpalite/impl/db/PersistenceContextImpl.java b/jpalite-core/src/main/java/io/jpalite/impl/db/PersistenceContextImpl.java index babb2ff..30395fe 100644 --- a/jpalite-core/src/main/java/io/jpalite/impl/db/PersistenceContextImpl.java +++ b/jpalite-core/src/main/java/io/jpalite/impl/db/PersistenceContextImpl.java @@ -20,7 +20,7 @@ import io.jpalite.PersistenceContext; import io.jpalite.*; import io.jpalite.impl.EntityL1LocalCacheImpl; -import io.jpalite.impl.EntityL2CacheImpl; +import io.jpalite.impl.caching.EntityCacheImpl; import io.jpalite.impl.queries.EntityDeleteQueryImpl; import io.jpalite.impl.queries.EntityInsertQueryImpl; import io.jpalite.impl.queries.EntityUpdateQueryImpl; @@ -34,10 +34,10 @@ import io.quarkus.runtime.Application; import jakarta.annotation.Nonnull; import jakarta.enterprise.inject.spi.CDI; -import jakarta.persistence.RollbackException; -import jakarta.persistence.TransactionRequiredException; import jakarta.persistence.*; -import jakarta.transaction.*; +import jakarta.transaction.Status; +import jakarta.transaction.SystemException; +import jakarta.transaction.TransactionManager; import org.eclipse.microprofile.config.ConfigProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,8 +48,8 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; -import static io.jpalite.JPALiteEntityManager.JPALITE_SHOW_SQL; import static io.jpalite.JPALiteEntityManager.PERSISTENCE_QUERY_LOG_SLOWTIME; +import static io.jpalite.JPALiteEntityManager.PERSISTENCE_SHOW_SQL; import static io.jpalite.PersistenceAction.*; /** @@ -197,7 +197,8 @@ public PersistenceContextImpl(DatabasePool pool, JPALitePersistenceUnit persiste entityL1Cache = new EntityL1LocalCacheImpl(this); - entityL2Cache = new EntityL2CacheImpl(this.persistenceUnit); + //entityL2Cache = new EntityL2CacheImpl(this.persistenceUnit); + entityL2Cache = new EntityCacheImpl(this.persistenceUnit); LOG.debug("Created {}", this); @@ -229,7 +230,7 @@ public void setProperty(String name, Object value) slowQueryTime = slowQuery; }//if } - case JPALITE_SHOW_SQL -> { + case PERSISTENCE_SHOW_SQL -> { if (value instanceof String strValue) { value = Boolean.parseBoolean(strValue); }//if @@ -772,7 +773,7 @@ private void flushEntityInternal(@Nonnull JPAEntity entity) if (action == PersistenceAction.DELETE) { entity._setEntityState(EntityState.REMOVED); if (entity._getMetaData().isCacheable()) { - l2Cache().remove(entity); + l2Cache().evict(entity.get$$EntityClass(), entity._getPrimaryKey()); }//if cascadeRemove(Set.of(MappingType.MANY_TO_ONE), entity); @@ -787,7 +788,7 @@ private void flushEntityInternal(@Nonnull JPAEntity entity) }//try }//if else if (entity._getMetaData().isCacheable()) { - l2Cache().update(entity); + l2Cache().replace(entity); }//else if cascadePersist(Set.of(MappingType.ONE_TO_MANY, MappingType.ONE_TO_ONE), entity); @@ -974,7 +975,7 @@ public void begin() catch (SQLException ex) { throw new PersistenceException("Error beginning a transaction", ex); }//catch - catch (SystemException | NotSupportedException ex) { + catch (SystemException ex) { throw new PersistenceException("Error beginning a transaction in TransactionManager", ex); }//catch finally { @@ -1019,8 +1020,7 @@ public void commit() setRollbackOnly(); throw new PersistenceException("Error committing transaction", ex); }//catch - catch (SystemException | jakarta.transaction.RollbackException | - HeuristicMixedException | HeuristicRollbackException ex) { + catch (SystemException ex) { setRollbackOnly(); throw new PersistenceException("Error committing transaction in TransactionManager", ex); }//catch @@ -1091,6 +1091,11 @@ public boolean isActive() } // + public boolean isReadonly() + { + return readOnly; + } + // public long getSlowQueryTime() { @@ -1113,11 +1118,6 @@ public void setEnableLogging(boolean pEnableLogging) connection.setEnableLogging(pEnableLogging && showSql); }//setEnableLogging - public boolean isReadonly() - { - return readOnly; - } - public void setReadonly(boolean pReadonly) { readOnly = pReadonly; diff --git a/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/BigDecimalFieldType.java b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/BigDecimalFieldType.java new file mode 100644 index 0000000..365418e --- /dev/null +++ b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/BigDecimalFieldType.java @@ -0,0 +1,78 @@ +package io.jpalite.impl.fieldtypes; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonNode; +import io.jpalite.FieldConvertType; +import jakarta.persistence.Converter; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.sql.ResultSet; +import java.sql.SQLException; + +@Converter(autoApply = true) +public class BigDecimalFieldType implements FieldConvertType +{ + @Override + public Class getAttributeType() + { + return BigDecimal.class; + } + + @Override + public void toJson(JsonGenerator jsonGenerator, BigDecimal value) throws IOException + { + jsonGenerator.writeStartObject(); + jsonGenerator.writeFieldName("scale"); + jsonGenerator.writeNumber(value.scale()); + jsonGenerator.writeFieldName("value"); + jsonGenerator.writeString(value.toPlainString()); + jsonGenerator.writeEndObject(); + } + + @Override + public BigDecimal fromJson(JsonNode json) + { + int scale = json.get("scale").asInt(); + return new BigDecimal(json.get("value").textValue()).setScale(scale, RoundingMode.HALF_DOWN); + } + + @Override + public void writeField(Object value, DataOutputStream out) throws IOException + { + if (value == null) { + value = BigDecimal.ZERO; + } + out.writeInt(((BigDecimal) value).scale()); + out.writeUTF(((BigDecimal) value).toPlainString()); + } + + @Override + public BigDecimal readField(DataInputStream in) throws IOException + { + int scale = in.readInt(); + return new BigDecimal(in.readUTF()).setScale(scale, RoundingMode.HALF_DOWN); + } + + @Override + public BigDecimal convertToEntityAttribute(ResultSet resultSet, int column) throws SQLException + { + BigDecimal val = resultSet.getBigDecimal(column); + return (resultSet.wasNull() ? null : convertToEntityAttribute(val)); + } + + @Override + public BigDecimal convertToDatabaseColumn(BigDecimal attribute) + { + return attribute; + } + + @Override + public BigDecimal convertToEntityAttribute(BigDecimal dbData) + { + return dbData; + } +} diff --git a/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/BoolFieldType.java b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/BoolFieldType.java new file mode 100644 index 0000000..934d6b7 --- /dev/null +++ b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/BoolFieldType.java @@ -0,0 +1,67 @@ +package io.jpalite.impl.fieldtypes; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonNode; +import io.jpalite.FieldConvertType; +import jakarta.persistence.Converter; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.sql.ResultSet; +import java.sql.SQLException; + +@Converter(autoApply = true) +public class BoolFieldType implements FieldConvertType +{ + @Override + public Class getAttributeType() + { + return boolean.class; + } + + @Override + public void toJson(JsonGenerator jsonGenerator, Boolean value) throws IOException + { + jsonGenerator.writeBoolean(value); + } + + @Override + public Boolean fromJson(JsonNode json) + { + return json.booleanValue(); + } + + @Override + public void writeField(Object value, DataOutputStream out) throws IOException + { + if (value == null) { + value = Boolean.FALSE; + } + out.writeBoolean((Boolean) value); + } + + @Override + public Boolean readField(DataInputStream in) throws IOException + { + return in.readBoolean(); + } + + @Override + public Boolean convertToEntityAttribute(ResultSet resultSet, int column) throws SQLException + { + return convertToEntityAttribute(resultSet.getBoolean(column)); + } + + @Override + public Boolean convertToDatabaseColumn(Boolean attribute) + { + return attribute; + } + + @Override + public Boolean convertToEntityAttribute(Boolean dbData) + { + return dbData; + } +} diff --git a/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/BooleanFieldType.java b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/BooleanFieldType.java new file mode 100644 index 0000000..4babca2 --- /dev/null +++ b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/BooleanFieldType.java @@ -0,0 +1,66 @@ +package io.jpalite.impl.fieldtypes; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonNode; +import io.jpalite.FieldConvertType; +import jakarta.persistence.Converter; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.sql.ResultSet; +import java.sql.SQLException; + +@Converter(autoApply = true) +public class BooleanFieldType implements FieldConvertType +{ + @Override + public Class getAttributeType() + { + return Boolean.class; + } + + + @Override + public void toJson(JsonGenerator jsonGenerator, Boolean value) throws IOException + { + jsonGenerator.writeBoolean(value); + } + + @Override + public Boolean fromJson(JsonNode json) + { + return json.booleanValue(); + } + + @Override + public void writeField(Object value, DataOutputStream out) throws IOException + { + out.writeBoolean((Boolean) value); + } + + @Override + public Boolean readField(DataInputStream in) throws IOException + { + return in.readBoolean(); + } + + @Override + public Boolean convertToEntityAttribute(ResultSet resultSet, int column) throws SQLException + { + boolean val = resultSet.getBoolean(column); + return (resultSet.wasNull() ? null : convertToEntityAttribute(val)); + } + + @Override + public Boolean convertToDatabaseColumn(Boolean attribute) + { + return attribute; + } + + @Override + public Boolean convertToEntityAttribute(Boolean dbData) + { + return dbData; + } +} diff --git a/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/BytesFieldType.java b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/BytesFieldType.java new file mode 100644 index 0000000..4f23057 --- /dev/null +++ b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/BytesFieldType.java @@ -0,0 +1,82 @@ +package io.jpalite.impl.fieldtypes; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonNode; +import io.jpalite.CachingException; +import io.jpalite.FieldConvertType; +import jakarta.persistence.Converter; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.sql.ResultSet; +import java.sql.SQLException; + +@Converter(autoApply = true) +public class BytesFieldType implements FieldConvertType +{ + @Override + public Class getAttributeType() + { + return byte[].class; + } + + @Override + public void toJson(JsonGenerator jsonGenerator, byte[] value) throws IOException + { + jsonGenerator.writeBinary(value); + } + + @Override + public byte[] fromJson(JsonNode json) + { + try { + return json.binaryValue(); + } + catch (IOException ex) { + throw new CachingException("Error reading byte[]", ex); + } + } + + @Override + public void writeField(Object value, DataOutputStream out) throws IOException + { + byte[] bytes = (byte[]) value; + out.writeInt(bytes.length); + if (bytes.length > 0) { + out.write(bytes); + } + } + + @Override + public byte[] readField(DataInputStream in) throws IOException + { + byte[] bytes = null; + + int size = in.readInt(); + if (size > 0) { + bytes = in.readNBytes(size); + }//if + + return bytes; + } + + @Override + public byte[] convertToEntityAttribute(ResultSet resultSet, int column) throws SQLException + { + byte[] val = resultSet.getBytes(column); + return (resultSet.wasNull() ? null : convertToEntityAttribute(val)); + } + + @Override + public byte[] convertToDatabaseColumn(byte[] attribute) + { + return attribute; + } + + @Override + public byte[] convertToEntityAttribute(byte[] dbData) + { + return dbData; + } +} diff --git a/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/DoubleDoubleFieldType.java b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/DoubleDoubleFieldType.java new file mode 100644 index 0000000..805499a --- /dev/null +++ b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/DoubleDoubleFieldType.java @@ -0,0 +1,65 @@ +package io.jpalite.impl.fieldtypes; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonNode; +import io.jpalite.FieldConvertType; +import jakarta.persistence.Converter; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.sql.ResultSet; +import java.sql.SQLException; + +@Converter(autoApply = true) +public class DoubleDoubleFieldType implements FieldConvertType +{ + @Override + public Class getAttributeType() + { + return Double.class; + } + + @Override + public void toJson(JsonGenerator jsonGenerator, Double value) throws IOException + { + jsonGenerator.writeNumber(value); + } + + @Override + public Double fromJson(JsonNode json) + { + return json.doubleValue(); + } + + @Override + public void writeField(Object value, DataOutputStream out) throws IOException + { + out.writeDouble((Double) value); + } + + @Override + public Double readField(DataInputStream in) throws IOException + { + return in.readDouble(); + } + + @Override + public Double convertToEntityAttribute(ResultSet resultSet, int column) throws SQLException + { + double val = resultSet.getDouble(column); + return (resultSet.wasNull() ? null : convertToEntityAttribute(val)); + } + + @Override + public Double convertToDatabaseColumn(Double attribute) + { + return attribute; + } + + @Override + public Double convertToEntityAttribute(Double dbData) + { + return dbData; + } +} diff --git a/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/DoubleFieldType.java b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/DoubleFieldType.java new file mode 100644 index 0000000..51d7dac --- /dev/null +++ b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/DoubleFieldType.java @@ -0,0 +1,67 @@ +package io.jpalite.impl.fieldtypes; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonNode; +import io.jpalite.FieldConvertType; +import jakarta.persistence.Converter; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.sql.ResultSet; +import java.sql.SQLException; + +@Converter(autoApply = true) +public class DoubleFieldType implements FieldConvertType +{ + @Override + public Class getAttributeType() + { + return double.class; + } + + @Override + public void toJson(JsonGenerator jsonGenerator, Double value) throws IOException + { + jsonGenerator.writeNumber(value); + } + + @Override + public Double fromJson(JsonNode json) + { + return json.doubleValue(); + } + + @Override + public void writeField(Object value, DataOutputStream out) throws IOException + { + if (value == null) { + value = 0.0; + } + out.writeDouble((Double) value); + } + + @Override + public Double readField(DataInputStream in) throws IOException + { + return in.readDouble(); + } + + @Override + public Double convertToEntityAttribute(ResultSet resultSet, int column) throws SQLException + { + return convertToEntityAttribute(resultSet.getDouble(column)); + } + + @Override + public Double convertToDatabaseColumn(Double attribute) + { + return attribute; + } + + @Override + public Double convertToEntityAttribute(Double dbData) + { + return dbData; + } +} diff --git a/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/EnumFieldType.java b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/EnumFieldType.java new file mode 100644 index 0000000..c1c9107 --- /dev/null +++ b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/EnumFieldType.java @@ -0,0 +1,83 @@ +package io.jpalite.impl.fieldtypes; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.TextNode; +import io.jpalite.FieldConvertType; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * Special converter type of enums. It is used internally by {@link io.jpalite.impl.EntityFieldImpl} + * Note that the type must not have a @Converter annotation + */ +public class EnumFieldType implements FieldConvertType, String> +{ + private final Class> enumType; + + public EnumFieldType(Class> enumType) + { + this.enumType = enumType; + } + + @Override + @SuppressWarnings("rawtypes") + public Class getAttributeType() + { + return Enum.class; + } + + @Override + public void toJson(JsonGenerator jsonGenerator, Enum value) throws IOException + { + jsonGenerator.writeString(value.name()); + } + + @Override + public Enum fromJson(JsonNode enumName) + { + for (Enum enumValue : enumType.getEnumConstants()) { + if (enumValue.name().equals(enumName.textValue())) { + return enumValue; + }//if + }//for + + return null; + } + + @Override + public void writeField(Object value, DataOutputStream out) throws IOException + { + out.writeShort(((Enum) value).ordinal()); + } + + @Override + public Enum readField(DataInputStream in) throws IOException + { + int ordinal = in.readShort(); + return enumType.getEnumConstants()[ordinal]; + } + + @Override + public Enum convertToEntityAttribute(ResultSet resultSet, int column) throws SQLException + { + String val = resultSet.getString(column); + return (resultSet.wasNull() ? null : convertToEntityAttribute(val)); + } + + @Override + public String convertToDatabaseColumn(Enum attribute) + { + return (attribute).name(); + } + + @Override + public Enum convertToEntityAttribute(String dbData) + { + return fromJson(TextNode.valueOf(dbData)); + } +} diff --git a/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/IntFieldType.java b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/IntFieldType.java new file mode 100644 index 0000000..9fc268f --- /dev/null +++ b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/IntFieldType.java @@ -0,0 +1,67 @@ +package io.jpalite.impl.fieldtypes; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonNode; +import io.jpalite.FieldConvertType; +import jakarta.persistence.Converter; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.sql.ResultSet; +import java.sql.SQLException; + +@Converter(autoApply = true) +public class IntFieldType implements FieldConvertType +{ + @Override + public Class getAttributeType() + { + return int.class; + } + + @Override + public void toJson(JsonGenerator jsonGenerator, Integer value) throws IOException + { + jsonGenerator.writeNumber(value); + } + + @Override + public Integer fromJson(JsonNode json) + { + return json.intValue(); + } + + @Override + public void writeField(Object value, DataOutputStream out) throws IOException + { + if (value == null) { + value = 0; + } + out.writeInt((Integer) value); + } + + @Override + public Integer readField(DataInputStream in) throws IOException + { + return in.readInt(); + } + + @Override + public Integer convertToEntityAttribute(ResultSet resultSet, int column) throws SQLException + { + return convertToEntityAttribute(resultSet.getInt(column)); + } + + @Override + public Integer convertToDatabaseColumn(Integer attribute) + { + return attribute; + } + + @Override + public Integer convertToEntityAttribute(Integer dbData) + { + return dbData; + } +} diff --git a/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/IntegerFieldType.java b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/IntegerFieldType.java new file mode 100644 index 0000000..4abc2c5 --- /dev/null +++ b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/IntegerFieldType.java @@ -0,0 +1,65 @@ +package io.jpalite.impl.fieldtypes; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonNode; +import io.jpalite.FieldConvertType; +import jakarta.persistence.Converter; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.sql.ResultSet; +import java.sql.SQLException; + +@Converter(autoApply = true) +public class IntegerFieldType implements FieldConvertType +{ + @Override + public Class getAttributeType() + { + return Integer.class; + } + + @Override + public void toJson(JsonGenerator jsonGenerator, Integer value) throws IOException + { + jsonGenerator.writeNumber(value); + } + + @Override + public Integer fromJson(JsonNode json) + { + return json.intValue(); + } + + @Override + public void writeField(Object value, DataOutputStream out) throws IOException + { + out.writeInt((Integer) value); + } + + @Override + public Integer readField(DataInputStream in) throws IOException + { + return in.readInt(); + } + + @Override + public Integer convertToEntityAttribute(ResultSet resultSet, int column) throws SQLException + { + int val = resultSet.getInt(column); + return (resultSet.wasNull() ? null : convertToEntityAttribute(val)); + } + + @Override + public Integer convertToDatabaseColumn(Integer attribute) + { + return attribute; + } + + @Override + public Integer convertToEntityAttribute(Integer dbData) + { + return dbData; + } +} diff --git a/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/LocalDateTimeFieldType.java b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/LocalDateTimeFieldType.java new file mode 100644 index 0000000..4a3b71d --- /dev/null +++ b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/LocalDateTimeFieldType.java @@ -0,0 +1,77 @@ +package io.jpalite.impl.fieldtypes; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonNode; +import io.jpalite.FieldConvertType; +import jakarta.persistence.Converter; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; + +@Converter(autoApply = true) +public class LocalDateTimeFieldType implements FieldConvertType +{ + @Override + public Class getAttributeType() + { + return LocalDateTime.class; + } + + @Override + public void toJson(JsonGenerator jsonGenerator, LocalDateTime value) throws IOException + { + jsonGenerator.writeString(DateTimeFormatter.ISO_INSTANT.format(value.truncatedTo(ChronoUnit.NANOS))); + } + + @Override + public LocalDateTime fromJson(JsonNode json) + { + return LocalDateTime.from(Instant.parse(json.textValue())); + } + + @Override + public void writeField(Object value, DataOutputStream out) throws IOException + { + if (value == null) { + out.writeLong(0L); + } + else { + out.writeLong(((LocalDateTime) value).toInstant(ZoneOffset.UTC).getEpochSecond()); + } + } + + @Override + public LocalDateTime readField(DataInputStream in) throws IOException + { + return LocalDateTime.ofInstant(Instant.ofEpochMilli(in.readLong()), ZoneId.of("UTC")); + } + + @Override + public LocalDateTime convertToEntityAttribute(ResultSet resultSet, int column) throws SQLException + { + Timestamp val = resultSet.getTimestamp(column); + return (resultSet.wasNull() ? null : convertToEntityAttribute(val)); + } + + @Override + public Timestamp convertToDatabaseColumn(LocalDateTime attribute) + { + return Timestamp.from(attribute.toInstant(ZoneOffset.UTC)); + } + + @Override + public LocalDateTime convertToEntityAttribute(Timestamp dbData) + { + return dbData.toLocalDateTime(); + } +} diff --git a/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/LongFieldType.java b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/LongFieldType.java new file mode 100644 index 0000000..7c6327d --- /dev/null +++ b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/LongFieldType.java @@ -0,0 +1,67 @@ +package io.jpalite.impl.fieldtypes; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonNode; +import io.jpalite.FieldConvertType; +import jakarta.persistence.Converter; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.sql.ResultSet; +import java.sql.SQLException; + +@Converter(autoApply = true) +public class LongFieldType implements FieldConvertType +{ + @Override + public Class getAttributeType() + { + return long.class; + } + + @Override + public void toJson(JsonGenerator jsonGenerator, Long value) throws IOException + { + jsonGenerator.writeNumber(value); + } + + @Override + public Long fromJson(JsonNode json) + { + return json.longValue(); + } + + @Override + public void writeField(Object value, DataOutputStream out) throws IOException + { + if (value == null) { + value = 0L; + } + out.writeLong((Long) value); + } + + @Override + public Long readField(DataInputStream in) throws IOException + { + return in.readLong(); + } + + @Override + public Long convertToEntityAttribute(ResultSet resultSet, int column) throws SQLException + { + return convertToEntityAttribute(resultSet.getLong(column)); + } + + @Override + public Long convertToDatabaseColumn(Long attribute) + { + return attribute; + } + + @Override + public Long convertToEntityAttribute(Long dbData) + { + return dbData; + } +} diff --git a/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/LongLongFieldType.java b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/LongLongFieldType.java new file mode 100644 index 0000000..9a1c320 --- /dev/null +++ b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/LongLongFieldType.java @@ -0,0 +1,65 @@ +package io.jpalite.impl.fieldtypes; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonNode; +import io.jpalite.FieldConvertType; +import jakarta.persistence.Converter; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.sql.ResultSet; +import java.sql.SQLException; + +@Converter(autoApply = true) +public class LongLongFieldType implements FieldConvertType +{ + @Override + public Class getAttributeType() + { + return Long.class; + } + + @Override + public void toJson(JsonGenerator jsonGenerator, Long value) throws IOException + { + jsonGenerator.writeNumber(value); + } + + @Override + public Long fromJson(JsonNode json) + { + return json.longValue(); + } + + @Override + public void writeField(Object value, DataOutputStream out) throws IOException + { + out.writeLong((Long) value); + } + + @Override + public Long readField(DataInputStream in) throws IOException + { + return in.readLong(); + } + + @Override + public Long convertToEntityAttribute(ResultSet resultSet, int column) throws SQLException + { + long val = resultSet.getLong(column); + return (resultSet.wasNull() ? null : convertToEntityAttribute(val)); + } + + @Override + public Long convertToDatabaseColumn(Long attribute) + { + return attribute; + } + + @Override + public Long convertToEntityAttribute(Long dbData) + { + return dbData; + } +} diff --git a/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/ObjectFieldType.java b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/ObjectFieldType.java new file mode 100644 index 0000000..13689cc --- /dev/null +++ b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/ObjectFieldType.java @@ -0,0 +1,92 @@ +package io.jpalite.impl.fieldtypes; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonNode; +import io.jpalite.CachingException; +import io.jpalite.FieldConvertType; + +import java.io.*; +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * Special converter type of Object type. It is used internally by {@link io.jpalite.impl.EntityFieldImpl} + * Note that the type must not have a @Converter annotation + */ +public class ObjectFieldType implements FieldConvertType +{ + @Override + public Class getAttributeType() + { + return Object.class; + } + + @Override + public void toJson(JsonGenerator jsonGenerator, Object value) throws IOException + { + jsonGenerator.writeBinary(convertToDatabaseColumn(value)); + } + + @Override + public Object fromJson(JsonNode json) + { + try { + return convertToEntityAttribute(json.binaryValue()); + } + catch (IOException ex) { + throw new CachingException("Error reading byte[]", ex); + } + } + + @Override + public void writeField(Object value, DataOutputStream out) throws IOException + { + byte[] bytes = convertToDatabaseColumn(value); + out.writeShort(bytes.length); + out.write(bytes); + } + + @Override + public Object readField(DataInputStream in) throws IOException + { + int size = in.readShort(); + byte[] bytes = in.readNBytes(size); + + return convertToEntityAttribute(bytes); + } + + @Override + public Object convertToEntityAttribute(ResultSet resultSet, int column) throws SQLException + { + byte[] val = resultSet.getBytes(column); + return (resultSet.wasNull() ? null : convertToEntityAttribute(val)); + } + + @Override + public byte[] convertToDatabaseColumn(Object attribute) + { + try { + ByteArrayOutputStream recvOut = new ByteArrayOutputStream(); + ObjectOutputStream stream = new ObjectOutputStream(recvOut); + stream.writeObject(attribute); + stream.flush(); + return recvOut.toByteArray(); + } + catch (IOException ex) { + throw new CachingException("Error writing Object to stream", ex); + } + } + + @Override + public Object convertToEntityAttribute(byte[] dbData) + { + try { + ByteArrayInputStream in = new ByteArrayInputStream(dbData); + ObjectInputStream stream = new ObjectInputStream(in); + return stream.readObject(); + } + catch (IOException | ClassNotFoundException ex) { + throw new CachingException("Error reading Object from stream", ex); + } + } +} diff --git a/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/OrdinalEnumFieldType.java b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/OrdinalEnumFieldType.java new file mode 100644 index 0000000..bca79f8 --- /dev/null +++ b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/OrdinalEnumFieldType.java @@ -0,0 +1,77 @@ +package io.jpalite.impl.fieldtypes; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonNode; +import io.jpalite.FieldConvertType; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * Special converter type of ordinal enums. It is used internally by {@link io.jpalite.impl.EntityFieldImpl} + * Note that the type must not have a @Converter annotation + */ +public class OrdinalEnumFieldType implements FieldConvertType, Integer> +{ + private final Class> enumType; + + public OrdinalEnumFieldType(Class> enumType) + { + this.enumType = enumType; + } + + @Override + @SuppressWarnings("rawtypes") + public Class getAttributeType() + { + return Enum.class; + } + + @Override + public void toJson(JsonGenerator jsonGenerator, Enum value) throws IOException + { + jsonGenerator.writeNumber(value.ordinal()); + } + + @Override + public Enum fromJson(JsonNode json) + { + int ordinal = json.intValue(); + return enumType.getEnumConstants()[ordinal]; + } + + @Override + public void writeField(Object value, DataOutputStream out) throws IOException + { + out.writeShort(((Enum) value).ordinal()); + } + + @Override + public Enum readField(DataInputStream in) throws IOException + { + int ordinal = in.readShort(); + return enumType.getEnumConstants()[ordinal]; + } + + @Override + public Enum convertToEntityAttribute(ResultSet resultSet, int column) throws SQLException + { + int val = resultSet.getInt(column); + return (resultSet.wasNull() ? null : convertToEntityAttribute(val)); + } + + @Override + public Integer convertToDatabaseColumn(Enum attribute) + { + return attribute.ordinal(); + } + + @Override + public Enum convertToEntityAttribute(Integer ordinal) + { + return enumType.getEnumConstants()[ordinal]; + } +} diff --git a/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/StringFieldType.java b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/StringFieldType.java new file mode 100644 index 0000000..95c0b17 --- /dev/null +++ b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/StringFieldType.java @@ -0,0 +1,65 @@ +package io.jpalite.impl.fieldtypes; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonNode; +import io.jpalite.FieldConvertType; +import jakarta.persistence.Converter; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.sql.ResultSet; +import java.sql.SQLException; + +@Converter(autoApply = true) +public class StringFieldType implements FieldConvertType +{ + @Override + public Class getAttributeType() + { + return String.class; + } + + @Override + public void toJson(JsonGenerator jsonGenerator, String value) throws IOException + { + jsonGenerator.writeString(value); + } + + @Override + public String fromJson(JsonNode json) + { + return json.textValue(); + } + + @Override + public void writeField(Object value, DataOutputStream out) throws IOException + { + out.writeUTF((String) value); + } + + @Override + public String readField(DataInputStream in) throws IOException + { + return in.readUTF(); + } + + @Override + public String convertToEntityAttribute(ResultSet resultSet, int column) throws SQLException + { + String val = resultSet.getString(column); + return (resultSet.wasNull() ? null : convertToEntityAttribute(val)); + } + + @Override + public String convertToDatabaseColumn(String attribute) + { + return attribute; + } + + @Override + public String convertToEntityAttribute(String dbData) + { + return dbData; + } +} diff --git a/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/TimestampFieldType.java b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/TimestampFieldType.java new file mode 100644 index 0000000..10df047 --- /dev/null +++ b/jpalite-core/src/main/java/io/jpalite/impl/fieldtypes/TimestampFieldType.java @@ -0,0 +1,74 @@ +package io.jpalite.impl.fieldtypes; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonNode; +import io.jpalite.FieldConvertType; +import jakarta.persistence.Converter; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; + +@Converter(autoApply = true) +public class TimestampFieldType implements FieldConvertType +{ + @Override + public Class getAttributeType() + { + return Timestamp.class; + } + + @Override + public void toJson(JsonGenerator jsonGenerator, Timestamp value) throws IOException + { + jsonGenerator.writeString(DateTimeFormatter.ISO_INSTANT.format(value.toInstant().truncatedTo(ChronoUnit.NANOS))); + } + + @Override + public Timestamp fromJson(JsonNode json) + { + return Timestamp.from(Instant.parse(json.textValue())); + } + + @Override + public void writeField(Object value, DataOutputStream out) throws IOException + { + if (value == null) { + out.writeLong(0L); + } + else { + out.writeLong(((Timestamp) value).getTime()); + } + } + + @Override + public Timestamp readField(DataInputStream in) throws IOException + { + return new Timestamp(in.readLong()); + } + + @Override + public Timestamp convertToEntityAttribute(ResultSet resultSet, int column) throws SQLException + { + Timestamp val = resultSet.getTimestamp(column); + return (resultSet.wasNull() ? null : convertToEntityAttribute(val)); + } + + @Override + public Timestamp convertToDatabaseColumn(Timestamp attribute) + { + return attribute; + } + + @Override + public Timestamp convertToEntityAttribute(Timestamp dbData) + { + return dbData; + } +} diff --git a/jpalite-core/src/main/java/io/jpalite/impl/parsers/JPQLParser.java b/jpalite-core/src/main/java/io/jpalite/impl/parsers/JPQLParser.java index fcf9157..1fd2b2f 100644 --- a/jpalite-core/src/main/java/io/jpalite/impl/parsers/JPQLParser.java +++ b/jpalite-core/src/main/java/io/jpalite/impl/parsers/JPQLParser.java @@ -177,12 +177,12 @@ public JPQLParser(String rawQuery, Map queryHints) usingNamedParameters = false; queryParameters = new ArrayList<>(); - if (queryHints.get(JPALiteEntityManager.TRADESWITCH_OVERRIDE_FETCHTYPE) != null) { - overrideAllFetchType = (FetchType) queryHints.get(JPALiteEntityManager.TRADESWITCH_OVERRIDE_FETCHTYPE); + if (queryHints.get(JPALiteEntityManager.PERSISTENCE_OVERRIDE_FETCHTYPE) != null) { + overrideAllFetchType = (FetchType) queryHints.get(JPALiteEntityManager.PERSISTENCE_OVERRIDE_FETCHTYPE); }//if - if (queryHints.get(JPALiteEntityManager.TRADESWITCH_OVERRIDE_BASIC_FETCHTYPE) != null) { - overrideBasicFetchType = (FetchType) queryHints.get(JPALiteEntityManager.TRADESWITCH_OVERRIDE_BASIC_FETCHTYPE); + if (queryHints.get(JPALiteEntityManager.PERSISTENCE_OVERRIDE_BASIC_FETCHTYPE) != null) { + overrideBasicFetchType = (FetchType) queryHints.get(JPALiteEntityManager.PERSISTENCE_OVERRIDE_BASIC_FETCHTYPE); }//if try { diff --git a/jpalite-core/src/main/java/io/jpalite/impl/parsers/QueryParserFactory.java b/jpalite-core/src/main/java/io/jpalite/impl/parsers/QueryParserFactory.java index 72e46aa..c1f5ba7 100644 --- a/jpalite-core/src/main/java/io/jpalite/impl/parsers/QueryParserFactory.java +++ b/jpalite-core/src/main/java/io/jpalite/impl/parsers/QueryParserFactory.java @@ -25,8 +25,8 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import static io.jpalite.JPALiteEntityManager.TRADESWITCH_OVERRIDE_BASIC_FETCHTYPE; -import static io.jpalite.JPALiteEntityManager.TRADESWITCH_OVERRIDE_FETCHTYPE; +import static io.jpalite.JPALiteEntityManager.PERSISTENCE_OVERRIDE_BASIC_FETCHTYPE; +import static io.jpalite.JPALiteEntityManager.PERSISTENCE_OVERRIDE_FETCHTYPE; public class QueryParserFactory { @@ -37,7 +37,7 @@ private QueryParserFactory() } /** - * Factory for query parsers used in TradeSwitch JPA + * Factory for query parsers used in JPA Lite * * @param rawQuery The JQPL query * @param queryHints The query hints @@ -47,8 +47,8 @@ public static QueryParser getParser(QueryLanguage language, String rawQuery, Map /* * If we override the fetching definition on the entity, we need to reparse the query. */ - FetchType overrideFetch = (FetchType) queryHints.get(TRADESWITCH_OVERRIDE_FETCHTYPE); - FetchType overrideBasicFetch = (FetchType) queryHints.get(TRADESWITCH_OVERRIDE_BASIC_FETCHTYPE); + FetchType overrideFetch = (FetchType) queryHints.get(PERSISTENCE_OVERRIDE_FETCHTYPE); + FetchType overrideBasicFetch = (FetchType) queryHints.get(PERSISTENCE_OVERRIDE_BASIC_FETCHTYPE); String cacheKey = rawQuery + language + ((overrideFetch == null) ? "NONE" : overrideFetch) + diff --git a/jpalite-core/src/main/java/io/jpalite/impl/parsers/SQLParser.java b/jpalite-core/src/main/java/io/jpalite/impl/parsers/SQLParser.java index 83cf132..7b8d5fe 100644 --- a/jpalite-core/src/main/java/io/jpalite/impl/parsers/SQLParser.java +++ b/jpalite-core/src/main/java/io/jpalite/impl/parsers/SQLParser.java @@ -27,7 +27,7 @@ import java.util.List; import java.util.Map; -import static io.jpalite.JPALiteEntityManager.TRADESWITCH_ONLY_PRIMARYKEY_USED; +import static io.jpalite.JPALiteEntityManager.PERSISTENCE_PRIMARYKEY_USED; @SuppressWarnings("java:S1452") //generic wildcard is required public class SQLParser implements QueryParser @@ -72,8 +72,8 @@ public SQLParser(String rawQuery, Map queryHints) usingNamedParameters = false; queryParameters = new ArrayList<>(); - if (queryHints.containsKey(TRADESWITCH_ONLY_PRIMARYKEY_USED)) { - selectUsingPrimaryKey = Boolean.parseBoolean(queryHints.get(TRADESWITCH_ONLY_PRIMARYKEY_USED).toString()); + if (queryHints.containsKey(PERSISTENCE_PRIMARYKEY_USED)) { + selectUsingPrimaryKey = Boolean.parseBoolean(queryHints.get(PERSISTENCE_PRIMARYKEY_USED).toString()); }//if query = processSQLParameterLabels(rawQuery); diff --git a/jpalite-core/src/main/java/io/jpalite/impl/providers/JPALiteEntityManagerFactoryImpl.java b/jpalite-core/src/main/java/io/jpalite/impl/providers/JPALiteEntityManagerFactoryImpl.java index 8b72455..157dea3 100644 --- a/jpalite-core/src/main/java/io/jpalite/impl/providers/JPALiteEntityManagerFactoryImpl.java +++ b/jpalite-core/src/main/java/io/jpalite/impl/providers/JPALiteEntityManagerFactoryImpl.java @@ -19,9 +19,9 @@ import io.jpalite.PersistenceContext; import io.jpalite.*; -import io.jpalite.impl.EntityL2CacheImpl; import io.jpalite.impl.JPAConfig; import io.jpalite.impl.JPALiteEntityManagerImpl; +import io.jpalite.impl.caching.EntityCacheImpl; import io.jpalite.impl.db.DatabasePoolFactory; import jakarta.persistence.*; import jakarta.persistence.criteria.CriteriaBuilder; @@ -34,8 +34,8 @@ import java.util.Properties; import java.util.ServiceLoader; -import static io.jpalite.JPALiteEntityManager.JPALITE_SHOW_SQL; import static io.jpalite.JPALiteEntityManager.PERSISTENCE_QUERY_LOG_SLOWTIME; +import static io.jpalite.JPALiteEntityManager.PERSISTENCE_SHOW_SQL; import static io.jpalite.PersistenceContext.PERSISTENCE_JTA_MANAGED; @SuppressWarnings("unchecked") @@ -100,9 +100,8 @@ private JPALitePersistenceUnit getPersistenceUnit() }//if }//for - LOG.warn(String.format("No PersistenceUnit was found for '%s'. %d SPI services found implementing PersistenceUnitProvider.class.", - persistenceUnitName, loader.stream().count())); - return null; + throw new PersistenceUnitNotFoundException(String.format("No PersistenceUnit was found for '%s'. %d SPI services found implementing PersistenceUnitProvider.class.", + persistenceUnitName, loader.stream().count())); }//getPersistenceUnit private PersistenceContext getPersistenceContext(SynchronizationType synchronizationType, Map properties) throws SQLException @@ -118,7 +117,7 @@ private PersistenceContext getPersistenceContext(SynchronizationType synchroniza localProperties.putAll(properties); localProperties.put(PERSISTENCE_JTA_MANAGED, synchronizationType == SynchronizationType.SYNCHRONIZED); localProperties.putIfAbsent(PERSISTENCE_QUERY_LOG_SLOWTIME, defaultSlowQueryTime); - localProperties.putIfAbsent(JPALITE_SHOW_SQL, defaultShowQueries); + localProperties.putIfAbsent(PERSISTENCE_SHOW_SQL, defaultShowQueries); return databasePool.getPersistenceContext(persistenceUnit); }//getPersistenceContext @@ -167,7 +166,7 @@ public Map getProperties() @Override public Cache getCache() { - return new EntityL2CacheImpl(getPersistenceUnit()); + return new EntityCacheImpl(getPersistenceUnit()); }//getCache @Override @@ -181,7 +180,7 @@ private JPAEntity checkEntity(Object entity) return jpaEntity; }//if - throw new IllegalStateException(entity.getClass().getName() + " is not a TradeSwitch Entity"); + throw new IllegalStateException(entity.getClass().getName() + " is not a JPA Entity"); }//checkEntity @Override diff --git a/jpalite-core/src/main/java/io/jpalite/impl/queries/JPALiteQueryImpl.java b/jpalite-core/src/main/java/io/jpalite/impl/queries/JPALiteQueryImpl.java index 6c3e23d..c18fd56 100644 --- a/jpalite-core/src/main/java/io/jpalite/impl/queries/JPALiteQueryImpl.java +++ b/jpalite-core/src/main/java/io/jpalite/impl/queries/JPALiteQueryImpl.java @@ -149,13 +149,13 @@ public class JPALiteQueryImpl implements Query */ public JPALiteQueryImpl(String queryText, QueryLanguage queryLanguage, PersistenceContext persistenceContext, Class resultClass, @Nonnull Map hints, LockModeType lockMode) { - Span span = TRACER.spanBuilder("TradeSwitchQueryImpl::Init").setSpanKind(SpanKind.SERVER).startSpan(); + Span span = TRACER.spanBuilder("JPAQuery::Init").setSpanKind(SpanKind.SERVER).startSpan(); try (Scope ignored = span.makeCurrent()) { if (queryText == null || queryText.isEmpty()) { throw new IllegalArgumentException("No query was specified"); }//if - Boolean globalShowSQL = (Boolean) persistenceContext.getProperties().get(JPALITE_SHOW_SQL); + Boolean globalShowSQL = (Boolean) persistenceContext.getProperties().get(PERSISTENCE_SHOW_SQL); showSql = globalShowSQL != null && globalShowSQL; this.lockMode = lockMode; rawQuery = queryText; @@ -183,12 +183,12 @@ public JPALiteQueryImpl(String queryText, QueryLanguage queryLanguage, Persisten finally { span.end(); } - }//TradeSwitchQueryImpl + }//JpaLiteQueryImpl public JPALiteQueryImpl(String queryText, QueryLanguage queryLanguage, PersistenceContext persistenceContext, Class resultClass, @Nonnull Map hints) { this(queryText, queryLanguage, persistenceContext, resultClass, hints, NONE); - }//TradeSwitchQueryImpl + }//JpaLiteQueryImpl private void checkResultClass(Class returnClass) { @@ -286,12 +286,25 @@ protected Object mapResultSet(ResultSet resultSet) }//if else { if (fieldType == FieldType.TYPE_ENTITY) { + + JPAEntity entity = (JPAEntity) getNewObject(resultClass); if (queryResultTypes.length == 0) { - return persistenceContext.mapResultSet(getNewObject(resultClass), resultSet); + entity._mapResultSet(null, resultSet); }//if else { - return persistenceContext.mapResultSet(getNewObject(resultClass), "c1", resultSet); + entity._mapResultSet("c1", resultSet); }//else + + //Check if the entity is not already in L1 Cache + JPAEntity l1Entity = (JPAEntity) persistenceContext.l1Cache().find(entity.get$$EntityClass(), entity._getPrimaryKey()); + if (l1Entity == null) { + persistenceContext.l1Cache().manage(entity); + }//if + else { + entity = l1Entity; + } + + return entity; }//if else { return getColumnValue(null, resultSet, 1); @@ -385,7 +398,7 @@ private void applyLockTimeout(Statement statement) private Object executeQuery(String sqlQuery, SQLFunction function) { - Span span = TRACER.spanBuilder("TradeSwitchQueryImpl::executeQuery").setSpanKind(SpanKind.SERVER).startSpan(); + Span span = TRACER.spanBuilder("JPAQuery::executeQuery").setSpanKind(SpanKind.SERVER).startSpan(); try (Scope ignored = span.makeCurrent(); Connection connection = persistenceContext.getConnection(getConnectionName()); PreparedStatement vStatement = bindParameters(connection.prepareStatement(sqlQuery))) { @@ -428,7 +441,7 @@ private Object executeQuery(String sqlQuery, SQLFunction func @SuppressWarnings("unchecked") public List getResultList() { - Span span = TRACER.spanBuilder("TradeSwitchQueryImpl::getResultList").setSpanKind(SpanKind.SERVER).startSpan(); + Span span = TRACER.spanBuilder("JPAQuery::getResultList").setSpanKind(SpanKind.SERVER).startSpan(); try (Scope ignored = span.makeCurrent()) { span.setAttribute("resultType", resultClass.getSimpleName()); @@ -508,7 +521,7 @@ private T checkL2Cache(Object primaryKey) if (result instanceof JPAEntity entity) { persistenceContext.l1Cache().manage(entity); - FetchType hintValue = (FetchType) hints.get(TRADESWITCH_OVERRIDE_FETCHTYPE); + FetchType hintValue = (FetchType) hints.get(PERSISTENCE_OVERRIDE_FETCHTYPE); if (hintValue == null || hintValue.equals(FetchType.EAGER)) { entity._lazyFetchAll(hintValue != null); }//if @@ -528,7 +541,7 @@ private T checkL2Cache(Object primaryKey) @SuppressWarnings("unchecked") public T getSingleResult() { - Span span = TRACER.spanBuilder("TradeSwitchQueryImpl::getSingleResult").setSpanKind(SpanKind.SERVER).startSpan(); + Span span = TRACER.spanBuilder("JPAQuery::getSingleResult").setSpanKind(SpanKind.SERVER).startSpan(); try (Scope ignored = span.makeCurrent()) { span.setAttribute("resultType", resultClass.getSimpleName()); @@ -553,21 +566,13 @@ public T getSingleResult() }//if if (result instanceof JPAEntity jpaEntity) { - //Check if the entity is not already in L1 Cache - T l1Entity = (T) persistenceContext.l1Cache().find(jpaEntity.get$$EntityClass(), jpaEntity._getPrimaryKey()); - if (l1Entity != null) { - persistenceContext.l1Cache().detach(jpaEntity); - result = l1Entity; - } - else { - if (jpaEntity._getMetaData().isCacheable() && !bypassL2Cache) { - persistenceContext.l2Cache().add(jpaEntity); - }//if - - if (isPessimisticLocking(lockMode)) { - jpaEntity._setLockMode(lockMode); - }//if - }//else + if (jpaEntity._getMetaData().isCacheable() && !bypassL2Cache) { + persistenceContext.l2Cache().add(jpaEntity); + }//if + + if (isPessimisticLocking(lockMode)) { + jpaEntity._setLockMode(lockMode); + }//if }//if span.setAttribute("result", "Result found"); @@ -587,7 +592,7 @@ public T getSingleResult() @Override public int executeUpdate() { - Span span = TRACER.spanBuilder("TradeSwitchQueryImpl::executeUpdate").setSpanKind(SpanKind.SERVER).startSpan(); + Span span = TRACER.spanBuilder("JPAQuery::executeUpdate").setSpanKind(SpanKind.SERVER).startSpan(); try (Scope ignored = span.makeCurrent()) { span.setAttribute(SQL_QUERY, getQuery()); @@ -678,7 +683,6 @@ else if (value instanceof String aString) { lockTimeout = Integer.parseInt(aString); } } - case TRADESWITCH_CONNECTION_NAME -> connectionName = value.toString(); case PERSISTENCE_CACHE_RETRIEVEMODE -> { if (value instanceof CacheRetrieveMode mode) { bypassL2Cache = CacheRetrieveMode.BYPASS.equals(mode); @@ -687,7 +691,7 @@ else if (value instanceof String aString) { bypassL2Cache = CacheRetrieveMode.BYPASS.equals(CacheRetrieveMode.valueOf(value.toString())); } } - case JPALITE_SHOW_SQL -> { + case PERSISTENCE_SHOW_SQL -> { if (value instanceof Boolean showSqlHint) { this.showSql = showSqlHint; }//if @@ -695,7 +699,7 @@ else if (value instanceof String aString) { showSql = Boolean.parseBoolean(value.toString()); } } - case TRADESWITCH_CACHE_RESULTLIST -> { + case PERSISTENCE_CACHE_RESULTLIST -> { EntityMetaData vMetaData = EntityMetaDataManager.getMetaData(resultClass); if (vMetaData.isCacheable()) { cacheResultList = Boolean.parseBoolean(value.toString()); @@ -704,8 +708,8 @@ else if (value instanceof String aString) { cacheResultList = false; }//else } - case TRADESWITCH_ONLY_PRIMARYKEY_USED -> selectUsingPrimaryKey = true; - case TRADESWITCH_OVERRIDE_BASIC_FETCHTYPE, TRADESWITCH_OVERRIDE_FETCHTYPE -> { + case PERSISTENCE_PRIMARYKEY_USED -> selectUsingPrimaryKey = true; + case PERSISTENCE_OVERRIDE_BASIC_FETCHTYPE, PERSISTENCE_OVERRIDE_FETCHTYPE -> { if (value instanceof FetchType fetchType) { hints.put(hintName, fetchType); }//if @@ -733,7 +737,7 @@ private void processQuery() It is illegal to do a "SELECT FOR UPDATE" query that contains joins. We are forcing the parser to generate a query that do not have any joins. */ - hints.put(TRADESWITCH_OVERRIDE_FETCHTYPE, FetchType.LAZY); + hints.put(PERSISTENCE_OVERRIDE_FETCHTYPE, FetchType.LAZY); }//if try { @@ -773,12 +777,12 @@ be used more than once in the query (which is okay) if (showSql) { LOG.info("\n------------ Query Parser -------------\n" + - "Query language: {}\n" + - "----------- Raw ----------\n" + - "{}\n" + - "---------- Parsed --------\n" + - "{}\n" + - "--------------------------------------", + "Query language: {}\n" + + "----------- Raw ----------\n" + + "{}\n" + + "---------- Parsed --------\n" + + "{}\n" + + "--------------------------------------", queryLanguage, rawQuery, query); }//if }//try diff --git a/jpalite-core/src/main/java/io/jpalite/impl/serializers/JPAEntityMarshaller.java b/jpalite-core/src/main/java/io/jpalite/impl/serializers/JPAEntityMarshaller.java deleted file mode 100644 index 5ccc64c..0000000 --- a/jpalite-core/src/main/java/io/jpalite/impl/serializers/JPAEntityMarshaller.java +++ /dev/null @@ -1,248 +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.impl.serializers; - -import io.jpalite.*; -import org.infinispan.protostream.ProtobufTagMarshaller; -import org.infinispan.protostream.TagReader; -import org.infinispan.protostream.TagWriter; -import org.infinispan.protostream.annotations.impl.GeneratedMarshallerBase; -import org.infinispan.protostream.impl.BaseMarshallerDelegate; -import org.infinispan.protostream.impl.ByteArrayOutputStreamEx; -import org.infinispan.protostream.impl.SerializationContextImpl; -import org.infinispan.protostream.impl.TagWriterImpl; - -import java.io.*; -import java.sql.Timestamp; -import java.time.LocalDateTime; -import java.time.ZoneOffset; - -public class JPAEntityMarshaller extends GeneratedMarshallerBase implements ProtobufTagMarshaller -{ - private final Class entityClass; - - public JPAEntityMarshaller(Class entityClass) - { - this.entityClass = entityClass; - } - - @Override - public Class getJavaClass() - { - return entityClass; - } - - @Override - public String getTypeName() - { - return "org.tradeswitch." + entityClass.getSimpleName(); - } - - @Override - public T read(ReadContext context) throws IOException - { - TagReader reader = context.getReader(); - - try { - EntityMetaData metaData = EntityMetaDataManager.getMetaData(entityClass); - - T entity = (T) metaData.getNewEntity(); - - int tag = reader.readTag(); - for (EntityField field : metaData.getEntityFields()) { - if (tag == 0) { - //if tag is zero we have reached the end - break; - }//if - - int fieldTag; - if (field.getFieldType() == FieldType.TYPE_ENTITY) { - EntityMetaData entityMetaData = EntityMetaDataManager.getMetaData(field.getType()); - if (entityMetaData.getEntityType() == EntityType.ENTITY_EMBEDDABLE) { - fieldTag = field.getFieldType().getWireTypeTag(field.getFieldNr()); - }//if - else { - fieldTag = entityMetaData.getIdField().getFieldType().getWireTypeTag(field.getFieldNr()); - }//else - }//if - else { - fieldTag = field.getFieldType().getWireTypeTag(field.getFieldNr()); - }//else - - if (fieldTag == tag) { - Object value = readField(context, reader, metaData, entity, field); - field.invokeSetter(entity, value); - tag = reader.readTag(); - }//if - }//for - ((JPAEntity) entity)._clearModified(); - - return entity; - }//try - catch (Throwable ex) { - throw new IOException("Error reading Entity", ex); - }//catch - }//read - - @SuppressWarnings("java:S6205")//Block is not redundant - private Object readField(ReadContext context, TagReader reader, EntityMetaData metaData, T entity, EntityField field) throws Throwable - { - return switch (field.getFieldType()) { - case TYPE_BOOLEAN, TYPE_BOOL -> reader.readBool(); - case TYPE_INTEGER, TYPE_INT -> reader.readInt32(); - case TYPE_LONGLONG, TYPE_LONG -> reader.readInt64(); - case TYPE_DOUBLEDOUBLE, TYPE_DOUBLE -> reader.readDouble(); - case TYPE_STRING -> reader.readString(); - case TYPE_ENUM -> { - String enumName = reader.readString(); - for (Object enumVal : field.getType().getEnumConstants()) { - if (((Enum) enumVal).name().equals(enumName)) { - yield enumVal; - }//if - }//for - - yield null; - } - case TYPE_ORDINAL_ENUM -> field.getType().getEnumConstants()[reader.readInt32()]; - case TYPE_TIMESTAMP -> new Timestamp(reader.readFixed64()); - case TYPE_LOCALTIME -> new Timestamp(reader.readFixed64()).toLocalDateTime(); - case TYPE_BYTES -> reader.readByteArray(); - case TYPE_OBJECT -> { - ByteArrayInputStream in = new ByteArrayInputStream(reader.readByteArray()); - ObjectInputStream stream = new ObjectInputStream(in); - yield stream.readObject(); - } - case TYPE_CUSTOMTYPE -> readNestedEntity(context, reader, field.getType()); - case TYPE_ENTITY -> { - if (metaData.getEntityType() == EntityType.ENTITY_EMBEDDABLE) { - yield readNestedEntity(context, reader, field.getType()); - }//if - else { - EntityMetaData subMetaData = EntityMetaDataManager.getMetaData(field.getType()); - Object primaryKey; - //If we have multiple keys then that primary key will be stored in an embedded object - if (metaData.hasMultipleIdFields()) { - primaryKey = readNestedEntity(context, reader, field.getType()); - }//if - else { - primaryKey = readField(context, reader, metaData, entity, subMetaData.getIdField()); - }//else - - JPAEntity sub = (JPAEntity) subMetaData.getNewEntity(); - sub._makeReference(primaryKey); - yield sub; - }//else - } - }; - }//readField - - private Object readNestedEntity(ReadContext context, TagReader reader, Class objectClass) throws IOException - { - BaseMarshallerDelegate delegate = ((SerializationContextImpl) context.getSerializationContext()).getMarshallerDelegate(objectClass); - - int length = reader.readUInt32(); - int oldLimit = reader.pushLimit(length); - - Object nestedObj = readMessage(delegate, context); - - reader.checkLastTagWas(0); - reader.popLimit(oldLimit); - - return nestedObj; - } - - @Override - public void write(WriteContext context, T entity) throws IOException - { - try { - writeEntity(context, context.getWriter(), (JPAEntity) entity); - }//try - catch (Throwable ex) { - throw new IOException("Error writing Entity", ex); - }//catch - }//write - - private void writeNestedEntity(WriteContext context, TagWriter writer, int fieldNr, JPAEntity entity) throws Throwable - { - ByteArrayOutputStreamEx out = new ByteArrayOutputStreamEx(); - TagWriterImpl nestedWriter = TagWriterImpl.newNestedInstance(context, out); - writeEntity(context, nestedWriter, entity); - writer.writeBytes(fieldNr, out.getByteBuffer()); - }//writeNestedEntity - - private void writeField(WriteContext context, TagWriter writer, Class typeClass, FieldType fieldType, int fieldNr, Object value) throws Throwable - { - switch (fieldType) { - case TYPE_BOOLEAN -> writer.writeBool(fieldNr, (Boolean) value); - case TYPE_INTEGER -> writer.writeInt32(fieldNr, (Integer) value); - case TYPE_LONGLONG -> writer.writeInt64(fieldNr, (Long) value); - case TYPE_DOUBLEDOUBLE -> writer.writeDouble(fieldNr, (Double) value); - case TYPE_BOOL -> writer.writeBool(fieldNr, (boolean) value); - case TYPE_INT -> writer.writeInt32(fieldNr, (int) value); - case TYPE_LONG -> writer.writeInt64(fieldNr, (long) value); - case TYPE_DOUBLE -> writer.writeDouble(fieldNr, (double) value); - case TYPE_STRING -> writer.writeString(fieldNr, (String) value); - case TYPE_ENUM -> writer.writeString(fieldNr, ((Enum) value).name()); - case TYPE_ORDINAL_ENUM -> writer.writeInt32(fieldNr, ((Enum) value).ordinal()); - case TYPE_TIMESTAMP -> writer.writeFixed64(fieldNr, ((Timestamp) value).getTime()); - case TYPE_BYTES -> writer.writeBytes(fieldNr, ((byte[]) value)); - case TYPE_LOCALTIME -> - writer.writeFixed64(fieldNr, Timestamp.from(((LocalDateTime) value).toInstant(ZoneOffset.UTC)).getTime()); - case TYPE_OBJECT -> { - ByteArrayOutputStream recvOut = new ByteArrayOutputStream(); - ObjectOutputStream stream = new ObjectOutputStream(recvOut); - stream.writeObject(value); - stream.flush(); - writer.writeBytes(fieldNr, recvOut.toByteArray()); - } - case TYPE_CUSTOMTYPE -> { - @SuppressWarnings("java:S3740")//Can't use generics here - BaseMarshallerDelegate delegate = ((SerializationContextImpl) context.getSerializationContext()).getMarshallerDelegate(typeClass); - writeNestedMessage(delegate, context, fieldNr, value); - } - case TYPE_ENTITY -> { - EntityMetaData metaData = ((JPAEntity) value)._getMetaData(); - - //If it is not an embeddable entity then we only store the primary key - if (metaData.getEntityType() == EntityType.ENTITY_EMBEDDABLE) { - writeNestedEntity(context, writer, fieldNr, (JPAEntity) value); - }//if - else { - //If we have multiple keys then that primary key will be stored in an embedded object - if (metaData.hasMultipleIdFields()) { - writeNestedEntity(context, writer, fieldNr, (JPAEntity) ((JPAEntity) value)._getPrimaryKey()); - }//if - else { - EntityField vKeyField = metaData.getIdField(); - writeField(context, writer, vKeyField.getType(), vKeyField.getFieldType(), fieldNr, ((JPAEntity) value)._getPrimaryKey()); - }//else - }//else - } - }//switch - }//writeField - - private void writeEntity(WriteContext context, TagWriter writer, JPAEntity entity) throws Throwable - { - for (EntityField field : entity._getMetaData().getEntityFields()) { - Object value = field.invokeGetter(entity); - if (value != null) { - writeField(context, writer, field.getType(), field.getFieldType(), field.getFieldNr(), value); - }//if - }//for - }//writeEntity -} diff --git a/jpalite-core/src/main/resources/META-INF/services/io.jpalite.FieldConvertType b/jpalite-core/src/main/resources/META-INF/services/io.jpalite.FieldConvertType new file mode 100644 index 0000000..85a2184 --- /dev/null +++ b/jpalite-core/src/main/resources/META-INF/services/io.jpalite.FieldConvertType @@ -0,0 +1,13 @@ +io.jpalite.impl.fieldtypes.BooleanFieldType +io.jpalite.impl.fieldtypes.IntegerFieldType +io.jpalite.impl.fieldtypes.LongLongFieldType +io.jpalite.impl.fieldtypes.DoubleDoubleFieldType +io.jpalite.impl.fieldtypes.BoolFieldType +io.jpalite.impl.fieldtypes.IntFieldType +io.jpalite.impl.fieldtypes.LongFieldType +io.jpalite.impl.fieldtypes.DoubleFieldType +io.jpalite.impl.fieldtypes.StringFieldType +io.jpalite.impl.fieldtypes.BytesFieldType +io.jpalite.impl.fieldtypes.TimestampFieldType +io.jpalite.impl.fieldtypes.LocalDateTimeFieldType +io.jpalite.impl.fieldtypes.BigDecimalFieldType diff --git a/jpalite-core/src/test/java/io/jpalite/impl/JPAEntityImplTest.java b/jpalite-core/src/test/java/io/jpalite/impl/JPAEntityImplTest.java new file mode 100644 index 0000000..cfb8593 --- /dev/null +++ b/jpalite-core/src/test/java/io/jpalite/impl/JPAEntityImplTest.java @@ -0,0 +1,65 @@ +package io.jpalite.impl; + +import io.jpalite.test.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class JPAEntityImplTest +{ + + @BeforeAll + static void beforeAll() + { + TestEntityMetaDataManager.init(); + } + + @Test + void testToJson() + { + Company c = new Company(); + c.setId(3); + c.setName("Test Company"); + + Department d = new Department(); + d.setId(2); + d.setName("Test Dept"); + d.setCompany(c); + + Employee e = new Employee(); + e.setAge(10); + e.setId(1); + e.setSalary(BigDecimal.valueOf(4000.00).setScale(4, RoundingMode.HALF_DOWN)); + e.setFullName(new FullName("Test", "Employee")); + e.setDepartment(d); + + Phone p1 = new Phone(); + p1.setId(4); + p1.setNumber("1234567890"); + p1.setEmployee(e); + + Phone p2 = new Phone(); + p2.setId(5); + p2.setNumber("0987654321"); + p2.setEmployee(e); + + e.setPhones(List.of(p1, p2)); + + String json = e._toJson(); + + Employee e2 = new Employee(); + e2._fromJson(json); + + assertEquals(e.getId(), e2.getId()); + assertEquals(e.getAge(), e2.getAge()); + assertEquals(e.getFullName().getName(), e2.getFullName().getName()); + assertEquals(e.getFullName().getSurname(), e2.getFullName().getSurname()); + assertEquals(e.getDepartment().getId(), e2.getDepartment().getId()); + assertEquals(e.getSalary(), e2.getSalary()); + } +} diff --git a/jpalite-core/src/test/java/io/jpalite/jqpl/JPQLParserTest.java b/jpalite-core/src/test/java/io/jpalite/jqpl/JPQLParserTest.java index 63b3154..9a0fb69 100644 --- a/jpalite-core/src/test/java/io/jpalite/jqpl/JPQLParserTest.java +++ b/jpalite-core/src/test/java/io/jpalite/jqpl/JPQLParserTest.java @@ -17,36 +17,28 @@ package io.jpalite.jqpl; +import io.jpalite.EntityMetaData; +import io.jpalite.EntityMetaDataManager; import io.jpalite.JPALiteEntityManager; +import io.jpalite.impl.parsers.JPQLParser; +import io.jpalite.test.*; import jakarta.persistence.FetchType; import net.sf.jsqlparser.JSQLParserException; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import io.jpalite.EntityMetaData; -import io.jpalite.EntityMetaDataManager; -import io.jpalite.impl.EntityMetaDataImpl; -import io.jpalite.impl.parsers.JPQLParser; import java.util.HashMap; import java.util.Map; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.*; class JPQLParserTest { - static { - // This is a hack to get the JPA to work - EntityMetaDataManager.register(new EntityMetaDataImpl<>(RatePlan.class)); - EntityMetaDataManager.register(new EntityMetaDataImpl<>(FullName.class)); - EntityMetaDataManager.register(new EntityMetaDataImpl<>(Employee.class)); - EntityMetaDataManager.register(new EntityMetaDataImpl<>(Department.class)); - EntityMetaDataManager.register(new EntityMetaDataImpl<>(Company.class)); - EntityMetaDataManager.register(new EntityMetaDataImpl<>(Phone.class)); - - EntityMetaDataManager.register(new EntityMetaDataImpl<>(Employee1.class)); - EntityMetaDataManager.register(new EntityMetaDataImpl<>(Department1.class)); + @BeforeAll + static void beforeAll() + { + TestEntityMetaDataManager.init(); } @Test @@ -54,10 +46,10 @@ void testUsingSelectIn() { JPQLParser vParser = new JPQLParser("select RatePlan from RatePlan where (uid, resourceVersion) in (select e.uid, max(e.resourceVersion) from RatePlan e group by e.uid)", new HashMap<>()); Assertions.assertEquals("SELECT t1.ID \"c1-1\", t1.UID \"c1-2\", t1.RESOURCE_VERSION \"c1-3\", t1.OPERATOR_ID \"c1-4\", t1.PLAN_NAME \"c1-5\", t1.CREATED_BY \"c1-6\", t1.APPROVED_BY \"c1-7\", t1.EFFECTIVE_DATE \"c1-8\", " + - "t1.RATE_PLAN_CONFIG \"c1-9\", t1.MODIFIED_ON \"c1-10\", t1.CREATED_DATE \"c1-11\" " + - "FROM RATE_PLAN t1 " + - "WHERE (t1.UID, t1.RESOURCE_VERSION) IN (SELECT t2.UID \"c1\", max(t2.RESOURCE_VERSION) \"c2\" " + - "FROM RATE_PLAN t2 GROUP BY t2.UID)", + "t1.RATE_PLAN_CONFIG \"c1-9\", t1.MODIFIED_ON \"c1-10\", t1.CREATED_DATE \"c1-11\" " + + "FROM RATE_PLAN t1 " + + "WHERE (t1.UID, t1.RESOURCE_VERSION) IN (SELECT t2.UID \"c1\", max(t2.RESOURCE_VERSION) \"c2\" " + + "FROM RATE_PLAN t2 GROUP BY t2.UID)", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(RatePlan.class, vParser.getReturnTypes().get(0)); @@ -68,33 +60,33 @@ void testUsingBitAndOperators() throws JSQLParserException { JPQLParser vParser = new JPQLParser("select E from Employee E where bitand(E.department.id, :flag) = :flag", new HashMap<>()); Assertions.assertEquals("SELECT t1.IRN \"c1-1\", t1.NAME \"c1-2-1\", t1.SURNAME \"c1-2-2\", t1.AGE \"c1-3\", " + - "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + - "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + - "FROM EMPLOYEE t1 " + - "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + - "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + - "WHERE bitand(t2.IRN, ?) = ?", + "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + + "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + + "FROM EMPLOYEE t1 " + + "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + + "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + + "WHERE bitand(t2.IRN, ?) = ?", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Employee.class, vParser.getReturnTypes().get(0)); vParser = new JPQLParser("select Employee from Employee where bitand(department.id, :flag) = :flag", new HashMap<>()); Assertions.assertEquals("SELECT t1.IRN \"c1-1\", t1.NAME \"c1-2-1\", t1.SURNAME \"c1-2-2\", t1.AGE \"c1-3\", " + - "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + - "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + - "FROM EMPLOYEE t1 " + - "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + - "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + - "WHERE bitand(t2.IRN, ?) = ?", + "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + + "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + + "FROM EMPLOYEE t1 " + + "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + + "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + + "WHERE bitand(t2.IRN, ?) = ?", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Employee.class, vParser.getReturnTypes().get(0)); vParser = new JPQLParser("select Employee1 from Employee1 where bitand(department.id, :flag) = :flag", new HashMap<>()); Assertions.assertEquals("SELECT t1.IRN \"c1-1\", t1.NAME \"c1-2\", t1.AGE \"c1-3\", t1.DEPT \"c1-5\" " + - "FROM EMPLOYEE t1 " + - "LEFT JOIN DEPT t2 ON t1.DEPT = t2.IRN " + - "WHERE bitand(t2.IRN, ?) = ?", + "FROM EMPLOYEE t1 " + + "LEFT JOIN DEPT t2 ON t1.DEPT = t2.IRN " + + "WHERE bitand(t2.IRN, ?) = ?", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Employee1.class, vParser.getReturnTypes().get(0)); @@ -105,24 +97,24 @@ void whenUsingBracketsInWhereClauses() throws JSQLParserException { JPQLParser vParser = new JPQLParser("select E from Employee E where (E.department.id = :val)", new HashMap<>()); Assertions.assertEquals("SELECT t1.IRN \"c1-1\", t1.NAME \"c1-2-1\", t1.SURNAME \"c1-2-2\", t1.AGE \"c1-3\", " + - "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + - "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + - "FROM EMPLOYEE t1 " + - "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + - "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + - "WHERE (t2.IRN = ?)", + "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + + "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + + "FROM EMPLOYEE t1 " + + "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + + "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + + "WHERE (t2.IRN = ?)", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Employee.class, vParser.getReturnTypes().get(0)); vParser = new JPQLParser("select Employee from Employee where (department.id = :val)", new HashMap<>()); Assertions.assertEquals("SELECT t1.IRN \"c1-1\", t1.NAME \"c1-2-1\", t1.SURNAME \"c1-2-2\", t1.AGE \"c1-3\", " + - "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + - "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + - "FROM EMPLOYEE t1 " + - "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + - "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + - "WHERE (t2.IRN = ?)", + "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + + "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + + "FROM EMPLOYEE t1 " + + "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + + "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + + "WHERE (t2.IRN = ?)", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Employee.class, vParser.getReturnTypes().get(0)); @@ -133,16 +125,16 @@ void whenPathExpressionIsUsedForSingleValuedAssociation_thenCreatesImplicitInner { JPQLParser vParser = new JPQLParser("SELECT e FROM Employee1 e", new HashMap<>()); Assertions.assertEquals("SELECT t1.IRN \"c1-1\", t1.NAME \"c1-2\", t1.AGE \"c1-3\", t1.DEPT \"c1-5\" " + - "FROM EMPLOYEE t1", + "FROM EMPLOYEE t1", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Employee1.class, vParser.getReturnTypes().get(0)); vParser = new JPQLParser("SELECT e FROM Employee1 e JOIN e.department", new HashMap<>()); Assertions.assertEquals("SELECT t1.IRN \"c1-1\", t1.NAME \"c1-2\", t1.AGE \"c1-3\", " + - "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", t2.COMP \"c1-5-3\" " + - "FROM EMPLOYEE t1 " + - "LEFT JOIN DEPT t2 ON t1.DEPT = t2.IRN", + "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", t2.COMP \"c1-5-3\" " + + "FROM EMPLOYEE t1 " + + "LEFT JOIN DEPT t2 ON t1.DEPT = t2.IRN", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Employee1.class, vParser.getReturnTypes().get(0)); @@ -152,34 +144,34 @@ void whenPathExpressionIsUsedForSingleValuedAssociation_thenCreatesImplicitInner void whenOverridingFetchTypes_thenGenerateQueryCorrect() throws JSQLParserException { Map vHints = new HashMap<>(); - vHints.put(JPALiteEntityManager.TRADESWITCH_OVERRIDE_BASIC_FETCHTYPE, FetchType.EAGER); + vHints.put(JPALiteEntityManager.PERSISTENCE_OVERRIDE_BASIC_FETCHTYPE, FetchType.EAGER); JPQLParser vParser = new JPQLParser("SELECT e FROM Employee e", vHints); Assertions.assertEquals("SELECT t1.IRN \"c1-1\", t1.NAME \"c1-2-1\", t1.SURNAME \"c1-2-2\", t1.AGE \"c1-3\", t1.SALARY \"c1-4\", " + - "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + - "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + - "FROM EMPLOYEE t1 " + - "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + - "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN", + "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + + "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + + "FROM EMPLOYEE t1 " + + "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + + "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Employee.class, vParser.getReturnTypes().get(0)); vHints = new HashMap<>(); - vHints.put(JPALiteEntityManager.TRADESWITCH_OVERRIDE_FETCHTYPE, FetchType.LAZY); + vHints.put(JPALiteEntityManager.PERSISTENCE_OVERRIDE_FETCHTYPE, FetchType.LAZY); vParser = new JPQLParser("SELECT e FROM Employee e", vHints); Assertions.assertEquals("SELECT t1.IRN \"c1-1\", t1.NAME \"c1-2-1\", t1.SURNAME \"c1-2-2\", t1.AGE \"c1-3\", t1.DEPT \"c1-5\" " + - "FROM EMPLOYEE t1", + "FROM EMPLOYEE t1", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Employee.class, vParser.getReturnTypes().get(0)); vHints = new HashMap<>(); - vHints.put(JPALiteEntityManager.TRADESWITCH_OVERRIDE_FETCHTYPE, FetchType.LAZY); - vHints.put(JPALiteEntityManager.TRADESWITCH_OVERRIDE_BASIC_FETCHTYPE, FetchType.EAGER); + vHints.put(JPALiteEntityManager.PERSISTENCE_OVERRIDE_FETCHTYPE, FetchType.LAZY); + vHints.put(JPALiteEntityManager.PERSISTENCE_OVERRIDE_BASIC_FETCHTYPE, FetchType.EAGER); vParser = new JPQLParser("SELECT e FROM Employee e", vHints); Assertions.assertEquals("SELECT t1.IRN \"c1-1\", t1.NAME \"c1-2-1\", t1.SURNAME \"c1-2-2\", t1.AGE \"c1-3\", " + - "t1.SALARY \"c1-4\", t1.DEPT \"c1-5\" " + - "FROM EMPLOYEE t1", + "t1.SALARY \"c1-4\", t1.DEPT \"c1-5\" " + + "FROM EMPLOYEE t1", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Employee.class, vParser.getReturnTypes().get(0)); @@ -190,43 +182,43 @@ void whenPathExpressionIsUsedForSingleValuedAssociation_thenCreatesImplicitInner { JPQLParser vParser = new JPQLParser("SELECT e FROM Employee e", new HashMap<>()); Assertions.assertEquals("SELECT t1.IRN \"c1-1\", t1.NAME \"c1-2-1\", t1.SURNAME \"c1-2-2\", t1.AGE \"c1-3\", " + - "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + - "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + - "FROM EMPLOYEE t1 " + - "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + - "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN", + "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + + "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + + "FROM EMPLOYEE t1 " + + "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + + "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Employee.class, vParser.getReturnTypes().get(0)); vParser = new JPQLParser("SELECT Employee FROM Employee", new HashMap<>()); Assertions.assertEquals("SELECT t1.IRN \"c1-1\", t1.NAME \"c1-2-1\", t1.SURNAME \"c1-2-2\", t1.AGE \"c1-3\", " + - "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + - "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + - "FROM EMPLOYEE t1 " + - "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + - "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN", + "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + + "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + + "FROM EMPLOYEE t1 " + + "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + + "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Employee.class, vParser.getReturnTypes().get(0)); vParser = new JPQLParser("SELECT e FROM Employee e JOIN e.department", new HashMap<>()); Assertions.assertEquals("SELECT t1.IRN \"c1-1\", t1.NAME \"c1-2-1\", t1.SURNAME \"c1-2-2\", t1.AGE \"c1-3\", " + - "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + - "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + - "FROM EMPLOYEE t1 " + - "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + - "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN", + "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + + "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + + "FROM EMPLOYEE t1 " + + "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + + "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Employee.class, vParser.getReturnTypes().get(0)); vParser = new JPQLParser("select e.department.id, e.department.company from Employee e", new HashMap<>()); Assertions.assertEquals("SELECT t2.IRN \"c1\", " + - "t3.IRN \"c2-1\", t3.NAME \"c2-2\" " + - "FROM EMPLOYEE t1 " + - "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + - "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN", + "t3.IRN \"c2-1\", t3.NAME \"c2-2\" " + + "FROM EMPLOYEE t1 " + + "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + + "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN", vParser.getQuery()); Assertions.assertEquals(2, vParser.getReturnTypes().size()); Assertions.assertEquals(Integer.class, vParser.getReturnTypes().get(0)); @@ -234,8 +226,8 @@ void whenPathExpressionIsUsedForSingleValuedAssociation_thenCreatesImplicitInner vParser = new JPQLParser("select e.fullName.name, e.department.name, e.department.id from Employee e", new HashMap<>()); Assertions.assertEquals("SELECT t1.NAME \"c1\", t2.NAME \"c2\", t2.IRN \"c3\" " + - "FROM EMPLOYEE t1 " + - "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN", + "FROM EMPLOYEE t1 " + + "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN", vParser.getQuery()); Assertions.assertEquals(3, vParser.getReturnTypes().size()); Assertions.assertEquals(String.class, vParser.getReturnTypes().get(0)); @@ -244,10 +236,10 @@ void whenPathExpressionIsUsedForSingleValuedAssociation_thenCreatesImplicitInner vParser = new JPQLParser("select e.fullName, e.department from Employee e", new HashMap<>()); Assertions.assertEquals("SELECT t1.NAME \"c1-1\", t1.SURNAME \"c1-2\", t2.IRN \"c2-1\", t2.NAME \"c2-2\", " + - "t3.IRN \"c2-3-1\", t3.NAME \"c2-3-2\" " + - "FROM EMPLOYEE t1 " + - "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + - "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN", + "t3.IRN \"c2-3-1\", t3.NAME \"c2-3-2\" " + + "FROM EMPLOYEE t1 " + + "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + + "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN", vParser.getQuery()); Assertions.assertEquals(2, vParser.getReturnTypes().size()); Assertions.assertEquals(FullName.class, vParser.getReturnTypes().get(0)); @@ -265,10 +257,10 @@ void whenJoinKeywordIsUsed_thenCreatesExplicitInnerJoin() throws JSQLParserExcep vParser = new JPQLParser("select count(Employee) from Employee where department.company=:id", new HashMap<>()); Assertions.assertEquals("SELECT count(t1.IRN) \"c1\" " + - "FROM EMPLOYEE t1 " + - "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + - "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + - "WHERE t3.IRN = ?", + "FROM EMPLOYEE t1 " + + "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + + "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + + "WHERE t3.IRN = ?", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Object.class, vParser.getReturnTypes().get(0)); @@ -283,12 +275,12 @@ void whenWhereINIsUsed() throws JSQLParserException JPQLParser vParser = new JPQLParser("SELECT e FROM Employee e where e.age in (11,22,33)", new HashMap<>()); Assertions.assertEquals("SELECT t1.IRN \"c1-1\", t1.NAME \"c1-2-1\", t1.SURNAME \"c1-2-2\", t1.AGE \"c1-3\", " + - "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + - "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + - "FROM EMPLOYEE t1 " + - "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + - "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + - "WHERE t1.AGE IN (11, 22, 33)", + "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + + "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + + "FROM EMPLOYEE t1 " + + "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + + "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + + "WHERE t1.AGE IN (11, 22, 33)", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Employee.class, vParser.getReturnTypes().get(0)); @@ -296,12 +288,12 @@ void whenWhereINIsUsed() throws JSQLParserException vParser = new JPQLParser("SELECT e FROM Employee e where e.age in (select e1.age from Employee1 e1)", new HashMap<>()); Assertions.assertEquals("SELECT t1.IRN \"c1-1\", t1.NAME \"c1-2-1\", t1.SURNAME \"c1-2-2\", t1.AGE \"c1-3\", " + - "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + - "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + - "FROM EMPLOYEE t1 " + - "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + - "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + - "WHERE t1.AGE IN (SELECT t4.AGE \"c1\" FROM EMPLOYEE t4)", + "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + + "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + + "FROM EMPLOYEE t1 " + + "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + + "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + + "WHERE t1.AGE IN (SELECT t4.AGE \"c1\" FROM EMPLOYEE t4)", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Employee.class, vParser.getReturnTypes().get(0)); @@ -314,12 +306,12 @@ void whenIsNullIsUsed() throws JSQLParserException JPQLParser vParser = new JPQLParser("SELECT e FROM Employee e where e.fullName.name is null", new HashMap<>()); Assertions.assertEquals("SELECT t1.IRN \"c1-1\", t1.NAME \"c1-2-1\", t1.SURNAME \"c1-2-2\", t1.AGE \"c1-3\", " + - "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + - "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + - "FROM EMPLOYEE t1 " + - "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + - "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + - "WHERE t1.NAME IS NULL", + "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + + "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + + "FROM EMPLOYEE t1 " + + "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + + "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + + "WHERE t1.NAME IS NULL", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Employee.class, vParser.getReturnTypes().get(0)); @@ -327,12 +319,12 @@ void whenIsNullIsUsed() throws JSQLParserException vParser = new JPQLParser("SELECT e FROM Employee e where e.fullName.name is not null", new HashMap<>()); Assertions.assertEquals("SELECT t1.IRN \"c1-1\", t1.NAME \"c1-2-1\", t1.SURNAME \"c1-2-2\", t1.AGE \"c1-3\", " + - "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + - "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + - "FROM EMPLOYEE t1 " + - "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + - "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + - "WHERE t1.NAME IS NOT NULL", + "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + + "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + + "FROM EMPLOYEE t1 " + + "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + + "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + + "WHERE t1.NAME IS NOT NULL", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Employee.class, vParser.getReturnTypes().get(0)); @@ -344,10 +336,10 @@ void whenExistIsUsed_thenCreatesExplicitInnerJoin() throws JSQLParserException JPQLParser vParser = new JPQLParser("SELECT d FROM Employee e JOIN e.department d", new HashMap<>()); Assertions.assertEquals("SELECT t2.IRN \"c1-1\", t2.NAME \"c1-2\", " + - "t3.IRN \"c1-3-1\", t3.NAME \"c1-3-2\" " + - "FROM EMPLOYEE t1 " + - "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + - "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN", + "t3.IRN \"c1-3-1\", t3.NAME \"c1-3-2\" " + + "FROM EMPLOYEE t1 " + + "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + + "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Department.class, vParser.getReturnTypes().get(0)); @@ -359,10 +351,10 @@ void whenLeftKeywordIsSpecified_thenCreatesOuterJoinAndIncludesNonMatched() thro JPQLParser vParser = new JPQLParser("SELECT DISTINCT d FROM Department d LEFT JOIN d.employees e", new HashMap<>()); Assertions.assertEquals("SELECT DISTINCT t1.IRN \"c1-1\", t1.NAME \"c1-2\", " + - "t3.IRN \"c1-3-1\", t3.NAME \"c1-3-2\" " + - "FROM DEPT t1 " + - "LEFT JOIN EMPLOYEE t2 ON t1.IRN = t2.DEPT " + - "INNER JOIN COMPANY t3 ON t1.COMP = t3.IRN", + "t3.IRN \"c1-3-1\", t3.NAME \"c1-3-2\" " + + "FROM DEPT t1 " + + "LEFT JOIN EMPLOYEE t2 ON t1.IRN = t2.DEPT " + + "INNER JOIN COMPANY t3 ON t1.COMP = t3.IRN", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Department.class, vParser.getReturnTypes().get(0)); @@ -379,10 +371,10 @@ void whenEntitiesAreListedInFrom_ThenCreatesCartesianProduct() throws JSQLParser vMetaData.getEntityField("company").setFetchType(FetchType.EAGER); Assertions.assertEquals("SELECT t1.IRN \"c1-1\", t1.NAME \"c1-2-1\", t1.SURNAME \"c1-2-2\", t1.AGE \"c1-3\", " + - "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + - "t2.COMP \"c1-5-3\", " + - "t2.IRN \"c2-1\", t2.NAME \"c2-2\", t2.COMP \"c2-3\" " + - "FROM EMPLOYEE t1, DEPT t2", + "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + + "t2.COMP \"c1-5-3\", " + + "t2.IRN \"c2-1\", t2.NAME \"c2-2\", t2.COMP \"c2-3\" " + + "FROM EMPLOYEE t1, DEPT t2", vParser.getQuery()); Assertions.assertEquals(2, vParser.getReturnTypes().size()); Assertions.assertEquals(Employee.class, vParser.getReturnTypes().get(0)); @@ -395,10 +387,10 @@ void whenEntitiesAreListedInFromAndMatchedInWhere_ThenCreatesJoin() throws JSQLP JPQLParser vParser = new JPQLParser("SELECT d FROM Employee e, Department d WHERE e.department = d", new HashMap<>()); Assertions.assertEquals("SELECT t2.IRN \"c1-1\", t2.NAME \"c1-2\", " + - "t3.IRN \"c1-3-1\", t3.NAME \"c1-3-2\" " + - "FROM EMPLOYEE t1, DEPT t2 " + - "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + - "WHERE t1.DEPT = t2.IRN", + "t3.IRN \"c1-3-1\", t3.NAME \"c1-3-2\" " + + "FROM EMPLOYEE t1, DEPT t2 " + + "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + + "WHERE t1.DEPT = t2.IRN", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Department.class, vParser.getReturnTypes().get(0)); @@ -411,12 +403,12 @@ void whenEntitiesAreOrderedByFK() throws JSQLParserException JPQLParser vParser = new JPQLParser("SELECT Employee FROM Employee Order by department.name asc", new HashMap<>()); Assertions.assertEquals("SELECT t1.IRN \"c1-1\", t1.NAME \"c1-2-1\", t1.SURNAME \"c1-2-2\", t1.AGE \"c1-3\", " + - "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + - "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + - "FROM EMPLOYEE t1 " + - "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + - "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + - "ORDER BY t2.NAME ASC", vParser.getQuery()); + "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + + "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + + "FROM EMPLOYEE t1 " + + "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + + "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + + "ORDER BY t2.NAME ASC", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Employee.class, vParser.getReturnTypes().get(0)); } @@ -427,13 +419,13 @@ void whenCollectionValuedAssociationIsJoined_ThenCanSelect() throws JSQLParserEx JPQLParser vParser = new JPQLParser("SELECT e FROM Employee e JOIN e.phones ph WHERE ph LIKE '1%'", new HashMap<>()); Assertions.assertEquals("SELECT t1.IRN \"c1-1\", t1.NAME \"c1-2-1\", t1.SURNAME \"c1-2-2\", t1.AGE \"c1-3\", " + - "t3.IRN \"c1-5-1\", t3.NAME \"c1-5-2\", " + - "t4.IRN \"c1-5-3-1\", t4.NAME \"c1-5-3-2\" " + - "FROM EMPLOYEE t1 " + - "JOIN PHONE t2 ON t1.IRN = t2.EMPL " + - "INNER JOIN DEPT t3 ON t1.DEPT = t3.IRN " + - "INNER JOIN COMPANY t4 ON t3.COMP = t4.IRN " + - "WHERE t2.IRN LIKE '1%'", + "t3.IRN \"c1-5-1\", t3.NAME \"c1-5-2\", " + + "t4.IRN \"c1-5-3-1\", t4.NAME \"c1-5-3-2\" " + + "FROM EMPLOYEE t1 " + + "JOIN PHONE t2 ON t1.IRN = t2.EMPL " + + "INNER JOIN DEPT t3 ON t1.DEPT = t3.IRN " + + "INNER JOIN COMPANY t4 ON t3.COMP = t4.IRN " + + "WHERE t2.IRN LIKE '1%'", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); @@ -444,18 +436,18 @@ void whenCollectionValuedAssociationIsJoined_ThenCanSelect() throws JSQLParserEx void whenMultipleEntitiesAreListedWithJoin_ThenCreatesMultipleJoins() throws JSQLParserException { JPQLParser vParser = new JPQLParser("SELECT ph FROM Employee e " + - "JOIN e.department d " + - "JOIN e.phones ph " + - "WHERE d.name IS NOT NULL", new HashMap<>()); + "JOIN e.department d " + + "JOIN e.phones ph " + + "WHERE d.name IS NOT NULL", new HashMap<>()); Assertions.assertEquals("SELECT t3.IRN \"c1-1\", t3.NUM \"c1-2\", " + - "t1.IRN \"c1-3-1\", t1.NAME \"c1-3-2-1\", t1.SURNAME \"c1-3-2-2\", t1.AGE \"c1-3-3\", " + - "t2.IRN \"c1-3-5-1\", t2.NAME \"c1-3-5-2\", " + - "t4.IRN \"c1-3-5-3-1\", t4.NAME \"c1-3-5-3-2\" " + - "FROM EMPLOYEE t1 " + - "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + - "JOIN PHONE t3 ON t1.IRN = t3.EMPL " + - "INNER JOIN COMPANY t4 ON t2.COMP = t4.IRN " + - "WHERE t2.NAME IS NOT NULL", + "t1.IRN \"c1-3-1\", t1.NAME \"c1-3-2-1\", t1.SURNAME \"c1-3-2-2\", t1.AGE \"c1-3-3\", " + + "t2.IRN \"c1-3-5-1\", t2.NAME \"c1-3-5-2\", " + + "t4.IRN \"c1-3-5-3-1\", t4.NAME \"c1-3-5-3-2\" " + + "FROM EMPLOYEE t1 " + + "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + + "JOIN PHONE t3 ON t1.IRN = t3.EMPL " + + "INNER JOIN COMPANY t4 ON t2.COMP = t4.IRN " + + "WHERE t2.NAME IS NOT NULL", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Phone.class, vParser.getReturnTypes().get(0)); @@ -465,14 +457,14 @@ void whenMultipleEntitiesAreListedWithJoin_ThenCreatesMultipleJoins() throws JSQ void whenGroupByOrderBy_ThenCreatesJoins() throws JSQLParserException { JPQLParser vParser = new JPQLParser("SELECT e.fullName.name, count(ph.id) FROM Employee e " + - "JOIN e.phones ph " + - "group by e.fullName.name " + - "order by e.fullName.name, ph", new HashMap<>()); + "JOIN e.phones ph " + + "group by e.fullName.name " + + "order by e.fullName.name, ph", new HashMap<>()); Assertions.assertEquals("SELECT t1.NAME \"c1\", count(t2.IRN) \"c2\" " + - "FROM EMPLOYEE t1 " + - "JOIN PHONE t2 ON t1.IRN = t2.EMPL " + - "GROUP BY t1.NAME " + - "ORDER BY t1.NAME, t2.IRN", + "FROM EMPLOYEE t1 " + + "JOIN PHONE t2 ON t1.IRN = t2.EMPL " + + "GROUP BY t1.NAME " + + "ORDER BY t1.NAME, t2.IRN", vParser.getQuery()); Assertions.assertEquals(2, vParser.getReturnTypes().size()); Assertions.assertEquals(String.class, vParser.getReturnTypes().get(0)); @@ -483,14 +475,14 @@ void whenGroupByOrderBy_ThenCreatesJoins() throws JSQLParserException void whenGroupByHaving_ThenCreatesJoins() throws JSQLParserException { JPQLParser vParser = new JPQLParser("SELECT e.fullName.name, count(ph.id),:p1 FROM Employee e " + - "JOIN e.phones ph " + - "group by e.fullName.name " + - "having count(ph.id) > 1", new HashMap<>()); + "JOIN e.phones ph " + + "group by e.fullName.name " + + "having count(ph.id) > 1", new HashMap<>()); Assertions.assertEquals("SELECT t1.NAME \"c1\", count(t2.IRN) \"c2\", ? \"c3\" " + - "FROM EMPLOYEE t1 " + - "JOIN PHONE t2 ON t1.IRN = t2.EMPL " + - "GROUP BY t1.NAME " + - "HAVING count(t2.IRN) > 1", + "FROM EMPLOYEE t1 " + + "JOIN PHONE t2 ON t1.IRN = t2.EMPL " + + "GROUP BY t1.NAME " + + "HAVING count(t2.IRN) > 1", vParser.getQuery()); assertDoesNotThrow(() -> vParser.checkType(Object[].class)); assertThrows(IllegalArgumentException.class, () -> vParser.checkType(Employee.class)); @@ -500,14 +492,14 @@ void whenGroupByHaving_ThenCreatesJoins() throws JSQLParserException void whenWhereByEntity_ThenCreateWhereOnPK() throws JSQLParserException { JPQLParser vParser = new JPQLParser("SELECT e FROM Employee e " + - "where e.fullName=:FullName", new HashMap<>()); + "where e.fullName=:FullName", new HashMap<>()); Assertions.assertEquals("SELECT t1.IRN \"c1-1\", t1.NAME \"c1-2-1\", t1.SURNAME \"c1-2-2\", t1.AGE \"c1-3\", " + - "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + - "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + - "FROM EMPLOYEE t1 " + - "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + - "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + - "WHERE (t1.NAME, t1.SURNAME) = (?, ?)", + "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + + "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + + "FROM EMPLOYEE t1 " + + "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + + "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + + "WHERE (t1.NAME, t1.SURNAME) = (?, ?)", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Employee.class, vParser.getReturnTypes().get(0)); @@ -515,15 +507,15 @@ void whenWhereByEntity_ThenCreateWhereOnPK() throws JSQLParserException Assertions.assertEquals(FullName.class, vParser.getQueryParameters().get(0).getParameterType()); vParser = new JPQLParser("SELECT Employee FROM Employee " + - "where fullName=:FullName and age>:age", new HashMap<>()); + "where fullName=:FullName and age>:age", new HashMap<>()); Assertions.assertEquals("SELECT t1.IRN \"c1-1\", t1.NAME \"c1-2-1\", t1.SURNAME \"c1-2-2\", t1.AGE \"c1-3\", " + - "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + - "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + - "FROM EMPLOYEE t1 " + - "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + - "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + - "WHERE (t1.NAME, t1.SURNAME) = (?, ?) " + - "AND t1.AGE > ?", + "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + + "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + + "FROM EMPLOYEE t1 " + + "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + + "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + + "WHERE (t1.NAME, t1.SURNAME) = (?, ?) " + + "AND t1.AGE > ?", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Employee.class, vParser.getReturnTypes().get(0)); @@ -532,12 +524,12 @@ void whenWhereByEntity_ThenCreateWhereOnPK() throws JSQLParserException vParser = new JPQLParser("SELECT e FROM Employee e where e=?1", new HashMap<>()); Assertions.assertEquals("SELECT t1.IRN \"c1-1\", t1.NAME \"c1-2-1\", t1.SURNAME \"c1-2-2\", t1.AGE \"c1-3\", " + - "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + - "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + - "FROM EMPLOYEE t1 " + - "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + - "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + - "WHERE t1.IRN = ?", + "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + + "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + + "FROM EMPLOYEE t1 " + + "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + + "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + + "WHERE t1.IRN = ?", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Employee.class, vParser.getReturnTypes().get(0)); @@ -547,12 +539,12 @@ void whenWhereByEntity_ThenCreateWhereOnPK() throws JSQLParserException vParser = new JPQLParser("SELECT e FROM Employee e where e.department.company=?1", new HashMap<>()); Assertions.assertEquals("SELECT t1.IRN \"c1-1\", t1.NAME \"c1-2-1\", t1.SURNAME \"c1-2-2\", t1.AGE \"c1-3\", " + - "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + - "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + - "FROM EMPLOYEE t1 " + - "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + - "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + - "WHERE t3.IRN = ?", + "t2.IRN \"c1-5-1\", t2.NAME \"c1-5-2\", " + + "t3.IRN \"c1-5-3-1\", t3.NAME \"c1-5-3-2\" " + + "FROM EMPLOYEE t1 " + + "INNER JOIN DEPT t2 ON t1.DEPT = t2.IRN " + + "INNER JOIN COMPANY t3 ON t2.COMP = t3.IRN " + + "WHERE t3.IRN = ?", vParser.getQuery()); Assertions.assertEquals(1, vParser.getReturnTypes().size()); Assertions.assertEquals(Employee.class, vParser.getReturnTypes().get(0)); @@ -577,22 +569,22 @@ void deleteEntityUsingPK() throws JSQLParserException void updateEntityUsingPK() throws JSQLParserException { JPQLParser vParser = new JPQLParser("UPDATE Employee e set e.salary=e.salary*1.05 " + - "where e.id=:id", new HashMap<>()); + "where e.id=:id", new HashMap<>()); Assertions.assertEquals("UPDATE EMPLOYEE t1 SET t1.SALARY = t1.SALARY * 1.05 WHERE t1.IRN = ?", vParser.getQuery()); vParser = new JPQLParser("UPDATE Employee set salary=salary*1.05 " + - "where id=:id", new HashMap<>()); + "where id=:id", new HashMap<>()); Assertions.assertEquals("UPDATE EMPLOYEE t1 SET t1.SALARY = t1.SALARY * 1.05 WHERE t1.IRN = ?", vParser.getQuery()); vParser = new JPQLParser("UPDATE Employee e set (e.salary,e.age)=(e.salary*1.05,e.age+1) " + - "where e.id=:id", new HashMap<>()); + "where e.id=:id", new HashMap<>()); Assertions.assertEquals("UPDATE EMPLOYEE t1 SET (t1.SALARY, t1.AGE) = (t1.SALARY * 1.05, t1.AGE + 1) WHERE t1.IRN = ?", vParser.getQuery()); vParser = new JPQLParser("UPDATE Employee set (salary,age)=(salary*1.05,age+1) " + - "where id=:id", new HashMap<>()); + "where id=:id", new HashMap<>()); Assertions.assertEquals("UPDATE EMPLOYEE t1 SET (t1.SALARY, t1.AGE) = (t1.SALARY * 1.05, t1.AGE + 1) WHERE t1.IRN = ?", vParser.getQuery()); } @@ -601,8 +593,8 @@ void updateEntityUsingPK() throws JSQLParserException void whenUsingNamedParameters_thenCheckIfNamesReused() { JPQLParser vParser = new JPQLParser("select Employee from Employee " + - "where id = :num " + - "and age= :num", + "where id = :num " + + "and age= :num", new HashMap<>()); assertEquals(2, vParser.getNumberOfParameters()); } diff --git a/jpalite-core/src/test/java/io/jpalite/jqpl/Company.java b/jpalite-core/src/test/java/io/jpalite/test/Company.java similarity index 97% rename from jpalite-core/src/test/java/io/jpalite/jqpl/Company.java rename to jpalite-core/src/test/java/io/jpalite/test/Company.java index d3bcf7f..9f1e4a9 100644 --- a/jpalite-core/src/test/java/io/jpalite/jqpl/Company.java +++ b/jpalite-core/src/test/java/io/jpalite/test/Company.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package io.jpalite.jqpl; +package io.jpalite.test; import io.jpalite.impl.JPAEntityImpl; import jakarta.persistence.*; diff --git a/jpalite-core/src/test/java/io/jpalite/jqpl/Department.java b/jpalite-core/src/test/java/io/jpalite/test/Department.java similarity index 98% rename from jpalite-core/src/test/java/io/jpalite/jqpl/Department.java rename to jpalite-core/src/test/java/io/jpalite/test/Department.java index b63f2ce..c665c28 100644 --- a/jpalite-core/src/test/java/io/jpalite/jqpl/Department.java +++ b/jpalite-core/src/test/java/io/jpalite/test/Department.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package io.jpalite.jqpl; +package io.jpalite.test; import io.jpalite.impl.JPAEntityImpl; import jakarta.persistence.*; diff --git a/jpalite-core/src/test/java/io/jpalite/jqpl/Department1.java b/jpalite-core/src/test/java/io/jpalite/test/Department1.java similarity index 98% rename from jpalite-core/src/test/java/io/jpalite/jqpl/Department1.java rename to jpalite-core/src/test/java/io/jpalite/test/Department1.java index ea0770d..1711090 100644 --- a/jpalite-core/src/test/java/io/jpalite/jqpl/Department1.java +++ b/jpalite-core/src/test/java/io/jpalite/test/Department1.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package io.jpalite.jqpl; +package io.jpalite.test; import io.jpalite.impl.JPAEntityImpl; import jakarta.persistence.*; diff --git a/jpalite-core/src/test/java/io/jpalite/jqpl/Employee.java b/jpalite-core/src/test/java/io/jpalite/test/Employee.java similarity index 98% rename from jpalite-core/src/test/java/io/jpalite/jqpl/Employee.java rename to jpalite-core/src/test/java/io/jpalite/test/Employee.java index 30fbecd..809e43a 100644 --- a/jpalite-core/src/test/java/io/jpalite/jqpl/Employee.java +++ b/jpalite-core/src/test/java/io/jpalite/test/Employee.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.jpalite.jqpl; +package io.jpalite.test; import io.jpalite.impl.JPAEntityImpl; import jakarta.persistence.*; diff --git a/jpalite-core/src/test/java/io/jpalite/jqpl/Employee1.java b/jpalite-core/src/test/java/io/jpalite/test/Employee1.java similarity index 98% rename from jpalite-core/src/test/java/io/jpalite/jqpl/Employee1.java rename to jpalite-core/src/test/java/io/jpalite/test/Employee1.java index f18a921..6ed0885 100644 --- a/jpalite-core/src/test/java/io/jpalite/jqpl/Employee1.java +++ b/jpalite-core/src/test/java/io/jpalite/test/Employee1.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.jpalite.jqpl; +package io.jpalite.test; import io.jpalite.impl.JPAEntityImpl; import jakarta.persistence.*; diff --git a/jpalite-core/src/test/java/io/jpalite/jqpl/FullName.java b/jpalite-core/src/test/java/io/jpalite/test/FullName.java similarity index 98% rename from jpalite-core/src/test/java/io/jpalite/jqpl/FullName.java rename to jpalite-core/src/test/java/io/jpalite/test/FullName.java index f890708..1ed4bec 100644 --- a/jpalite-core/src/test/java/io/jpalite/jqpl/FullName.java +++ b/jpalite-core/src/test/java/io/jpalite/test/FullName.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.jpalite.jqpl; +package io.jpalite.test; import io.jpalite.impl.JPAEntityImpl; import jakarta.persistence.Column; diff --git a/jpalite-core/src/test/java/io/jpalite/jqpl/Phone.java b/jpalite-core/src/test/java/io/jpalite/test/Phone.java similarity index 97% rename from jpalite-core/src/test/java/io/jpalite/jqpl/Phone.java rename to jpalite-core/src/test/java/io/jpalite/test/Phone.java index 843c2fd..a8c8c45 100644 --- a/jpalite-core/src/test/java/io/jpalite/jqpl/Phone.java +++ b/jpalite-core/src/test/java/io/jpalite/test/Phone.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package io.jpalite.jqpl; +package io.jpalite.test; import io.jpalite.impl.JPAEntityImpl; import jakarta.persistence.*; diff --git a/jpalite-core/src/test/java/io/jpalite/jqpl/RatePlan.java b/jpalite-core/src/test/java/io/jpalite/test/RatePlan.java similarity index 98% rename from jpalite-core/src/test/java/io/jpalite/jqpl/RatePlan.java rename to jpalite-core/src/test/java/io/jpalite/test/RatePlan.java index 6243319..e9bf37d 100644 --- a/jpalite-core/src/test/java/io/jpalite/jqpl/RatePlan.java +++ b/jpalite-core/src/test/java/io/jpalite/test/RatePlan.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package io.jpalite.jqpl; +package io.jpalite.test; import io.jpalite.impl.JPAEntityImpl; import jakarta.persistence.*; diff --git a/jpalite-core/src/test/java/io/jpalite/test/TestEntityMetaDataManager.java b/jpalite-core/src/test/java/io/jpalite/test/TestEntityMetaDataManager.java new file mode 100644 index 0000000..9a3ed82 --- /dev/null +++ b/jpalite-core/src/test/java/io/jpalite/test/TestEntityMetaDataManager.java @@ -0,0 +1,40 @@ +package io.jpalite.test; + +import io.jpalite.EntityMetaDataManager; +import io.jpalite.impl.EntityMetaDataImpl; +import io.jpalite.impl.fieldtypes.*; + +public class TestEntityMetaDataManager +{ + static { + // This is a hack to get the JPA to work + EntityMetaDataManager.registerConverter(new BooleanFieldType()); + EntityMetaDataManager.registerConverter(new BoolFieldType()); + EntityMetaDataManager.registerConverter(new IntegerFieldType()); + EntityMetaDataManager.registerConverter(new IntFieldType()); + EntityMetaDataManager.registerConverter(new LongLongFieldType()); + EntityMetaDataManager.registerConverter(new LongFieldType()); + EntityMetaDataManager.registerConverter(new DoubleDoubleFieldType()); + EntityMetaDataManager.registerConverter(new DoubleFieldType()); + EntityMetaDataManager.registerConverter(new LocalDateTimeFieldType()); + EntityMetaDataManager.registerConverter(new TimestampFieldType()); + EntityMetaDataManager.registerConverter(new BytesFieldType()); + EntityMetaDataManager.registerConverter(new StringFieldType()); + EntityMetaDataManager.registerConverter(new BigDecimalFieldType()); + + EntityMetaDataManager.register(new EntityMetaDataImpl<>(RatePlan.class)); + EntityMetaDataManager.register(new EntityMetaDataImpl<>(FullName.class)); + EntityMetaDataManager.register(new EntityMetaDataImpl<>(Employee.class)); + EntityMetaDataManager.register(new EntityMetaDataImpl<>(Department.class)); + EntityMetaDataManager.register(new EntityMetaDataImpl<>(Company.class)); + EntityMetaDataManager.register(new EntityMetaDataImpl<>(Phone.class)); + + EntityMetaDataManager.register(new EntityMetaDataImpl<>(Employee1.class)); + EntityMetaDataManager.register(new EntityMetaDataImpl<>(Department1.class)); + } + + public static void init() + { + //do nothing + } +} diff --git a/jpalite-maven-plugin/src/main/java/io/jpalite/JPALiteMojo.java b/jpalite-maven-plugin/src/main/java/io/jpalite/JPALiteMojo.java index 1b33618..ad3a906 100644 --- a/jpalite-maven-plugin/src/main/java/io/jpalite/JPALiteMojo.java +++ b/jpalite-maven-plugin/src/main/java/io/jpalite/JPALiteMojo.java @@ -43,8 +43,8 @@ /** * Goal which touches a timestamp file. * - * @goal1 jpalite - * @phase1 process-sources + * @goal jpalite + * @phase process-sources *

* {@code ... * @@ -161,4 +161,4 @@ public void execute() throws MojoExecutionException }//if }//finally }//execute -}//TradeSwitchJPAMojo +}//JPALiteMojo diff --git a/jpalite-maven-plugin/src/main/java/io/jpalite/impl/JPALiteToolingImpl.java b/jpalite-maven-plugin/src/main/java/io/jpalite/impl/JPALiteToolingImpl.java index b7d6959..11db6e9 100644 --- a/jpalite-maven-plugin/src/main/java/io/jpalite/impl/JPALiteToolingImpl.java +++ b/jpalite-maven-plugin/src/main/java/io/jpalite/impl/JPALiteToolingImpl.java @@ -21,7 +21,6 @@ import io.jpalite.JPALiteToolingException; import jakarta.persistence.*; import javassist.*; -import org.infinispan.protostream.GeneratedSchema; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,7 +43,6 @@ public class JPALiteToolingImpl implements JPALiteTooling private final Map entityClasses = new TreeMap<>(); private final List converterClasses = new ArrayList<>(); - private final List protoStreamClasses = new ArrayList<>(); private String outputDir; private final ClassPool classPool; @@ -121,11 +119,6 @@ private void applyTooling(CtClass entityClass) throws JPALiteToolingException CtClass jpaEntityImpl = classPool.get(jpaEntityClass.getName()); entityClass.setSuperclass(jpaEntityImpl); - if (entityClass.getAnnotation(Cacheable.class) != null) { - CtClass serializationContext = classPool.get(GeneratedSchema.class.getName()); - entityClass.addInterface(serializationContext); - protoStreamClasses.add(entityClass.getName()); - }//if for (CtField field : entityClass.getDeclaredFields()) { if (!isStatic(field.getModifiers()) && !isFinal(field.getModifiers()) && !isTransient(field.getModifiers()) && field.getAnnotation(Transient.class) == null) { applyChangeTracker(entityClass, field); @@ -221,7 +214,7 @@ public void process(String outputDir, List classList) throws JPALiteTool classPool.appendSystemPath(); Files.createDirectories(Path.of(outputDir + "/META-INF/services")); - Files.createDirectories(Path.of(outputDir + "/META-INF/native-image/org.tradeswitch.persistent")); + Files.createDirectories(Path.of(outputDir + "/META-INF/native-image/io.jpalite.persistent")); //Build a list of all the converter classes first for (String className : classList) { @@ -236,7 +229,7 @@ public void process(String outputDir, List classList) throws JPALiteTool }//for try (FileOutputStream outputStream = new FileOutputStream(outputDir + "/META-INF/persistenceUnits.properties"); - FileOutputStream nativeImageStream = new FileOutputStream(outputDir + "/META-INF/native-image/org.tradeswitch.persistent/reflect-config.json")) { + FileOutputStream nativeImageStream = new FileOutputStream(outputDir + "/META-INF/native-image/io.jpalite.persistent/reflect-config.json")) { nativeImageStream.write("[\n".getBytes()); boolean first = true; for (Map.Entry entry : entityClasses.entrySet()) { @@ -258,7 +251,7 @@ public void process(String outputDir, List classList) throws JPALiteTool } - try (FileOutputStream converterStream = new FileOutputStream(outputDir + "/META-INF/org.tradeswitch.converters")) { + try (FileOutputStream converterStream = new FileOutputStream(outputDir + "/META-INF/services/io.jpalite.FieldConvertType")) { for (CtClass convertClass : converterClasses) { try { @@ -291,13 +284,6 @@ public void process(String outputDir, List classList) throws JPALiteTool nativeImageStream.write(']'); }//try - - try (FileOutputStream outputStream = new FileOutputStream(outputDir + "/META-INF/services/org.infinispan.protostream.SerializationContextInitializer", true)) { - for (String className : protoStreamClasses) { - outputStream.write(className.getBytes()); - outputStream.write('\n'); - }//for - }//try }//try catch (NotFoundException ex) { throw new JPALiteToolingException(ERROR_PROCESSING_FILE, ex); 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 3c054e2..fb58fc6 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 @@ -39,7 +39,8 @@ FeatureBuildItem feature() NativeImageResourceBuildItem nativeImageResourceBuildItem() { return new NativeImageResourceBuildItem("META-INF/services/io.jpalite.DataSourceProvider", - "META-INF/services/io.jpalite.PersistenceUnitProvider"); + "META-INF/services/io.jpalite.PersistenceUnitProvider", + "META-INF/services/io.jpalite.FieldConvertType"); } @BuildStep 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 af1154d..8a2b327 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 @@ -18,6 +18,7 @@ package io.jpalite.extension; import io.jpalite.JPALiteEntityManager; +import io.jpalite.impl.CacheFormat; import io.quarkus.runtime.annotations.ConfigDocMapKey; import io.quarkus.runtime.annotations.ConfigDocSection; import io.quarkus.runtime.annotations.ConfigPhase; @@ -63,16 +64,27 @@ interface PersistenceUnitConfig String datasourceName(); /** - * The name of the 2nd level cache that is to be used + * The region prefix to use (second level caching) */ - @WithDefault("L2CACHE") - @WithName("cache-name") - String cacheName(); + @WithDefault("") + @WithName("cache-region-prefix") + String cacheRegionPrefix(); /** - * The name of the 2nd level cache provider that is to be used + * Retrieves the cache client (infinispan-client, redis-client, etc) to be used by the cache provider. + * Care should be taken to reference a client that is compatible with the cache provider specified. + * + * @return The cache client name as specified in the persistence unit configuration, or the default value if not set. + * @see PersistenceUnitConfig#cacheClient() */ @WithDefault("") + @WithName("cache-client") + String cacheClient(); + + /** + * The name of the 2nd level cache provider that is to be used. + */ + @WithDefault("io.jpalite.impl.caching.JPALiteInfinispanCache") @WithName("cache-provider") String cacheProvider(); @@ -86,6 +98,14 @@ interface PersistenceUnitConfig @WithName("cache-config") String cacheConfig(); + /** + * The cache format to use when storing cache entities + */ + @WithDefault("JSON") + @WithName("cache-format") + @WithConverter(CacheFormatConverter.class) + CacheFormat cacheFormat(); + /** * Set to TRUE if the persistence unit reference a multi-tenant data source */ @@ -129,7 +149,7 @@ class PersistenceUnitTransactionTypeConverter implements Converter @Override public SharedCacheMode convert(String value) throws IllegalArgumentException, NullPointerException { - return SharedCacheMode.valueOf(value); + return SharedCacheMode.valueOf(value.toUpperCase()); + } + } + + class CacheFormatConverter implements Converter + { + @Override + public CacheFormat convert(String value) throws IllegalArgumentException, NullPointerException + { + return CacheFormat.valueOf(value.toUpperCase()); } } @@ -147,7 +176,7 @@ class ValidationModeConverter implements Converter @Override public ValidationMode convert(String value) throws IllegalArgumentException, NullPointerException { - return ValidationMode.valueOf(value); + return ValidationMode.valueOf(value.toUpperCase()); } } diff --git a/jpalite-quarkus-extension/runtime/src/main/java/io/jpalite/extension/PersistenceUnitProperties.java b/jpalite-quarkus-extension/runtime/src/main/java/io/jpalite/extension/PersistenceUnitProperties.java index 6d8a342..6fb0df4 100644 --- a/jpalite-quarkus-extension/runtime/src/main/java/io/jpalite/extension/PersistenceUnitProperties.java +++ b/jpalite-quarkus-extension/runtime/src/main/java/io/jpalite/extension/PersistenceUnitProperties.java @@ -22,7 +22,6 @@ import jakarta.persistence.PersistenceException; import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.ConfigProvider; -import org.infinispan.commons.configuration.StringConfiguration; public class PersistenceUnitProperties extends CustomPersistenceUnit { @@ -43,11 +42,13 @@ public PersistenceUnitProperties(String unitName) setMultiTenantMode(unitConfig.multiTenant()); setTransactionType(unitConfig.transactionType()); setDataSourceName(unitConfig.datasourceName()); - setCacheName(unitConfig.cacheName()); + setCacheRegionPrefix(unitConfig.cacheRegionPrefix()); + setCacheFormat(unitConfig.cacheFormat()); setSharedCacheMode(unitConfig.sharedCacheMode()); setValidationMode(unitConfig.validationMode()); + setCacheClient(unitConfig.cacheClient()); setCacheProvider(unitConfig.cacheProvider()); - setCacheConfig(new StringConfiguration(unitConfig.cacheConfig())); + setCacheConfig(unitConfig.cacheConfig()); }//PersistenceUnitProperties }//PersistenceUnitProperties 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 66ee4e6..b913659 100644 --- a/jpalite-repository/src/main/java/io/jpalite/repository/JPALiteRepositoryProcessor.java +++ b/jpalite-repository/src/main/java/io/jpalite/repository/JPALiteRepositoryProcessor.java @@ -161,7 +161,7 @@ private void addJpaRepository(PrintWriter out, DeclaredType jpaRepository) private void addPagingRepository(PrintWriter out, DeclaredType pagingRepository) { - String vArgType = pagingRepository.getTypeArguments().get(0).toString(); + String vArgType = pagingRepository.getTypeArguments().getFirst().toString(); String vEntityType = vArgType.substring(vArgType.lastIndexOf(".") + 1); out.println("public long count(Filter filter, Map hints) {"); @@ -246,7 +246,7 @@ private void createMethod(PrintWriter out, ExecutableElement method) String returnType = method.getReturnType().toString(); if (method.getReturnType() instanceof DeclaredType declaredType && !declaredType.getTypeArguments().isEmpty()) { returnCollection = true; - returnType = declaredType.getTypeArguments().get(0).toString(); + returnType = declaredType.getTypeArguments().getFirst().toString(); }//if StringBuilder pageAndSort = new StringBuilder(); @@ -421,8 +421,8 @@ private void generateRepo(TypeElement repoElement, Repository annotation) throws SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT); out.println("@Generated(value={\"Generated by " + getClass().getName() + "\"}," + - "date=\"" + dateFormat.format(new Date()) + "\"," + - "comments=\"JPALite Repository Generation\")"); + "date=\"" + dateFormat.format(new Date()) + "\"," + + "comments=\"JPALite Repository Generation\")"); out.println("@RequestScoped"); out.print("public class "); out.print(repoClassName); @@ -435,18 +435,18 @@ 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;"); +// out.println(" @Inject"); +// out.print(" @PersistenceUnit(\""); +// out.print(annotation.persistenceUnit()); +// out.println("\")"); +// out.println(" EntityManager em;"); 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;"); + 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;"); out.println("}"); repoElement.getInterfaces() diff --git a/jpalite-repository/src/main/java/io/jpalite/repository/PagingRepository.java b/jpalite-repository/src/main/java/io/jpalite/repository/PagingRepository.java index bfc8d69..608aebb 100644 --- a/jpalite-repository/src/main/java/io/jpalite/repository/PagingRepository.java +++ b/jpalite-repository/src/main/java/io/jpalite/repository/PagingRepository.java @@ -22,9 +22,9 @@ import java.util.Map; /** - * The PagingRepository is part of the TradeSwitch Repository generation feature. + * The PagingRepository is part of the JPALite Repository generation feature. *

The implementation of the interface - * must not be coded and is generated by the TradeSwitch Repository Generator + * must not be coded and is generated by the JPALite Repository Generator *

Remember to attach the interface * to the Repository Generator using the @Repository annotation! *

diff --git a/pom.xml b/pom.xml index ead2d7c..c74d810 100644 --- a/pom.xml +++ b/pom.xml @@ -112,6 +112,20 @@ maven-deploy-plugin ${maven-deploy-plugin.version} + + org.apache.maven.plugins + maven-source-plugin + 3.3.0 + + + attach-sources + verify + + jar-no-fork + + + +