diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..9308dc19 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,41 @@ +name: "CodeQL" + +on: + push: + branches: [ "master", "1.x.x" ] + pull_request: + branches: [ "master" ] + schedule: + - cron: "26 11 * * 3" + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ java ] + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + queries: +security-and-quality + + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{ matrix.language }}" \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 61937267..86e15a12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Feature: startsWith, contains, endsWith, notStartsWith, notContains and notEndsWith for like and notLike - Feature: Group By for Simple DSL - Feature: Having for Simple DSL +- Feature: Simple one type read for Simple DSL - Feature: SQL Ansi Aggregation Functions (COUNT, SUM, MIN, MAX, AVG) - Feature: Quarkus Extension module - Feature: FrameworkIntegrationService for Integration with external framework like Quarkus or Micronaut @@ -16,10 +17,32 @@ - Feature: Support for Metrics using MetricsTracker - Feature: DB2 Sql Specifics - Feature: Bean Provider for Bean Validation +- Feature: Cascade delete on not initialized lazy entities +- Feature: UUID SqlAccessor +- Feature: Merge for Entities +- Feature: Simple DSL distinct select +- Feature: Added missing methods from Optional to Result +- Feature: Count and hasResults for DSL +- Feature: Distinct for DSL Entity Select +- Feature: Distinct for DSL SubQuery +- Feature: Simple EnumConverter +- Feature: InlineValue with Alias +- Feature: Support WITH for Query Annotation - Removal: Removed notLike and like - Refactor: Refactor of IT Tests - Bugfix: Fix missing upsert for entities with relationships - Bugfix: Fix dev mode reloading issues for Quarkus +- Bugfix: Fix event dispatch on unsupported relationships +- Bugfix: Fix missing setEntity for projections with inherited classes +- Bugfix: Fix NPE on where checker for Simple DSL +- Bugfix: Fix IllegalArgumentException for invalid Vendor Function params +- Bugfix: Fix wrong exceptions on Update/Remove Event +- Bugfix: Removed useless update on Entities with a false modified flag +- Bugfix: Fix auto-set inverse execution +- Bugfix: Fix match of custom functions (like ::date) for named parameters in @Query +- Bugfix: Fix delete cascade order on one-to-one entities +- Bugfix: Copy annotations from entity methods on override +- Bugfix: Removed spaced before validation/generation of custom queries ## 1.10.0 - Feature: BeanProvider for Dependency Injection Extensions diff --git a/README.md b/README.md index 787493a9..1ae78054 100644 --- a/README.md +++ b/README.md @@ -28,14 +28,16 @@ JAORM is divided in modules that are used from main module using **Java SPI** - MySql - MS SQL Server - PostgreSQL + - DB2 - Validation - Extensions - ANSI SQL Build Extensions - - Dependency Injection Extensions + - Framework and Dependency Injection Extensions - Micronaut Extension - Jakarta CDI Extension - Javax CDI Extension - Spring DI Extension + - Quarkus Extension ## Features @@ -48,7 +50,7 @@ JAORM is divided in modules that are used from main module using **Java SPI** - Supports for most of famous RDBMS - Supports for **@Getter**, **@Setter** and **@Data** Annotations of [Lombok](https://projectlombok.org/) on Entity - Supports for [JSR 380](https://beanvalidation.org/2.0-jsr380/) Validation on Entity during Persist/Update -- Supports for DI with JavaEE, JakartaEE, Micronaut and Spring +- Supports for DI with JavaEE, JakartaEE, Micronaut, Quarkus and Spring ## Use diff --git a/jaorm-bom/pom.xml b/jaorm-bom/pom.xml index f4d31a0f..6f0cbaa7 100644 --- a/jaorm-bom/pom.xml +++ b/jaorm-bom/pom.xml @@ -5,7 +5,7 @@ jaorm-pom io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 pom diff --git a/jaorm-cache/pom.xml b/jaorm-cache/pom.xml index d7b64c24..0cdb69c0 100644 --- a/jaorm-cache/pom.xml +++ b/jaorm-cache/pom.xml @@ -5,7 +5,7 @@ jaorm-pom io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-core/pom.xml b/jaorm-core/pom.xml index 6608f8f4..bb21cb8a 100644 --- a/jaorm-core/pom.xml +++ b/jaorm-core/pom.xml @@ -5,7 +5,7 @@ jaorm-pom io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/Arguments.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/Arguments.java index e7ff04f6..7ea0316e 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/Arguments.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/Arguments.java @@ -14,6 +14,10 @@ public abstract class Arguments { public abstract boolean equals(Object o); public abstract int hashCode(); + public Stream stream() { + return Stream.of(getValues()); + } + public static Arguments of(Object... values) { return values(values); } diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/BaseDao.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/BaseDao.java index 0cd464b2..5eeef44e 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/BaseDao.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/BaseDao.java @@ -4,10 +4,7 @@ import io.github.ulisse1996.jaorm.entity.Page; import io.github.ulisse1996.jaorm.entity.Result; import io.github.ulisse1996.jaorm.entity.event.*; -import io.github.ulisse1996.jaorm.entity.relationship.EntityEvent; -import io.github.ulisse1996.jaorm.entity.relationship.EntityEventType; -import io.github.ulisse1996.jaorm.entity.relationship.Relationship; -import io.github.ulisse1996.jaorm.entity.relationship.UpdateEvent; +import io.github.ulisse1996.jaorm.entity.relationship.*; import io.github.ulisse1996.jaorm.entity.sql.SqlAccessor; import io.github.ulisse1996.jaorm.entity.sql.SqlParameter; import io.github.ulisse1996.jaorm.entity.validation.ValidationResult; @@ -74,28 +71,32 @@ default R update(R entity) { .apply(entity); checkUpsert(entity); } else { - if (entity instanceof PreUpdate) { - try { - ((PreUpdate) entity).preUpdate(); - } catch (Exception ex) { - throw new UpdateEventException(ex); - } - } - ListenersService.getInstance().fireEvent(entity, GlobalEventType.PRE_UPDATE); - UpdateEvent.updateEntity(entity); - checkUpsert(entity); - if (entity instanceof PostUpdate) { - try { - ((PostUpdate) entity).postUpdate(); - } catch (Exception ex) { - throw new UpdateEventException(ex); - } - } + handleSimpleUpdate(entity); } ListenersService.getInstance().fireEvent(entity, GlobalEventType.POST_UPDATE); return entity; } + default void handleSimpleUpdate(R entity) { + if (entity instanceof PreUpdate) { + try { + ((PreUpdate) entity).preUpdate(); + } catch (Exception ex) { + throw new UpdateEventException(ex); + } + } + ListenersService.getInstance().fireEvent(entity, GlobalEventType.PRE_UPDATE); + UpdateEvent.updateEntity(entity); + checkUpsert(entity); + if (entity instanceof PostUpdate) { + try { + ((PostUpdate) entity).postUpdate(); + } catch (Exception ex) { + throw new UpdateEventException(ex); + } + } + } + default void checkUpsert(R entity) { if (FeatureConfigurator.getInstance().isInsertAfterFailedUpdateEnabled()) { // We check for updated row for upsert @@ -106,6 +107,37 @@ default void checkUpsert(R entity) { } } + default R merge(R entity) { + Objects.requireNonNull(entity); + doValidation(entity); + RelationshipService relationshipService = RelationshipService.getInstance(); + if (relationshipService.isEventActive(entity.getClass(), EntityEventType.MERGE)) { + ListenersService.getInstance().fireEvent(entity, GlobalEventType.PRE_MERGE); + EntityEvent.forType(EntityEventType.MERGE) + .apply(entity); + } else { + if (entity instanceof PreMerge) { + try { + ((PreMerge) entity).preMerge(); + } catch (Exception ex) { + throw new UpdateEventException(ex); + } + } + ListenersService.getInstance().fireEvent(entity, GlobalEventType.PRE_MERGE); + MergeEvent.mergeEntity(entity); + checkUpsert(entity); + if (entity instanceof PostMerge) { + try { + ((PostMerge) entity).postMerge(); + } catch (Exception ex) { + throw new UpdateEventException(ex); + } + } + } + ListenersService.getInstance().fireEvent(entity, GlobalEventType.POST_MERGE); + return entity; + } + default R insert(R entity) { Objects.requireNonNull(entity); doValidation(entity); @@ -164,6 +196,11 @@ default void delete(List entities) { entities.forEach(this::delete); } + default List merge(List entities) { + Objects.requireNonNull(entities); + return entities.stream().map(this::merge).collect(Collectors.toList()); + } + default List insert(List entities) { Objects.requireNonNull(entities); return entities.stream() @@ -223,19 +260,19 @@ default void applyRelationshipBatch(List entities, Class entityClass, Enti if (node.isOpt()) { results = entities.stream() .map(EntityDelegate::unboxEntity) - .map(node::getAsOpt) + .map(i -> node.getAsOpt(i, eventType)) .filter(Result::isPresent) .map(Result::get) .collect(Collectors.toList()); } else if (node.isCollection()) { results = entities.stream() .map(EntityDelegate::unboxEntity) - .flatMap(e -> Optional.ofNullable(node.getAsCollection(e)).orElse(Collections.emptyList()).stream()) + .flatMap(e -> Optional.ofNullable(node.getAsCollection(e, eventType)).orElse(Collections.emptyList()).stream()) .collect(Collectors.toList()); } else { results = entities.stream() .map(EntityDelegate::unboxEntity) - .map(node::get) + .map(i -> node.get(i, eventType)) .filter(Objects::nonNull) .collect(Collectors.toList()); } diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/InlineValue.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/InlineValue.java index 859e14ce..1908e8a0 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/InlineValue.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/InlineValue.java @@ -7,4 +7,8 @@ public interface InlineValue extends Selectable { static InlineValue inline(R value) { return () -> value; } + + default InlineValueWithAlias as(String alias) { + return new InlineValueWithAlias<>(getValue(), alias); + } } diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/InlineValueWithAlias.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/InlineValueWithAlias.java new file mode 100644 index 00000000..67168511 --- /dev/null +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/InlineValueWithAlias.java @@ -0,0 +1,21 @@ +package io.github.ulisse1996.jaorm; + +public class InlineValueWithAlias implements InlineValue { + + private final R value; + private final String alias; + + InlineValueWithAlias(R value, String alias) { + this.value = value; + this.alias = alias; + } + + public String getAlias() { + return alias; + } + + @Override + public R getValue() { + return this.value; + } +} diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/annotation/CascadeType.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/annotation/CascadeType.java index f002df65..add4f890 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/annotation/CascadeType.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/annotation/CascadeType.java @@ -7,5 +7,6 @@ public enum CascadeType { ALL, PERSIST, REMOVE, - UPDATE + UPDATE, + MERGE } diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/DirtinessTracker.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/DirtinessTracker.java new file mode 100644 index 00000000..51995c18 --- /dev/null +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/DirtinessTracker.java @@ -0,0 +1,34 @@ +package io.github.ulisse1996.jaorm.entity; + +import java.util.ArrayList; +import java.util.List; + +public class DirtinessTracker { + + private final EntityDelegate delegate; + private final List removedElements; + + public DirtinessTracker(EntityDelegate delegate) { + this.delegate = delegate; + this.removedElements = new ArrayList<>(); + } + + public EntityDelegate getDelegate() { + return delegate; + } + + public void registerRemoved(Object element) { + if (element instanceof Result) { + Result result = (Result) element; + if (result.isPresent() && result.get() instanceof EntityDelegate) { + this.removedElements.add(result.get()); + } + } else if (element instanceof EntityDelegate) { + this.removedElements.add(element); + } + } + + public List getRemovedElements() { + return removedElements; + } +} diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/EntityDelegate.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/EntityDelegate.java index 019df94c..f87689c0 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/EntityDelegate.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/EntityDelegate.java @@ -1,5 +1,7 @@ package io.github.ulisse1996.jaorm.entity; +import io.github.ulisse1996.jaorm.entity.relationship.LazyEntityInfo; +import io.github.ulisse1996.jaorm.entity.relationship.RelationshipManager; import io.github.ulisse1996.jaorm.schema.TableInfo; import java.sql.ResultSet; @@ -17,6 +19,15 @@ static R unboxEntity(R e) { return e; } + @SuppressWarnings("unchecked") + static EntityDelegate unwrap(R e) { + if (e instanceof EntityDelegate) { + return (EntityDelegate) e; + } else { + throw new UnsupportedOperationException(String.format("%s is not an EntityDelegate", e != null ? e.getClass().getName() : "null")); + } + } + EntityDelegate generateDelegate(); Supplier getEntityInstance(); EntityMapper getEntityMapper(); @@ -33,9 +44,15 @@ static R unboxEntity(R e) { String getUpdateSql(); String getDeleteSql(); boolean isModified(); + void setModified(boolean modified); boolean isDefaultGeneration(); T initDefault(T entity); TableInfo toTableInfo(); + DirtinessTracker getTracker(); + boolean isLazyEntity(); + LazyEntityInfo getLazyInfo(); + void setLazyInfo(LazyEntityInfo info); + RelationshipManager getRelationshipManager(); default void setAutoGenerated(Map generated) { if (!generated.isEmpty()) { EntityMapper mapper = getEntityMapper(); diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/Result.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/Result.java index 4183f9ce..fa7bd212 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/Result.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/Result.java @@ -5,6 +5,9 @@ import java.util.Optional; import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Stream; public class Result { @@ -28,6 +31,10 @@ public boolean isPresent() { return entity != null; } + public boolean isEmpty() { + return entity == null; + } + public T get() { if (this.entity == null) { throw new NoSuchElementException("No value present"); @@ -59,11 +66,92 @@ public void ifPresent(Consumer consumer) { } } + public void ifPresentOrElse(Consumer action, Runnable emptyAction) { + if (entity != null) { + action.accept(entity); + } else { + emptyAction.run(); + } + } + + public Result filter(Predicate predicate) { + Objects.requireNonNull(predicate); + if (!isPresent()) { + return this; + } else { + return predicate.test(entity) ? this : empty(); + } + } + + public Result or(Supplier> supplier) { + Objects.requireNonNull(supplier); + if (isPresent()) { + return this; + } else { + @SuppressWarnings("unchecked") + Result r = (Result) supplier.get(); + return Objects.requireNonNull(r); + } + } + + public Stream stream() { + if (!isPresent()) { + return Stream.empty(); + } else { + return Stream.of(entity); + } + } + public T orElse(T val) { return isPresent() ? this.entity : val; } + public T orElseGet(Supplier supplier) { + return entity != null ? entity : supplier.get(); + } + + public T orElseThrow() { + if (entity == null) { + throw new NoSuchElementException("No value present"); + } + return entity; + } + + public T orElseThrow(Supplier exceptionSupplier) throws X { + if (entity != null) { + return entity; + } else { + throw exceptionSupplier.get(); + } + } + public Optional toOptional() { return Optional.ofNullable(this.entity); } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (!(obj instanceof Result)) { + return false; + } + + Result other = (Result) obj; + return Objects.equals(entity, other.entity); + } + + @Override + public int hashCode() { + return Objects.hashCode(entity); + } + + @Override + public String toString() { + return entity != null + ? String.format("Result[%s]", entity) + : "Result.empty"; + } } diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/TrackedList.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/TrackedList.java new file mode 100644 index 00000000..ef30e012 --- /dev/null +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/TrackedList.java @@ -0,0 +1,84 @@ +package io.github.ulisse1996.jaorm.entity; + +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class TrackedList extends AbstractList { + + private final List delegate; + private final List removedElements; + private final EntityDelegate parent; + + public TrackedList(EntityDelegate parent, List delegate) { + this(parent, delegate, new ArrayList<>()); + } + + public TrackedList(EntityDelegate parent, List delegate, List removedElements) { + this.parent = parent; + this.delegate = new ArrayList<>(delegate); + this.removedElements = new ArrayList<>(removedElements); + } + + public static TrackedList merge(EntityDelegate parent, List from, List newList) { + if (from == null) { + return new TrackedList<>(parent, newList, new ArrayList<>()); + } + if (from instanceof TrackedList) { + // Override of current state is equals to clear, so we get all elements (previously deleted or not) and add to initial deleted + List all = Stream.concat(((TrackedList) from).getRemovedElements().stream(), from.stream()) + .collect(Collectors.toList()); + return new TrackedList<>(parent, newList, all); + } else { + return new TrackedList<>(parent, newList, from); + } + } + + @Override + public T get(int index) { + return delegate.get(index); + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public T set(int index, T element) { + T removed = delegate.set(index, element); + this.removedElements.add(removed); + return removed; + } + + @Override + public void add(int index, T element) { + delegate.add(index, element); + } + + @Override + public T remove(int index) { + T removed = delegate.remove(index); + this.removedElements.add(removed); + this.parent.getTracker().registerRemoved(removed); + return removed; + } + + public List getRemovedElements() { + return this.removedElements; + } + + @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") + @Override + public boolean equals(Object o) { + return Objects.equals(this.delegate, o); + } + + @Override + public int hashCode() { + return this.delegate.hashCode(); + } +} diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/converter/EnumConverter.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/converter/EnumConverter.java new file mode 100644 index 00000000..eb2ab147 --- /dev/null +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/converter/EnumConverter.java @@ -0,0 +1,27 @@ +package io.github.ulisse1996.jaorm.entity.converter; + +import java.util.stream.Stream; + +public abstract class EnumConverter> implements ValueConverter { + + private final T[] values; + private final Class klass; + + protected EnumConverter(Class klass) { + this.klass = klass; + this.values = klass.getEnumConstants(); + } + + @Override + public T fromSql(String val) { + return Stream.of(values) + .filter(el -> el.name().equalsIgnoreCase(val)) + .findFirst() + .orElseThrow(() -> new EnumConstantNotPresentException(this.klass, val)); + } + + @Override + public String toSql(T val) { + return val != null ? val.name() : null; + } +} \ No newline at end of file diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/event/GlobalEventType.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/event/GlobalEventType.java index fb64ebd0..078f3d23 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/event/GlobalEventType.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/event/GlobalEventType.java @@ -6,5 +6,7 @@ public enum GlobalEventType { POST_UPDATE, PRE_UPDATE, POST_REMOVE, - PRE_REMOVE + PRE_REMOVE, + PRE_MERGE, + POST_MERGE; } diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/event/PostMerge.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/event/PostMerge.java new file mode 100644 index 00000000..1c1d5779 --- /dev/null +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/event/PostMerge.java @@ -0,0 +1,6 @@ +package io.github.ulisse1996.jaorm.entity.event; + +public interface PostMerge { + + void postMerge() throws X; +} diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/event/PreMerge.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/event/PreMerge.java new file mode 100644 index 00000000..3fd3380f --- /dev/null +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/event/PreMerge.java @@ -0,0 +1,6 @@ +package io.github.ulisse1996.jaorm.entity.event; + +public interface PreMerge { + + void preMerge() throws X; +} diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/EntityEvent.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/EntityEvent.java index 9e0df682..cac9cc63 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/EntityEvent.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/EntityEvent.java @@ -12,11 +12,11 @@ static EntityEvent forType(EntityEventType type) { return type.getEntityEvent(); } - default boolean isDelegate(T entity) { + static boolean isDelegate(T entity) { return entity instanceof EntityDelegate; } - default Class getRealClass(Class klass) { + static Class getRealClass(Class klass) { return DelegatesService.getInstance().getEntityClass(klass); } diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/EntityEventType.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/EntityEventType.java index e159667f..ab6c5f83 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/EntityEventType.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/EntityEventType.java @@ -5,7 +5,8 @@ public enum EntityEventType { PERSIST(PersistEvent::new), REMOVE(RemoveEvent::new), - UPDATE(UpdateEvent::new); + UPDATE(UpdateEvent::new), + MERGE(MergeEvent::new); private final EntityEvent entityEvent; @@ -16,4 +17,9 @@ public enum EntityEventType { public EntityEvent getEntityEvent() { return entityEvent; } + + @Override + public String toString() { + return String.format("EntityEventType.%s", name()); + } } diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/LazyDeleteEvent.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/LazyDeleteEvent.java new file mode 100644 index 00000000..04a69af7 --- /dev/null +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/LazyDeleteEvent.java @@ -0,0 +1,59 @@ +package io.github.ulisse1996.jaorm.entity.relationship; + +import io.github.ulisse1996.jaorm.entity.EntityDelegate; +import io.github.ulisse1996.jaorm.entity.sql.SqlParameter; +import io.github.ulisse1996.jaorm.spi.DelegatesService; +import io.github.ulisse1996.jaorm.spi.QueryRunner; +import io.github.ulisse1996.jaorm.spi.RelationshipService; + +import java.util.List; +import java.util.stream.Collectors; + +public class LazyDeleteEvent { + + private static final String SELECT = "SELECT %s FROM %s%s"; + private static final String DELETE = "DELETE FROM %s WHERE %s %s %s"; + + @SuppressWarnings("unchecked") + public void apply(T entity, Relationship.Node node) { + EntityDelegate delegate; + if (!(entity instanceof EntityDelegate)) { + delegate = (EntityDelegate) DelegatesService.getInstance().searchDelegate(entity.getClass()).get(); + } else { + delegate = (EntityDelegate) entity; + } + LazyEntityInfo lazyEntityInfo = null; + if (delegate.isLazyEntity()) { + lazyEntityInfo = delegate.getLazyInfo(); + } + RelationshipManager.RelationshipInfo info = delegate.getRelationshipManager().getRelationshipInfo(node.getName()); + List parameters; + if (lazyEntityInfo != null) { + parameters = lazyEntityInfo.getParameters(); + } else { + parameters = info.getParameters().stream() + .map(el -> el.apply(entity)) + .map(SqlParameter::new) + .collect(Collectors.toList()); + } + EntityDelegate childDelegate = (EntityDelegate) DelegatesService.getInstance().searchDelegate(node.getLinkedClass()).get(); + if (RelationshipService.getInstance().isEventActive(node.getLinkedClass(), EntityEventType.REMOVE)) { + Relationship relationship = (Relationship) RelationshipService.getInstance().getRelationships(node.getLinkedClass()); + if (relationship == null) { + return; + } + for (Relationship.Node childNode : relationship.getNodeSet()) { + RelationshipManager.RelationshipInfo childInfo = childDelegate.getRelationshipManager().getRelationshipInfo(childNode.getName()); + String childWhere = childInfo.getWhere().replace("WHERE", "").trim().split(" ")[0]; // WHERE COLUMN_ID = ? + String sql = String.format(SELECT, childWhere, childDelegate.getTable(), info.getWhere()); + childDelegate.setLazyInfo(new LazyEntityInfo(parameters, sql, node.isCollection())); + new LazyDeleteEvent().apply(childDelegate, childNode); + } + } + String where = info.getWhere().replace("WHERE", "").trim().split(" ")[0]; // WHERE COLUMN_ID = ? + boolean many = lazyEntityInfo != null && lazyEntityInfo.isFromMany(); + String deleteWhere = lazyEntityInfo != null ? String.format("(%s)", lazyEntityInfo.getSql().trim()) : "?"; + String delete = String.format(DELETE, childDelegate.getTable(), where, many ? "IN" : "=", deleteWhere); + QueryRunner.getSimple().delete(delete, parameters); + } +} diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/LazyEntityInfo.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/LazyEntityInfo.java new file mode 100644 index 00000000..12788dcc --- /dev/null +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/LazyEntityInfo.java @@ -0,0 +1,30 @@ +package io.github.ulisse1996.jaorm.entity.relationship; + +import io.github.ulisse1996.jaorm.entity.sql.SqlParameter; + +import java.util.List; + +public class LazyEntityInfo { + + private final List parameters; + private final String sql; + private final boolean fromMany; + + public LazyEntityInfo(List parameters, String sql, boolean fromMany) { + this.parameters = parameters; + this.sql = sql; + this.fromMany = fromMany; + } + + public boolean isFromMany() { + return fromMany; + } + + public List getParameters() { + return parameters; + } + + public String getSql() { + return sql; + } +} diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/MergeEvent.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/MergeEvent.java new file mode 100644 index 00000000..d37b38ed --- /dev/null +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/MergeEvent.java @@ -0,0 +1,133 @@ +package io.github.ulisse1996.jaorm.entity.relationship; + +import io.github.ulisse1996.jaorm.BaseDao; +import io.github.ulisse1996.jaorm.entity.*; +import io.github.ulisse1996.jaorm.entity.event.PostMerge; +import io.github.ulisse1996.jaorm.entity.event.PreMerge; +import io.github.ulisse1996.jaorm.exception.MergeEventException; +import io.github.ulisse1996.jaorm.spi.DelegatesService; +import io.github.ulisse1996.jaorm.spi.QueriesService; +import io.github.ulisse1996.jaorm.spi.QueryRunner; +import io.github.ulisse1996.jaorm.spi.RelationshipService; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public class MergeEvent extends PreApplyEvent { + + @Override + public void apply(T entity) { + if (entity instanceof PreMerge) { + try { + ((PreMerge) entity).preMerge(); + } catch (Exception ex) { + throw new MergeEventException(ex); + } + } + mergeEntity(entity); + if (entity instanceof PostMerge) { + try { + ((PostMerge) entity).postMerge(); + } catch (Exception ex) { + throw new MergeEventException(ex); + } + } + } + + @SuppressWarnings("unchecked") + public static void mergeEntity(R entity) { + boolean hasRelationshipsWithLinkedId = hasRelationshipsWithLinkedId(entity); + if (!(entity instanceof EntityDelegate) && hasRelationshipsWithLinkedId) { + R insert = QueryRunner.getInstance(entity.getClass()) + .insert(entity, DelegatesService.getInstance().getInsertSql(entity), + DelegatesService.getInstance().asInsert(entity).asSqlParameters()); + doMergeAfterPersist(insert); + } else { + checkRemoved(entity); + doPreApply(entity, (dao, i) -> { + dao.merge(i); + return QueryRunner.getInstance(i.getClass()).getUpdatedRows(i); + }, true, EntityEventType.MERGE); + if (!(entity instanceof EntityDelegate)) { + new PersistEvent().persist(entity, (Class) entity.getClass()); + } else { + UpdateEvent.updateEntity(entity); + } + } + } + + private static void checkRemoved(R entity) { + if (!(entity instanceof EntityDelegate)) { + return; + } + EntityDelegate delegate = EntityDelegate.unwrap(entity); + DirtinessTracker tracker = delegate.getTracker(); + if (!tracker.getRemovedElements().isEmpty()) { + Relationship relationships = RelationshipService.getInstance().getRelationships(EntityDelegate.unboxEntity(entity).getClass()); + if (relationships == null) { + return; + } + applyRemove(relationships, tracker.getRemovedElements()); + } + } + + @SuppressWarnings("unchecked") + private static void doMergeAfterPersist(R entity) { + Class klass = (Class) entity.getClass(); + if (EntityEvent.isDelegate(entity)) { + klass = (Class) EntityEvent.getRealClass(klass); + } + Relationship tree = RelationshipService.getInstance().getRelationships(klass); + if (tree == null) { + return; + } + for (Relationship.Node node : tree.getNodeSet()) { + if (!node.matchEvent(EntityEventType.MERGE)) { + continue; + } + checkNode(node, entity); + } + } + + @SuppressWarnings("unchecked") + private static void checkNode(Relationship.Node node, R entity) { + if (node.isCollection()) { + Collection collection = node.getAsCollection(entity, EntityEventType.MERGE); + if (collection instanceof TrackedList && !((TrackedList) collection).getRemovedElements().isEmpty()) { + Relationship relationships = RelationshipService.getInstance().getRelationships(EntityDelegate.unboxEntity(entity).getClass()); + if (relationships == null) { + return; + } + applyRemove(relationships, ((TrackedList) collection).getRemovedElements()); + } + collection.forEach(i -> { + node.getAutoSet().accept(i, entity); + Objects.requireNonNull(i, "Collection can't contains null values !"); + BaseDao baseDao = QueriesService.getInstance().getBaseDao((Class) i.getClass()); + baseDao.merge(i); + }); + } else if (node.isOpt()) { + Result optional = node.getAsOpt(entity, EntityEventType.MERGE); + if (optional.isPresent()) { + Object i = optional.get(); + node.getAutoSet().accept(i, entity); + BaseDao baseDao = QueriesService.getInstance().getBaseDao((Class) i.getClass()); + baseDao.merge(i); + } + } else { + Object i = node.get(entity, EntityEventType.MERGE); + if (i != null) { + node.getAutoSet().accept(i, entity); + BaseDao baseDao = QueriesService.getInstance().getBaseDao((Class) i.getClass()); + baseDao.merge(i); + } + } + } + + @Override + public T applyAndReturn(T entity) { + throw new UnsupportedOperationException("ApplyAndReturn is not implemented for MergeEvent"); + } +} diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/PersistEvent.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/PersistEvent.java index ffd5e9d9..2878d067 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/PersistEvent.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/PersistEvent.java @@ -21,35 +21,34 @@ public void apply(T entity) { @SuppressWarnings("unchecked") public T applyAndReturn(T entity) { Class klass = (Class) entity.getClass(); - if (isDelegate(entity)) { - klass = (Class) getRealClass(klass); + if (EntityEvent.isDelegate(entity)) { + klass = (Class) EntityEvent.getRealClass(klass); } Relationship tree = RelationshipService.getInstance().getRelationships(klass); - doPrePersist(entity); - T insert = QueryRunner.getInstance(klass) - .insert(entity, DelegatesService.getInstance().getInsertSql(entity), - DelegatesService.getInstance().asInsert(entity).asSqlParameters()); - doPostPersist(entity); + T insert = persist(entity, klass); for (Relationship.Node node : tree.getNodeSet()) { + if (!node.matchEvent(EntityEventType.PERSIST)) { + continue; + } if (node.isCollection()) { - node.getAsCollection(insert).forEach(i -> { - node.getAutoSet().accept(insert, i); + node.getAsCollection(insert, EntityEventType.PERSIST).forEach(i -> { + node.getAutoSet().accept(i, insert); Objects.requireNonNull(i, "Collection can't contains null values !"); BaseDao baseDao = QueriesService.getInstance().getBaseDao((Class) i.getClass()); baseDao.insert(i); }); } else if (node.isOpt()) { - Result optional = node.getAsOpt(insert); + Result optional = node.getAsOpt(insert, EntityEventType.PERSIST); if (optional.isPresent()) { Object i = optional.get(); - node.getAutoSet().accept(insert, i); + node.getAutoSet().accept(i, insert); BaseDao baseDao = QueriesService.getInstance().getBaseDao((Class) i.getClass()); baseDao.insert(i); } } else { - Object i = node.get(insert); + Object i = node.get(insert, EntityEventType.PERSIST); if (i != null) { - node.getAutoSet().accept(insert, i); + node.getAutoSet().accept(i, insert); BaseDao baseDao = QueriesService.getInstance().getBaseDao((Class) i.getClass()); baseDao.insert(i); } @@ -59,6 +58,15 @@ public T applyAndReturn(T entity) { return insert; } + public T persist(T entity, Class klass) { + doPrePersist(entity); + T insert = QueryRunner.getInstance(klass) + .insert(entity, DelegatesService.getInstance().getInsertSql(entity), + DelegatesService.getInstance().asInsert(entity).asSqlParameters()); + doPostPersist(entity); + return insert; + } + private void doPostPersist(T entity) { if (entity instanceof PostPersist) { try { diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/PreApplyEvent.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/PreApplyEvent.java index 59cd951d..cafe3aa4 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/PreApplyEvent.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/PreApplyEvent.java @@ -1,73 +1,158 @@ package io.github.ulisse1996.jaorm.entity.relationship; import io.github.ulisse1996.jaorm.BaseDao; +import io.github.ulisse1996.jaorm.entity.EntityDelegate; +import io.github.ulisse1996.jaorm.entity.EntityMapper; import io.github.ulisse1996.jaorm.entity.Result; +import io.github.ulisse1996.jaorm.entity.TrackedList; import io.github.ulisse1996.jaorm.logger.JaormLogger; +import io.github.ulisse1996.jaorm.spi.DelegatesService; import io.github.ulisse1996.jaorm.spi.FeatureConfigurator; import io.github.ulisse1996.jaorm.spi.QueriesService; import io.github.ulisse1996.jaorm.spi.RelationshipService; -import java.util.Arrays; -import java.util.Objects; +import java.util.*; import java.util.function.BiFunction; +import java.util.stream.Collectors; @SuppressWarnings("unchecked") public abstract class PreApplyEvent implements EntityEvent { - protected void doPreApply(T entity, BiFunction, Object, Integer> function, boolean update) { + private static final JaormLogger logger = JaormLogger.getLogger(PreApplyEvent.class); + + private static List getKeys(R entity) { + EntityMapper entityMapper = DelegatesService.getInstance().searchDelegate(entity) + .get().getEntityMapper(); + return entityMapper.getMappers() + .stream() + .filter(EntityMapper.ColumnMapper::isKey) + .map(EntityMapper.ColumnMapper::getName) + .collect(Collectors.toList()); + } + + protected static boolean hasRelationshipsWithLinkedId(R entity) { + Class klass = (Class) entity.getClass(); + if (EntityEvent.isDelegate(entity)) { + klass = (Class) EntityEvent.getRealClass(entity.getClass()); + } + Relationship relationships = RelationshipService.getInstance().getRelationships(klass); + + if (relationships == null) { + return true; + } + + List keys = getKeys(entity); + for (Relationship.Node node : relationships.getNodeSet()) { + if (node.getLinkedKeys().stream().anyMatch(keys::contains)) { + return true; + } + } + + return false; + } + + protected static void doPreApply(T entity, BiFunction, Object, Integer> function, boolean update, + EntityEventType eventType) { Class klass = (Class) entity.getClass(); - if (isDelegate(entity)) { - klass = (Class) getRealClass(klass); + if (EntityEvent.isDelegate(entity)) { + klass = (Class) EntityEvent.getRealClass(klass); } Relationship tree = RelationshipService.getInstance().getRelationships(klass); + if (tree == null) { + return; + } for (Relationship.Node node : tree.getNodeSet()) { - if (node.isCollection()) { - node.getAsCollection(entity).forEach(i -> { - Objects.requireNonNull(i, "Collection can't contains null values !"); - BaseDao baseDao = (BaseDao) QueriesService.getInstance().getBaseDao(i.getClass()); - Integer res = function.apply(baseDao, i); - if (shouldTryInsert(res, update)) { - tryInsert(baseDao, node, i, entity); - } - }); - } else if (node.isOpt()) { - applyOpt(entity, function, node, update); + if (!node.matchEvent(eventType)) { + continue; + } + checkPreApplyNode(entity, function, update, eventType, node); + } + } + + private static void checkPreApplyNode(T entity, BiFunction, Object, Integer> function, boolean update, + EntityEventType eventType, Relationship.Node node) { + if (node.isCollection()) { + Collection collection = node.getAsCollection(entity, eventType); + if (EntityEventType.MERGE.equals(eventType) && collection instanceof TrackedList && !((TrackedList) collection).getRemovedElements().isEmpty()) { + Relationship relationships = RelationshipService.getInstance().getRelationships(EntityDelegate.unboxEntity(entity).getClass()); + if (relationships == null) { + return; + } + applyRemove(relationships, ((TrackedList) collection).getRemovedElements()); + } + collection.forEach(i -> { + Objects.requireNonNull(i, "Collection can't contains null values !"); + BaseDao baseDao = (BaseDao) QueriesService.getInstance().getBaseDao(i.getClass()); + Integer res = function.apply(baseDao, i); + if (shouldTryInsert(res, update)) { + tryInsert(baseDao, node, i, entity); + } + }); + } else if (node.isOpt()) { + applyOpt(entity, function, node, update, eventType); + } else { + doSimple(entity, function, update, node, eventType); + } + } + + protected static void applyRemove(Relationship relationships, List removedElements) { + for (Object removed : removedElements) { + Class entityClass; + if (removed instanceof EntityDelegate) { + entityClass = EntityDelegate.unboxEntity(removed).getClass(); } else { - doSimple(entity, function, update, node); + entityClass = removed.getClass(); + } + Optional> node = findNode(entityClass, relationships.getNodeSet()); + if (node.isPresent()) { + if (node.get().matchEvent(EntityEventType.MERGE)) { + new RemoveEvent().apply(removed); + } else { + logger.warn(() -> String.format("Linked Entity with class %s is removed but node doesn't match MERGE event", entityClass.getName())); + } } } } - private void doSimple(T entity, BiFunction, Object, Integer> function, boolean update, Relationship.Node node) { - Object i = node.get(entity); + private static Optional> findNode(Class aClass, List> nodeSet) { + return nodeSet.stream() + .filter(el -> el.getLinkedClass().equals(aClass)) + .findFirst(); + } + + private static void doSimple(T entity, BiFunction, Object, Integer> function, boolean update, Relationship.Node node, + EntityEventType eventType) { + Object i = node.get(entity, eventType); if (i != null) { - BaseDao baseDao = (BaseDao) QueriesService.getInstance().getBaseDao(i.getClass()); - Integer res = function.apply(baseDao, i); - if (shouldTryInsert(res, update)) { - tryInsert(baseDao, node, i, entity); - } + doApply(entity, function, update, node, i); } } - private void applyOpt(T entity, BiFunction, Object, Integer> function, Relationship.Node node, boolean update) { - Result optional = node.getAsOpt(entity); + private static void doApply(T entity, BiFunction, Object, Integer> function, boolean update, Relationship.Node node, Object i) { + BaseDao baseDao = (BaseDao) QueriesService.getInstance().getBaseDao(i.getClass()); + Integer res = function.apply(baseDao, i); + if (shouldTryInsert(res, update)) { + tryInsert(baseDao, node, i, entity); + } + node.getAutoSet().accept(i, entity); + } + + private static void applyOpt(T entity, BiFunction, Object, Integer> function, Relationship.Node node, boolean update, + EntityEventType eventType) { + Result optional = node.getAsOpt(entity, eventType); if (optional.isPresent()) { Object i = optional.get(); - BaseDao baseDao = (BaseDao) QueriesService.getInstance().getBaseDao(i.getClass()); - Integer res = function.apply(baseDao, i); - if (shouldTryInsert(res, update)) { - tryInsert(baseDao, node, i, entity); - } + doApply(entity, function, update, node, i); } } - private boolean shouldTryInsert(Integer res, boolean update) { + private static boolean shouldTryInsert(Integer res, boolean update) { return res != null && res == 0 && update; } - private void tryInsert(BaseDao baseDao, Relationship.Node node, Object i, T entity) { + private static void tryInsert(BaseDao baseDao, Relationship.Node node, Object i, T entity) { if (node.matchEvent(EntityEventType.PERSIST) && FeatureConfigurator.getInstance().isInsertAfterFailedUpdateEnabled()) { - node.getAutoSet().accept(entity, i); + node.getAutoSet().accept(i, entity); baseDao.insert(i); } else { JaormLogger.getLogger(PreApplyEvent.class).debug(() -> { diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/Relationship.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/Relationship.java index b4939379..5c2a7380 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/Relationship.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/Relationship.java @@ -25,7 +25,7 @@ public Node getLast() { return this.nodeSet.get(this.nodeSet.size() - 1); } - public List> getNodeSet() { //NOSONAR + public List> getNodeSet() { return Collections.unmodifiableList(this.nodeSet); } @@ -40,22 +40,35 @@ public static class Node { private final boolean collection; private final List events; private final Class linkedClass; - private BiConsumer autoSet; + private final String name; + private BiConsumer autoSet; + private final List linkedKeys; - public Node(Class linkedClass, Function function, boolean opt, boolean collection, EntityEventType... events) { + public Node(Class linkedClass, Function function, boolean opt, boolean collection, String name, List linkedKeys, + EntityEventType... events) { this.function = function; this.opt = opt; this.collection = collection; this.events = Arrays.asList(events); this.linkedClass = linkedClass; + this.name = name; this.autoSet = (entity, link) -> {}; + this.linkedKeys = Collections.unmodifiableList(linkedKeys); } - public void appendThen(BiConsumer then) { + public List getLinkedKeys() { + return linkedKeys; + } + + public String getName() { + return name; + } + + public void appendThen(BiConsumer then) { this.autoSet = this.autoSet.andThen(then); } - public BiConsumer getAutoSet() { //NOSONAR + public BiConsumer getAutoSet() { //NOSONAR return autoSet; } @@ -76,29 +89,41 @@ public boolean isOpt() { } @SuppressWarnings("unchecked") - public Result getAsOpt(T entity) { + public Result getAsOpt(T entity, EntityEventType eventType) { if (EntityDelegate.class.isAssignableFrom(entity.getClass())) { entity = ((EntityDelegate) entity).getEntity(); } - return Optional.ofNullable((Result) function.apply(entity)) + Result result = Optional.ofNullable((Result) function.apply(entity)) .orElse(Result.empty()); + if (EntityEventType.REMOVE.equals(eventType) && !result.isPresent()) { + new LazyDeleteEvent().apply(entity, this); + } + return result; } @SuppressWarnings("unchecked") - public Collection getAsCollection(T entity) { + public Collection getAsCollection(T entity, EntityEventType eventType) { if (EntityDelegate.class.isAssignableFrom(entity.getClass())) { entity = ((EntityDelegate) entity).getEntity(); } Collection res = (Collection) function.apply(entity); - return Optional.ofNullable(res).orElse(Collections.emptyList()); + Collection objects = Optional.ofNullable(res).orElse(Collections.emptyList()); + if (EntityEventType.REMOVE.equals(eventType) && objects.isEmpty()) { + new LazyDeleteEvent().apply(entity, this); + } + return objects; } @SuppressWarnings("unchecked") - public Object get(T entity) { + public Object get(T entity, EntityEventType eventType) { if (EntityDelegate.class.isAssignableFrom(entity.getClass())) { entity = ((EntityDelegate) entity).getEntity(); } - return function.apply(entity); + Object object = function.apply(entity); + if (EntityEventType.REMOVE.equals(eventType) && Objects.isNull(object)) { + new LazyDeleteEvent().apply(entity, this); + } + return object; } } } diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/RelationshipManager.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/RelationshipManager.java new file mode 100644 index 00000000..469df51b --- /dev/null +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/RelationshipManager.java @@ -0,0 +1,88 @@ +package io.github.ulisse1996.jaorm.entity.relationship; + +import io.github.ulisse1996.jaorm.logger.JaormLogger; + +import java.util.*; +import java.util.function.BiConsumer; +import java.util.function.Function; + +public class RelationshipManager { + + private static final JaormLogger logger = JaormLogger.getLogger(RelationshipManager.class); + private final Map> relationships; + + public RelationshipManager() { + this.relationships = new HashMap<>(); + } + + public RelationshipManager addRelationshipInfo(String name, RelationshipInfo info) { + this.relationships.put(name, info); + return this; + } + + public RelationshipInfo getRelationshipInfo(String name) { + Optional> info = Optional.ofNullable(this.relationships.get(name)); + return info.orElseThrow(() -> new IllegalArgumentException(String.format("Can't find relationship with name %s", name))); + } + + public static void applyCallback(String name, Object from, Object to, + Class fromClass, Class toClass, + BiConsumer setter, Function getter, + R defaultValue) { + R value = Objects.nonNull(defaultValue) ? defaultValue : getter.apply(fromClass.cast(from)); + if (Objects.nonNull(value)) { + logger.debug(() -> String.format("Applying relationship callback with name %s and value %s", name, value)); + M target = toClass.cast(to); + setter.accept(target, value); + } else { + logger.debug(() -> String.format("Skipping relationship callback with name %s for null value", name)); + } + } + + public static class RelationshipInfo { + + private final String where; + private final List> parameters; + + public static class Builder { + + private String where; + private final List> parameters; + + private Builder() { + this.parameters = new ArrayList<>(); + } + + public static Builder builder() { + return new Builder<>(); + } + + public Builder where(String where) { + this.where = where; + return this; + } + + public Builder param(Function parameter) { + this.parameters.add(parameter); + return this; + } + + public RelationshipInfo build() { + return new RelationshipInfo<>(this.where, this.parameters); + } + } + + private RelationshipInfo(String where, List> parameters) { + this.where = where; + this.parameters = parameters; + } + + public String getWhere() { + return where; + } + + public List> getParameters() { + return parameters; + } + } +} diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/RemoveEvent.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/RemoveEvent.java index b7702d63..d8b169f0 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/RemoveEvent.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/RemoveEvent.java @@ -3,7 +3,7 @@ import io.github.ulisse1996.jaorm.BaseDao; import io.github.ulisse1996.jaorm.entity.event.PostRemove; import io.github.ulisse1996.jaorm.entity.event.PreRemove; -import io.github.ulisse1996.jaorm.exception.PersistEventException; +import io.github.ulisse1996.jaorm.exception.RemoveEventException; import io.github.ulisse1996.jaorm.spi.DelegatesService; import io.github.ulisse1996.jaorm.spi.QueryRunner; @@ -15,24 +15,31 @@ public void apply(T entity) { try { ((PreRemove) entity).preRemove(); } catch (Exception ex) { - throw new PersistEventException(ex); + throw new RemoveEventException(ex); } } - doPreApply(entity, BaseDao::delete, false); - QueryRunner.getInstance(entity.getClass()) - .delete(DelegatesService.getInstance().getDeleteSql(entity.getClass()), - DelegatesService.getInstance().asWhere(entity).asSqlParameters()); + if (hasRelationshipsWithLinkedId(entity)) { + doPreApply(entity, BaseDao::delete, false, EntityEventType.REMOVE); + QueryRunner.getInstance(entity.getClass()) + .delete(DelegatesService.getInstance().getDeleteSql(entity.getClass()), + DelegatesService.getInstance().asWhere(entity).asSqlParameters()); + } else { + QueryRunner.getInstance(entity.getClass()) + .delete(DelegatesService.getInstance().getDeleteSql(entity.getClass()), + DelegatesService.getInstance().asWhere(entity).asSqlParameters()); + doPreApply(entity, BaseDao::delete, false, EntityEventType.REMOVE); + } if (entity instanceof PostRemove) { try { ((PostRemove) entity).postRemove(); } catch (Exception ex) { - throw new PersistEventException(ex); + throw new RemoveEventException(ex); } } } @Override public T applyAndReturn(T entity) { - throw new UnsupportedOperationException("ApplyAndReturn is not implemented for PersistEvent"); + throw new UnsupportedOperationException("ApplyAndReturn is not implemented for RemoveEvent"); } } diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/UpdateEvent.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/UpdateEvent.java index ffd66e2b..d582fe58 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/UpdateEvent.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/relationship/UpdateEvent.java @@ -1,9 +1,10 @@ package io.github.ulisse1996.jaorm.entity.relationship; +import io.github.ulisse1996.jaorm.entity.EntityDelegate; import io.github.ulisse1996.jaorm.entity.event.PostUpdate; import io.github.ulisse1996.jaorm.entity.event.PreUpdate; import io.github.ulisse1996.jaorm.entity.sql.SqlParameter; -import io.github.ulisse1996.jaorm.exception.PersistEventException; +import io.github.ulisse1996.jaorm.exception.UpdateEventException; import io.github.ulisse1996.jaorm.spi.DelegatesService; import io.github.ulisse1996.jaorm.spi.QueryRunner; @@ -19,24 +20,27 @@ public void apply(T entity) { try { ((PreUpdate) entity).preUpdate(); } catch (Exception ex) { - throw new PersistEventException(ex); + throw new UpdateEventException(ex); } } doPreApply(entity, (dao, i) -> { dao.update(i); return QueryRunner.getInstance(i.getClass()).getUpdatedRows(i); - }, true); + }, true, EntityEventType.UPDATE); updateEntity(entity); if (entity instanceof PostUpdate) { try { ((PostUpdate) entity).postUpdate(); } catch (Exception ex) { - throw new PersistEventException(ex); + throw new UpdateEventException(ex); } } } public static void updateEntity(T entity) { + if (entity instanceof EntityDelegate && !((EntityDelegate) entity).isModified()) { + return; + } List parameterList = Stream.concat(DelegatesService.getInstance().asArguments(entity).asSqlParameters().stream(), DelegatesService.getInstance().asWhere(entity).asSqlParameters().stream()) @@ -49,6 +53,6 @@ public static void updateEntity(T entity) { @Override public T applyAndReturn(T entity) { - throw new UnsupportedOperationException("ApplyAndReturn is not implemented for PersistEvent"); + throw new UnsupportedOperationException("ApplyAndReturn is not implemented for UpdateEvent"); } } diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/sql/DataSourceProvider.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/sql/DataSourceProvider.java index 06b51456..55b5bac5 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/sql/DataSourceProvider.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/sql/DataSourceProvider.java @@ -8,35 +8,48 @@ import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; +import java.util.concurrent.locks.ReentrantLock; public abstract class DataSourceProvider { private static final Singleton INSTANCE = Singleton.instance(); private static final ThreadLocal TRANSACTION_INSTANCE = new InheritableThreadLocal<>(); + private static final ReentrantLock LOCK = new ReentrantLock(); + private static final ReentrantLock DELEGATE_LOCK = new ReentrantLock(); - public static synchronized DataSourceProvider getCurrent() { - BeanProvider provider = BeanProvider.getInstance(); + public static DataSourceProvider getCurrent() { + LOCK.lock(); + try { + BeanProvider provider = BeanProvider.getInstance(); - if (provider.isActive()) { - return provider.getBean(DataSourceProvider.class); - } + if (provider.isActive()) { + return provider.getBean(DataSourceProvider.class); + } - if (!INSTANCE.isPresent()) { - INSTANCE.set(ServiceFinder.loadService(DataSourceProvider.class)); + if (!INSTANCE.isPresent()) { + INSTANCE.set(ServiceFinder.loadService(DataSourceProvider.class)); + } + } finally { + LOCK.unlock(); } return INSTANCE.get(); } - public static synchronized DataSourceProvider getCurrentDelegate() { + public static DataSourceProvider getCurrentDelegate() { return TRANSACTION_INSTANCE.get(); } - public static synchronized void setDelegate(DataSourceProvider provider) { - if (provider == null) { - TRANSACTION_INSTANCE.remove(); - } else { - TRANSACTION_INSTANCE.set(provider); + public static void setDelegate(DataSourceProvider provider) { + DELEGATE_LOCK.lock(); + try { + if (provider == null) { + TRANSACTION_INSTANCE.remove(); + } else { + TRANSACTION_INSTANCE.set(provider); + } + } finally { + DELEGATE_LOCK.unlock(); } } diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/sql/SqlAccessor.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/sql/SqlAccessor.java index bd07e7d5..05f4e3c4 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/sql/SqlAccessor.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/sql/SqlAccessor.java @@ -62,6 +62,7 @@ public abstract class SqlAccessor { } }){}; public static final SqlAccessor NULL = new SqlAccessor(void.class, (rs, colName) -> null, (pr, index, val) -> pr.setNull(index, JDBCType.NULL.getVendorTypeNumber())){}; + public static final SqlAccessor UUID = new SqlAccessor(java.util.UUID.class, (rs, colName) -> java.util.UUID.fromString(rs.getString(colName)), (pr, index, value) -> pr.setString(index, value.toString())) {}; private static final List ALL = Arrays.asList( BYTE, BYTE_WRAPPER, SHORT, SHORT_WRAPPER, INTEGER, INTEGER_WRAPPER, @@ -69,7 +70,8 @@ public abstract class SqlAccessor { STRING, BOOLEAN, BOOLEAN_WRAPPER, ARRAY, STREAM, BLOB, BYTES, NCLOB, XML, TIME, TIMESTAMP, URL, DATE, DATE_UTIL, INSTANT, OFFSET_DATE_TIME, ZONED_DATE_TIME, LOCAL_DATE_TIME, - LOCAL_DATE, LOCAL_TIME, OFFSET_TIME, BIG_DECIMAL, BIG_INTEGER, NULL + LOCAL_DATE, LOCAL_TIME, OFFSET_TIME, BIG_DECIMAL, BIG_INTEGER, NULL, + UUID ); private final Class klass; diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/sql/SqlParameter.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/sql/SqlParameter.java index 1c5c26e4..4034463f 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/sql/SqlParameter.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/entity/sql/SqlParameter.java @@ -3,6 +3,7 @@ import io.github.ulisse1996.jaorm.entity.NullWrapper; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -47,4 +48,17 @@ public Object getVal() { public SqlSetter getAccessor() { return accessor; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SqlParameter parameter = (SqlParameter) o; + return Objects.equals(val, parameter.val) && Objects.equals(accessor, parameter.accessor); + } + + @Override + public int hashCode() { + return Objects.hash(val, accessor); + } } diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/exception/MergeEventException.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/exception/MergeEventException.java new file mode 100644 index 00000000..c7bcca90 --- /dev/null +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/exception/MergeEventException.java @@ -0,0 +1,10 @@ +package io.github.ulisse1996.jaorm.exception; + +import io.github.ulisse1996.jaorm.entity.relationship.EntityEventType; + +public class MergeEventException extends EntityEventException{ + + public MergeEventException(Exception ex) { + super(EntityEventType.MERGE, ex); + } +} diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/external/LombokSupport.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/external/LombokSupport.java index 93101848..cb02a67b 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/external/LombokSupport.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/external/LombokSupport.java @@ -5,18 +5,25 @@ import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; +import java.util.concurrent.locks.ReentrantLock; public abstract class LombokSupport { private static final Singleton INSTANCE = Singleton.instance(); + private static final ReentrantLock LOCK = new ReentrantLock(); - public static synchronized LombokSupport getInstance() { - if (!INSTANCE.isPresent()) { - try { - INSTANCE.set(ServiceFinder.loadService(LombokSupport.class)); - } catch (Exception ex) { - INSTANCE.set(NoOp.INSTANCE); + public static LombokSupport getInstance() { + LOCK.lock(); + try { + if (!INSTANCE.isPresent()) { + try { + INSTANCE.set(ServiceFinder.loadService(LombokSupport.class)); + } catch (Exception ex) { + INSTANCE.set(NoOp.INSTANCE); + } } + } finally { + LOCK.unlock(); } return INSTANCE.get(); diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/graph/GraphPair.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/graph/GraphPair.java index c3f93940..93dee472 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/graph/GraphPair.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/graph/GraphPair.java @@ -12,6 +12,10 @@ public GraphPair(Class entity, String name) { this.name = name; } + public Class getEntity() { + return entity; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spatial/Geography.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spatial/Geography.java index b5f04dde..5988a1f3 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spatial/Geography.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spatial/Geography.java @@ -45,4 +45,13 @@ public int getSrid() { public void setSrid(int srid) { this.srid = srid; } + + @Override + public String toString() { + return "Geography{" + + "latitude=" + latitude + + ", longitude=" + longitude + + ", srid=" + srid + + '}'; + } } diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/BeanProvider.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/BeanProvider.java index 2f5322b1..36013b5e 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/BeanProvider.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/BeanProvider.java @@ -3,21 +3,29 @@ import io.github.ulisse1996.jaorm.ServiceFinder; import io.github.ulisse1996.jaorm.spi.common.Singleton; +import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.concurrent.locks.ReentrantLock; import java.util.stream.StreamSupport; public abstract class BeanProvider { private static final Singleton INSTANCE = Singleton.instance(); + private static final ReentrantLock LOCK = new ReentrantLock(); - public static synchronized BeanProvider getInstance() { - if (!INSTANCE.isPresent()) { - INSTANCE.set( - StreamSupport.stream(ServiceFinder.loadServices(BeanProvider.class).spliterator(), false) - .findFirst() - .orElse(BeanProvider.NoOp.INSTANCE) - ); + public static BeanProvider getInstance() { + LOCK.lock(); + try { + if (!INSTANCE.isPresent() || FrameworkIntegrationService.isReloadRequired(Collections.singleton(INSTANCE.get().getClass()))) { + INSTANCE.set( + StreamSupport.stream(ServiceFinder.loadServices(BeanProvider.class).spliterator(), false) + .findFirst() + .orElse(BeanProvider.NoOp.INSTANCE) + ); + } + } finally { + LOCK.unlock(); } return INSTANCE.get(); diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/CacheService.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/CacheService.java index 75eee67a..1fc3a966 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/CacheService.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/CacheService.java @@ -10,6 +10,7 @@ import io.github.ulisse1996.jaorm.spi.provider.CacheActivator; import java.util.*; +import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; import java.util.stream.StreamSupport; @@ -17,8 +18,10 @@ public abstract class CacheService { private static final JaormLogger logger = JaormLogger.getLogger(CacheService.class); private static final Singleton INSTANCE = Singleton.instance(); + private static final ReentrantLock LOCK = new ReentrantLock(); - public static synchronized CacheService getInstance() { + public static CacheService getInstance() { + LOCK.lock(); try { BeanProvider provider = BeanProvider.getInstance(); @@ -48,6 +51,8 @@ public static synchronized CacheService getInstance() { } catch (IllegalArgumentException ex) { logger.debug(ex::getMessage); INSTANCE.set(NoOpCache.INSTANCE); + } finally { + LOCK.unlock(); } return INSTANCE.get(); diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/ConverterService.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/ConverterService.java index 9cc108e0..aa65dcf9 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/ConverterService.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/ConverterService.java @@ -10,15 +10,22 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReentrantLock; public abstract class ConverterService { private static final Singleton INSTANCE = Singleton.instance(); private final Map, SqlAccessor> cache = new ConcurrentHashMap<>(); + private static final ReentrantLock LOCK = new ReentrantLock(); - public static synchronized ConverterService getInstance() { - if (!INSTANCE.isPresent() || FrameworkIntegrationService.isReloadRequired(INSTANCE.get().getConverters().keySet())) { - INSTANCE.set(new DefaultConverters(ServiceFinder.loadServices(ConverterProvider.class))); + public static ConverterService getInstance() { + LOCK.lock(); + try { + if (!INSTANCE.isPresent() || FrameworkIntegrationService.isReloadRequired(INSTANCE.get().getConverters().keySet())) { + INSTANCE.set(new DefaultConverters(ServiceFinder.loadServices(ConverterProvider.class))); + } + } finally { + LOCK.unlock(); } return INSTANCE.get(); diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/DelegatesService.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/DelegatesService.java index 843c6217..9f3dc071 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/DelegatesService.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/DelegatesService.java @@ -8,16 +8,23 @@ import io.github.ulisse1996.jaorm.spi.impl.DefaultDelegates; import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; public abstract class DelegatesService { private static final Singleton INSTANCE = Singleton.instance(); + private static final ReentrantLock LOCK = new ReentrantLock(); - public static synchronized DelegatesService getInstance() { - if (!INSTANCE.isPresent() || FrameworkIntegrationService.isReloadRequired(INSTANCE.get().getDelegates().keySet())) { - @SuppressWarnings("rawtypes") Iterable delegates = ServiceFinder.loadServices(EntityDelegate.class); - INSTANCE.set(new DefaultDelegates(delegates)); + public static DelegatesService getInstance() { + LOCK.lock(); + try { + if (!INSTANCE.isPresent() || FrameworkIntegrationService.isReloadRequired(INSTANCE.get().getDelegates().keySet())) { + @SuppressWarnings("rawtypes") Iterable delegates = ServiceFinder.loadServices(EntityDelegate.class); + INSTANCE.set(new DefaultDelegates(delegates)); + } + } finally { + LOCK.unlock(); } return INSTANCE.get(); @@ -41,7 +48,7 @@ public Class getEntityClass(Class delegateClass) { .stream() .filter(el -> el.getKey().isAssignableFrom(delegateClass)) .findFirst() - .map(Map.Entry::getKey) + .map(e -> e.getValue().get().toTableInfo().getEntity()) .orElseThrow(() -> new IllegalArgumentException("Can't find real class from delegate " + delegateClass)); } } diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/EntityValidator.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/EntityValidator.java index 4b5ada1b..c9dbd055 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/EntityValidator.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/EntityValidator.java @@ -4,20 +4,28 @@ import io.github.ulisse1996.jaorm.entity.validation.ValidationResult; import io.github.ulisse1996.jaorm.spi.common.Singleton; +import java.util.Collections; import java.util.List; import java.util.ServiceConfigurationError; +import java.util.concurrent.locks.ReentrantLock; public abstract class EntityValidator { private static final Singleton INSTANCE = Singleton.instance(); - - public static synchronized EntityValidator getInstance() { - if (!INSTANCE.isPresent()) { - try { - INSTANCE.set(ServiceFinder.loadService(EntityValidator.class)); - } catch (Exception | ServiceConfigurationError ex) { - INSTANCE.set(NoOp.INSTANCE); + private static final ReentrantLock LOCK = new ReentrantLock(); + + public static EntityValidator getInstance() { + LOCK.lock(); + try { + if (!INSTANCE.isPresent() || FrameworkIntegrationService.isReloadRequired(Collections.singleton(INSTANCE.get().getClass()))) { + try { + INSTANCE.set(ServiceFinder.loadService(EntityValidator.class)); + } catch (Exception | ServiceConfigurationError ex) { + INSTANCE.set(NoOp.INSTANCE); + } } + } finally { + LOCK.unlock(); } return INSTANCE.get(); diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/ExternalSqlAccessorService.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/ExternalSqlAccessorService.java index 1185d7fc..8e31a665 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/ExternalSqlAccessorService.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/ExternalSqlAccessorService.java @@ -7,19 +7,26 @@ import java.util.Collections; import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; public abstract class ExternalSqlAccessorService { private static final Singleton INSTANCE = Singleton.instance(); - - public static synchronized ExternalSqlAccessorService getInstance() { - if (!INSTANCE.isPresent()) { - Iterable iterable = ServiceFinder.loadServices(SqlAccessor.class); - if (iterable.iterator().hasNext()) { - INSTANCE.set(new DefaultSqlAccessors(iterable)); - } else { - INSTANCE.set(ExternalSqlAccessorService.NoOp.INSTANCE); + private static final ReentrantLock LOCK = new ReentrantLock(); + + public static ExternalSqlAccessorService getInstance() { + LOCK.lock(); + try { + if (!INSTANCE.isPresent() || FrameworkIntegrationService.isReloadRequired(Collections.singleton(INSTANCE.get().getClass()))) { + Iterable iterable = ServiceFinder.loadServices(SqlAccessor.class); + if (iterable.iterator().hasNext()) { + INSTANCE.set(new DefaultSqlAccessors(iterable)); + } else { + INSTANCE.set(ExternalSqlAccessorService.NoOp.INSTANCE); + } } + } finally { + LOCK.unlock(); } return INSTANCE.get(); diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/FeatureConfigurator.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/FeatureConfigurator.java index 86a5cb55..67f83dd7 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/FeatureConfigurator.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/FeatureConfigurator.java @@ -3,24 +3,33 @@ import io.github.ulisse1996.jaorm.ServiceFinder; import io.github.ulisse1996.jaorm.spi.common.Singleton; +import java.util.Collections; +import java.util.concurrent.locks.ReentrantLock; + public abstract class FeatureConfigurator { private static final Singleton INSTANCE = Singleton.instance(); + private static final ReentrantLock LOCK = new ReentrantLock(); - public static synchronized FeatureConfigurator getInstance() { - BeanProvider provider = BeanProvider.getInstance(); + public static FeatureConfigurator getInstance() { + LOCK.lock(); + try { + BeanProvider provider = BeanProvider.getInstance(); - if (provider.isActive()) { - return provider.getOptBean(FeatureConfigurator.class) - .orElse(DefaultConfiguration.INSTANCE); - } + if (provider.isActive()) { + return provider.getOptBean(FeatureConfigurator.class) + .orElse(DefaultConfiguration.INSTANCE); + } - if (!INSTANCE.isPresent()) { - try { - INSTANCE.set(ServiceFinder.loadService(FeatureConfigurator.class)); - } catch (Exception ex) { - INSTANCE.set(DefaultConfiguration.INSTANCE); + if (!INSTANCE.isPresent() || FrameworkIntegrationService.isReloadRequired(Collections.singleton(INSTANCE.get().getClass()))) { + try { + INSTANCE.set(ServiceFinder.loadService(FeatureConfigurator.class)); + } catch (Exception ex) { + INSTANCE.set(DefaultConfiguration.INSTANCE); + } } + } finally { + LOCK.unlock(); } return INSTANCE.get(); diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/FrameworkIntegrationService.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/FrameworkIntegrationService.java index 5a88cd6d..bcacfc59 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/FrameworkIntegrationService.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/FrameworkIntegrationService.java @@ -4,19 +4,26 @@ import io.github.ulisse1996.jaorm.spi.common.Singleton; import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; import java.util.stream.StreamSupport; public abstract class FrameworkIntegrationService { private static final Singleton INSTANCE = Singleton.instance(); - - public static synchronized FrameworkIntegrationService getInstance() { - if (!INSTANCE.isPresent()) { - INSTANCE.set( - StreamSupport.stream(ServiceFinder.loadServices(FrameworkIntegrationService.class).spliterator(), false) - .findFirst() - .orElse(FrameworkIntegrationService.NoOp.INSTANCE) - ); + private static final ReentrantLock LOCK = new ReentrantLock(); + + public static FrameworkIntegrationService getInstance() { + LOCK.lock(); + try { + if (!INSTANCE.isPresent()) { + INSTANCE.set( + StreamSupport.stream(ServiceFinder.loadServices(FrameworkIntegrationService.class).spliterator(), false) + .findFirst() + .orElse(FrameworkIntegrationService.NoOp.INSTANCE) + ); + } + } finally { + LOCK.unlock(); } return INSTANCE.get(); diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/GeneratorsService.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/GeneratorsService.java index 0b3a20da..7c4ebfe8 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/GeneratorsService.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/GeneratorsService.java @@ -8,19 +8,26 @@ import java.sql.SQLException; import java.util.*; +import java.util.concurrent.locks.ReentrantLock; public abstract class GeneratorsService { private static final Singleton INSTANCE = Singleton.instance(); + private static final ReentrantLock LOCK = new ReentrantLock(); - public static synchronized GeneratorsService getInstance() { - if (!INSTANCE.isPresent() || FrameworkIntegrationService.isReloadRequired(INSTANCE.get().getGenerated().keySet())) { - Iterable iterable = ServiceFinder.loadServices(GeneratorProvider.class); - if (iterable.iterator().hasNext()) { - INSTANCE.set(new DefaultGenerators(iterable)); - } else { - INSTANCE.set(NoOp.INSTANCE); + public static GeneratorsService getInstance() { + LOCK.lock(); + try { + if (!INSTANCE.isPresent() || FrameworkIntegrationService.isReloadRequired(INSTANCE.get().getGenerated().keySet())) { + Iterable iterable = ServiceFinder.loadServices(GeneratorProvider.class); + if (iterable.iterator().hasNext()) { + INSTANCE.set(new DefaultGenerators(iterable)); + } else { + INSTANCE.set(NoOp.INSTANCE); + } } + } finally { + LOCK.unlock(); } return INSTANCE.get(); diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/GlobalEventListener.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/GlobalEventListener.java index b50fcd9e..e58627fb 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/GlobalEventListener.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/GlobalEventListener.java @@ -5,26 +5,34 @@ import io.github.ulisse1996.jaorm.exception.GlobalEventException; import io.github.ulisse1996.jaorm.spi.common.Singleton; +import java.util.Collections; import java.util.ServiceConfigurationError; +import java.util.concurrent.locks.ReentrantLock; public abstract class GlobalEventListener { private static final Singleton INSTANCE = Singleton.instance(); + private static final ReentrantLock LOCK = new ReentrantLock(); - public static synchronized GlobalEventListener getInstance() { - BeanProvider provider = BeanProvider.getInstance(); + public static GlobalEventListener getInstance() { + LOCK.lock(); + try { + BeanProvider provider = BeanProvider.getInstance(); - if (provider.isActive()) { - return provider.getOptBean(GlobalEventListener.class) - .orElse(NoOp.INSTANCE); - } + if (provider.isActive()) { + return provider.getOptBean(GlobalEventListener.class) + .orElse(NoOp.INSTANCE); + } - if (!INSTANCE.isPresent()) { - try { - INSTANCE.set(ServiceFinder.loadService(GlobalEventListener.class)); - } catch (Exception | ServiceConfigurationError ex) { - INSTANCE.set(NoOp.INSTANCE); + if (!INSTANCE.isPresent() || FrameworkIntegrationService.isReloadRequired(Collections.singleton(GlobalEventListener.class))) { + try { + INSTANCE.set(ServiceFinder.loadService(GlobalEventListener.class)); + } catch (Exception | ServiceConfigurationError ex) { + INSTANCE.set(NoOp.INSTANCE); + } } + } finally { + LOCK.unlock(); } return INSTANCE.get(); diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/GraphsService.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/GraphsService.java index 866669b9..b08573a8 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/GraphsService.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/GraphsService.java @@ -9,15 +9,24 @@ import java.util.Map; import java.util.Optional; +import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; public abstract class GraphsService { private static final Singleton INSTANCE = Singleton.instance(); - - public static synchronized GraphsService getInstance() { - if (!INSTANCE.isPresent()) { - Iterable providers = ServiceFinder.loadServices(GraphProvider.class); - INSTANCE.set(new DefaultGraphs(providers)); + private static final ReentrantLock LOCK = new ReentrantLock(); + + public static GraphsService getInstance() { + LOCK.lock(); + try { + if (!INSTANCE.isPresent() || FrameworkIntegrationService.isReloadRequired( + INSTANCE.get().getEntityGraphs().keySet().stream().map(GraphPair::getEntity).collect(Collectors.toSet()))) { + Iterable providers = ServiceFinder.loadServices(GraphProvider.class); + INSTANCE.set(new DefaultGraphs(providers)); + } + } finally { + LOCK.unlock(); } return INSTANCE.get(); diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/ListenersService.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/ListenersService.java index fed0c275..2c069c99 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/ListenersService.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/ListenersService.java @@ -10,43 +10,37 @@ import java.util.Collections; import java.util.ServiceConfigurationError; import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; public abstract class ListenersService { private static final Singleton INSTANCE = Singleton.instance(); - - public static synchronized ListenersService getInstance() { - if (!INSTANCE.isPresent() || FrameworkIntegrationService.isReloadRequired(INSTANCE.get().getEventClasses())) { - try { - Iterable iterable = ServiceFinder.loadServices(ListenerProvider.class); - if (iterable.iterator().hasNext()) { - INSTANCE.set(new DefaultListeners(iterable)); - } else { + private static final ReentrantLock LOCK = new ReentrantLock(); + + public static ListenersService getInstance() { + LOCK.lock(); + try { + if (!INSTANCE.isPresent() || FrameworkIntegrationService.isReloadRequired(INSTANCE.get().getEventClasses())) { + try { + Iterable iterable = ServiceFinder.loadServices(ListenerProvider.class); + if (iterable.iterator().hasNext()) { + INSTANCE.set(new DefaultListeners(iterable)); + } else { + INSTANCE.set(NoOp.INSTANCE); + } + } catch (Exception | ServiceConfigurationError ex) { INSTANCE.set(NoOp.INSTANCE); } - } catch (Exception | ServiceConfigurationError ex) { - INSTANCE.set(NoOp.INSTANCE); } + } finally { + LOCK.unlock(); } return INSTANCE.get(); } public void fireEvent(Object entity, GlobalEventType eventType) { - Class klass = entity.getClass(); - klass = getRealClass(klass); - Class finalKlass = klass; - if (getEventClasses().stream().anyMatch(el -> el.equals(finalKlass))) { - GlobalEventListener.getInstance().handleEvent(entity, eventType); - } - } - - private Class getRealClass(Class klass) { - if (EntityDelegate.class.isAssignableFrom(klass)) { - return DelegatesService.getInstance().getEntityClass(klass); - } - - return klass; + GlobalEventListener.getInstance().handleEvent(entity, eventType); } public abstract Set> getEventClasses(); diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/MetricsService.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/MetricsService.java index 18baceb4..974d3c06 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/MetricsService.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/MetricsService.java @@ -6,28 +6,35 @@ import io.github.ulisse1996.jaorm.spi.common.Singleton; import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; public abstract class MetricsService { private static final Singleton INSTANCE = Singleton.instance(); + private static final ReentrantLock LOCK = new ReentrantLock(); private MetricsService() {} - public static synchronized MetricsTracker getInstance() { - BeanProvider provider = BeanProvider.getInstance(); + public static MetricsTracker getInstance() { + LOCK.lock(); + try { + BeanProvider provider = BeanProvider.getInstance(); - if (provider.isActive()) { - return provider.getOptBean(MetricsTracker.class) - .orElse(MetricsService.NoOp.INSTANCE); - } + if (provider.isActive()) { + return provider.getOptBean(MetricsTracker.class) + .orElse(MetricsService.NoOp.INSTANCE); + } - if (!INSTANCE.isPresent() || FrameworkIntegrationService.isReloadRequired(Set.of(INSTANCE.get().getClass()))) { - Iterable trackers = ServiceFinder.loadServices(MetricsTracker.class); - if (trackers.iterator().hasNext()) { - INSTANCE.set(trackers.iterator().next()); - } else { - INSTANCE.set(NoOp.INSTANCE); + if (!INSTANCE.isPresent() || FrameworkIntegrationService.isReloadRequired(Set.of(INSTANCE.get().getClass()))) { + Iterable trackers = ServiceFinder.loadServices(MetricsTracker.class); + if (trackers.iterator().hasNext()) { + INSTANCE.set(trackers.iterator().next()); + } else { + INSTANCE.set(NoOp.INSTANCE); + } } + } finally { + LOCK.unlock(); } return INSTANCE.get(); diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/ProjectionsService.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/ProjectionsService.java index 9bb5a93b..7b38e451 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/ProjectionsService.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/ProjectionsService.java @@ -6,16 +6,24 @@ import io.github.ulisse1996.jaorm.spi.impl.DefaultProjections; import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; public abstract class ProjectionsService { private static final Singleton INSTANCE = Singleton.instance(); + private static final ReentrantLock LOCK = new ReentrantLock(); - public static synchronized ProjectionsService getInstance() { - if (!INSTANCE.isPresent() || FrameworkIntegrationService.isReloadRequired(INSTANCE.get().getProjections().keySet())) { - INSTANCE.set(new DefaultProjections(ServiceFinder.loadServices(ProjectionDelegate.class))); + public static ProjectionsService getInstance() { + LOCK.lock(); + try { + if (!INSTANCE.isPresent() || FrameworkIntegrationService.isReloadRequired(INSTANCE.get().getProjections().keySet())) { + INSTANCE.set(new DefaultProjections(ServiceFinder.loadServices(ProjectionDelegate.class))); + } + } finally { + LOCK.unlock(); } + return INSTANCE.get(); } diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/QueriesService.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/QueriesService.java index a711ad76..7ad515d3 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/QueriesService.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/QueriesService.java @@ -9,15 +9,22 @@ import io.github.ulisse1996.jaorm.spi.provider.QueryProvider; import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; public abstract class QueriesService { private static final Singleton INSTANCE = Singleton.instance(); + private static final ReentrantLock LOCK = new ReentrantLock(); - public static synchronized QueriesService getInstance() { - if (!INSTANCE.isPresent() || FrameworkIntegrationService.isReloadRequired(INSTANCE.get().getQueries().keySet())) { - INSTANCE.set(new DefaultQueries(ServiceFinder.loadServices(QueryProvider.class))); + public static QueriesService getInstance() { + try { + LOCK.lock(); + if (!INSTANCE.isPresent() || FrameworkIntegrationService.isReloadRequired(INSTANCE.get().getQueries().keySet())) { + INSTANCE.set(new DefaultQueries(ServiceFinder.loadServices(QueryProvider.class))); + } + } finally { + LOCK.unlock(); } return INSTANCE.get(); diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/QueryRunner.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/QueryRunner.java index 34a1d0db..5b1d86d7 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/QueryRunner.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/QueryRunner.java @@ -32,7 +32,7 @@ public abstract class QueryRunner { public static final SqlJaormLogger logger = JaormLogger.getSqlLogger(ResultSetExecutor.class); private static final Singleton ENTITY_RUNNER = Singleton.instance(); private static final Singleton SIMPLE_RUNNER = Singleton.instance(); - private static final ThreadLocal> UPDATED_ROWS_LOCAL = ThreadLocal.withInitial(HashMap::new); //NOSONAR + private static final ThreadLocal> UPDATED_ROWS_LOCAL = ThreadLocal.withInitial(WeakHashMap::new); //NOSONAR public static QueryRunner getInstance(Class klass) { if (!isDelegate(klass)) { @@ -255,6 +255,9 @@ private boolean isSchemaSupport(TableInfo tableInfo) { public void registerUpdatedRows(Object object, int rows) { UPDATED_ROWS_LOCAL.get().put(object, rows); + if (object instanceof EntityDelegate) { + UPDATED_ROWS_LOCAL.get().put(EntityDelegate.unboxEntity(object), rows); + } } public Integer getUpdatedRows(Object object) { diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/RelationshipService.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/RelationshipService.java index bb532b89..98d02efa 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/RelationshipService.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/RelationshipService.java @@ -8,14 +8,24 @@ import io.github.ulisse1996.jaorm.spi.impl.DefaultRelationships; import io.github.ulisse1996.jaorm.spi.provider.RelationshipProvider; +import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; + public abstract class RelationshipService { private static final Singleton INSTANCE = Singleton.instance(); - - public static synchronized RelationshipService getInstance() { - if (!INSTANCE.isPresent()) { - INSTANCE.set(new DefaultRelationships(ServiceFinder.loadServices(RelationshipProvider.class))); + private static final ReentrantLock LOCK = new ReentrantLock(); + + public static RelationshipService getInstance() { + LOCK.lock(); + try { + if (!INSTANCE.isPresent() || FrameworkIntegrationService.isReloadRequired(RelationshipService.INSTANCE.get().getAllRelationships().keySet())) { + INSTANCE.set(new DefaultRelationships(ServiceFinder.loadServices(RelationshipProvider.class))); + } + } finally { + LOCK.unlock(); } + return INSTANCE.get(); } @@ -36,4 +46,5 @@ public boolean isEventActive(Class entityClass, EntityEventType eventTyp } public abstract Relationship getRelationships(Class entityClass); + public abstract Map, Relationship> getAllRelationships(); //NOSONAR } diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/impl/DefaultRelationships.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/impl/DefaultRelationships.java index 5718c533..070649f0 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/impl/DefaultRelationships.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/spi/impl/DefaultRelationships.java @@ -38,4 +38,9 @@ public Relationship getRelationships(Class entityClass) { .map(Map.Entry::getValue) .orElse(null); } + + @Override + public Map, Relationship> getAllRelationships() { + return this.relationships; + } } diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/vendor/VendorFunctionWithAlias.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/vendor/VendorFunctionWithAlias.java index 0faa5589..37ef7c0b 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/vendor/VendorFunctionWithAlias.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/vendor/VendorFunctionWithAlias.java @@ -1,5 +1,6 @@ package io.github.ulisse1996.jaorm.vendor; +import java.util.Collections; import java.util.List; public class VendorFunctionWithAlias implements VendorFunctionWithParams { @@ -35,7 +36,7 @@ public List getParams() { if (function instanceof VendorFunctionWithParams) { return ((VendorFunctionWithParams) function).getParams(); } else { - throw new UnsupportedOperationException("Can't get params from unsupported function !"); + return Collections.emptyList(); } } diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/vendor/VendorSpecific.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/vendor/VendorSpecific.java index 0868b122..d3d063e7 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/vendor/VendorSpecific.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/vendor/VendorSpecific.java @@ -5,27 +5,34 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReentrantLock; public class VendorSpecific { private static final Map, Specific> SPECIFIC_MAP = new ConcurrentHashMap<>(); + private static final ReentrantLock LOCK = new ReentrantLock(); private VendorSpecific() {} - public static synchronized T getSpecific(Class specific) { + public static T getSpecific(Class specific) { return getSpecific(specific, null); } @SuppressWarnings("unchecked") - public static synchronized T getSpecific(Class klass, T defaultObj) { + public static T getSpecific(Class klass, T defaultObj) { if (SPECIFIC_MAP.containsKey(klass)) { return (T) SPECIFIC_MAP.get(klass); } - Iterable services = ServiceFinder.loadServices(klass); - if (services.iterator().hasNext()) { - SPECIFIC_MAP.put(klass, services.iterator().next()); - return (T) SPECIFIC_MAP.get(klass); + LOCK.lock(); + try { + Iterable services = ServiceFinder.loadServices(klass); + if (services.iterator().hasNext()) { + SPECIFIC_MAP.put(klass, services.iterator().next()); + return (T) SPECIFIC_MAP.get(klass); + } + } finally { + LOCK.unlock(); } if (defaultObj != null) { diff --git a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/vendor/specific/MergeSpecific.java b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/vendor/specific/MergeSpecific.java index f6af7a56..f3ab97f5 100644 --- a/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/vendor/specific/MergeSpecific.java +++ b/jaorm-core/src/main/java/io/github/ulisse1996/jaorm/vendor/specific/MergeSpecific.java @@ -13,7 +13,7 @@ import java.util.Map; import java.util.stream.Collectors; -public abstract class MergeSpecific implements Specific { +public abstract class MergeSpecific implements Specific { //NOSONAR public abstract String fromUsing(); public abstract String appendAdditionalSql(); diff --git a/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/BaseDaoTest.java b/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/BaseDaoTest.java index 5917666d..df92059e 100644 --- a/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/BaseDaoTest.java +++ b/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/BaseDaoTest.java @@ -2,7 +2,6 @@ import io.github.ulisse1996.jaorm.entity.Page; import io.github.ulisse1996.jaorm.entity.Result; -import io.github.ulisse1996.jaorm.entity.relationship.EntityEvent; import io.github.ulisse1996.jaorm.entity.relationship.EntityEventType; import io.github.ulisse1996.jaorm.entity.relationship.Relationship; import io.github.ulisse1996.jaorm.entity.sql.SqlParameter; @@ -17,7 +16,6 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.function.Executable; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mock; @@ -27,6 +25,7 @@ import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.stream.Stream; @@ -360,14 +359,6 @@ void should_do_correct_list_update_with_saved_update_row() { .thenReturn(delegates); mkRelationship.when(RelationshipService::getInstance) .thenReturn(relationshipService); - Mockito.when(delegates.getUpdateSql(Mockito.any())) - .thenReturn("TEST"); - Mockito.when(delegates.asArguments(Mockito.any())) - .thenReturn(Arguments.empty()); - Mockito.when(delegates.asWhere(Mockito.any())) - .thenReturn(Arguments.empty()); - Mockito.when(runner.update(Mockito.anyString(), Mockito.any())) - .thenReturn(1); Mockito.when(runner.getUpdatedRows(Mockito.any())).thenReturn(1); dao.update(Collections.singletonList(entity)); @@ -542,13 +533,13 @@ void should_insert_with_batch_and_relationships(int type) { .then(i -> mockDao); switch (type) { case 1: - relationship.add(new Relationship.Node<>(Object.class, e -> Result.empty(), true, false, EntityEventType.values())); + relationship.add(new Relationship.Node<>(Object.class, e -> Result.empty(), true, false, "name", Collections.emptyList(), EntityEventType.values())); break; case 2: - relationship.add(new Relationship.Node<>(Object.class, e -> Collections.emptyList(), false, true, EntityEventType.values())); + relationship.add(new Relationship.Node<>(Object.class, e -> Collections.emptyList(), false, true, "name", Collections.emptyList(), EntityEventType.values())); break; default: - relationship.add(new Relationship.Node<>(Object.class, e -> null, false, false, EntityEventType.values())); + relationship.add(new Relationship.Node<>(Object.class, e -> null, false, false, "name", Collections.emptyList(), EntityEventType.values())); break; } List results = new MyDao().insertWithBatch(entities); @@ -594,13 +585,13 @@ void should_update_with_batch_and_relationships(int type) { .then(i -> mockDao); switch (type) { case 1: - relationship.add(new Relationship.Node<>(Object.class, e -> Result.empty(), true, false, EntityEventType.values())); + relationship.add(new Relationship.Node<>(Object.class, e -> Result.empty(), true, false, "name", Collections.emptyList(), EntityEventType.values())); break; case 2: - relationship.add(new Relationship.Node<>(Object.class, e -> Collections.emptyList(), false, true, EntityEventType.values())); + relationship.add(new Relationship.Node<>(Object.class, e -> Collections.emptyList(), false, true, "name", Collections.emptyList(), EntityEventType.values())); break; default: - relationship.add(new Relationship.Node<>(Object.class, e -> null, false, false, EntityEventType.values())); + relationship.add(new Relationship.Node<>(Object.class, e -> null, false, false, "name", Collections.emptyList(), EntityEventType.values())); break; } List results = new MyDao().updateWithBatch(entities); @@ -638,52 +629,6 @@ void should_execute_a_batch_insert_without_relationships() { } } - @ParameterizedTest - @EnumSource(EntityEventType.class) - void should_apply_relationship_event(EntityEventType eventType) { - final MyDao dao = Mockito.spy(new MyDao()); - RelationshipService relationshipService = Mockito.mock(RelationshipService.class); - EntityEvent event = Mockito.mock(EntityEvent.class); - QueryRunner simpleRunner = Mockito.mock(QueryRunner.class); - QueryRunner entityRunner = Mockito.mock(QueryRunner.class); - try (MockedStatic mk = Mockito.mockStatic(RelationshipService.class); - MockedStatic mkEntity = Mockito.mockStatic(EntityEvent.class); - MockedStatic mkList = Mockito.mockStatic(ListenersService.class); - MockedStatic mkDelegate = Mockito.mockStatic(DelegatesService.class); - MockedStatic mkRunner = Mockito.mockStatic(QueryRunner.class)) { - mkRunner.when(QueryRunner::getSimple).thenReturn(simpleRunner); - mkRunner.when(() -> QueryRunner.getInstance(Mockito.any())).thenReturn(entityRunner); - mkDelegate.when(DelegatesService::getInstance).thenReturn(Mockito.mock(DelegatesService.class)); - mkList.when(ListenersService::getInstance).thenReturn(Mockito.mock(ListenersService.class)); - mk.when(RelationshipService::getInstance) - .thenReturn(relationshipService); - Mockito.when(relationshipService.isEventActive(DelegatesMock.MyEntity.class, eventType)) - .thenReturn(true); - mkEntity.when(() -> EntityEvent.forType(eventType)) - .thenReturn(event); - switch (eventType) { - case PERSIST: - dao.insert(new DelegatesMock.MyEntity()); - break; - case REMOVE: - dao.delete(new DelegatesMock.MyEntity()); - break; - case UPDATE: - Mockito.when(entityRunner.getUpdatedRows(Mockito.any())).thenReturn(1); - dao.update(new DelegatesMock.MyEntity()); - break; - } - - if (!EntityEventType.PERSIST.equals(eventType)) { - Mockito.verify(event, Mockito.times(1)) - .apply(Mockito.any()); - } else { - Mockito.verify(event, Mockito.times(1)) - .applyAndReturn(Mockito.any()); - } - } - } - @Test void should_throw_exception_for_validation_errors_during_insert() { final MyDao dao = Mockito.spy(new MyDao()); @@ -848,5 +793,10 @@ public boolean isEventActive(Class entityClass, EntityEventType eventType public Relationship getRelationships(Class entityClass) { return null; } + + @Override + public Map, Relationship> getAllRelationships() { + return null; + } } } diff --git a/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/DelegatesMock.java b/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/DelegatesMock.java index 776ae558..e75ba3ce 100644 --- a/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/DelegatesMock.java +++ b/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/DelegatesMock.java @@ -1,10 +1,13 @@ package io.github.ulisse1996.jaorm; import io.github.ulisse1996.jaorm.annotation.Table; +import io.github.ulisse1996.jaorm.entity.DirtinessTracker; import io.github.ulisse1996.jaorm.entity.EntityDelegate; import io.github.ulisse1996.jaorm.entity.EntityMapper; import io.github.ulisse1996.jaorm.entity.SqlColumn; import io.github.ulisse1996.jaorm.entity.event.*; +import io.github.ulisse1996.jaorm.entity.relationship.LazyEntityInfo; +import io.github.ulisse1996.jaorm.entity.relationship.RelationshipManager; import io.github.ulisse1996.jaorm.schema.TableInfo; import io.github.ulisse1996.jaorm.spi.DelegatesService; @@ -175,6 +178,11 @@ public boolean isModified() { return false; } + @Override + public void setModified(boolean modified) { + + } + @Override public boolean isDefaultGeneration() { return false; @@ -189,5 +197,30 @@ public MyEntity initDefault(MyEntity entity) { public TableInfo toTableInfo() { return new TableInfo("TAB", MyEntity.class, Table.UNSET); } + + @Override + public DirtinessTracker getTracker() { + return null; + } + + @Override + public boolean isLazyEntity() { + return false; + } + + @Override + public LazyEntityInfo getLazyInfo() { + return null; + } + + @Override + public void setLazyInfo(LazyEntityInfo info) { + + } + + @Override + public RelationshipManager getRelationshipManager() { + return null; + } } } diff --git a/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/DirtinessTrackerTest.java b/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/DirtinessTrackerTest.java new file mode 100644 index 00000000..64b5f540 --- /dev/null +++ b/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/DirtinessTrackerTest.java @@ -0,0 +1,70 @@ +package io.github.ulisse1996.jaorm.entity; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.stream.Stream; + +@ExtendWith(MockitoExtension.class) +class DirtinessTrackerTest { + + @Mock private EntityDelegate delegate; + + @Test + void should_return_same_delegate() { + Assertions.assertEquals(delegate, new DirtinessTracker<>(delegate).getDelegate()); + } + + @ParameterizedTest + @MethodSource("getResults") + void should_not_add_empty_or_null_result(Result result) { + DirtinessTracker tracker = new DirtinessTracker<>(delegate); + tracker.registerRemoved(result); + Assertions.assertTrue(tracker.getRemovedElements().isEmpty()); + } + + @Test + void should_return_only_remove_item() { + DirtinessTracker tracker = new DirtinessTracker<>(delegate); + tracker.registerRemoved(Result.of(delegate)); + Assertions.assertEquals(1, tracker.getRemovedElements().size()); + Assertions.assertEquals( + delegate, + tracker.getRemovedElements().get(0) + ); + } + + @Test + void should_return_empty_list_for_removed_object_without_delegate() { + DirtinessTracker tracker = new DirtinessTracker<>(delegate); + tracker.registerRemoved(new Object()); + Assertions.assertTrue(tracker.getRemovedElements().isEmpty()); + } + + @Test + void should_return_deleted_entity() { + EntityDelegate mock = Mockito.mock(EntityDelegate.class); + DirtinessTracker tracker = new DirtinessTracker<>(delegate); + tracker.registerRemoved(mock); + Assertions.assertEquals(1, tracker.getRemovedElements().size()); + Assertions.assertEquals( + mock, + tracker.getRemovedElements().get(0) + ); + } + + private static Stream getResults() { + return Stream.of( + Arguments.of((Object) null), + Arguments.of(Result.empty()), + Arguments.of(Result.of("S")) + ); + } +} \ No newline at end of file diff --git a/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/EntityDelegateTest.java b/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/EntityDelegateTest.java index c89011e0..4de02edf 100644 --- a/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/EntityDelegateTest.java +++ b/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/EntityDelegateTest.java @@ -50,6 +50,21 @@ void should_return_entity_from_delegate() { Assertions.assertSame(object, EntityDelegate.unboxEntity(delegate)); } + @Test + void should_unwrap_entity_from_delegate() { + EntityDelegate delegate = Mockito.mock(EntityDelegate.class); + Assertions.assertEquals( + delegate, + EntityDelegate.unwrap(delegate) + ); + } + + @Test + void should_throw_exception_for_bad_entity_unwrap() { + Assertions.assertThrows(UnsupportedOperationException.class, + () -> EntityDelegate.unwrap(new Object())); + } + @SuppressWarnings("unchecked") private EntityMapper prepare(ColumnSetter notKey, ColumnSetter key) { ColumnGetter getter = Mockito.mock(ColumnGetter.class); diff --git a/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/TrackedListTest.java b/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/TrackedListTest.java new file mode 100644 index 00000000..a96aa4e0 --- /dev/null +++ b/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/TrackedListTest.java @@ -0,0 +1,112 @@ +package io.github.ulisse1996.jaorm.entity; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@ExtendWith(MockitoExtension.class) +class TrackedListTest { + + @Mock private EntityDelegate delegate; + @Mock private DirtinessTracker tracker; + + @Test + void should_create_standard_tracked_list() { + TrackedList list = TrackedList.merge(delegate, null, new ArrayList<>()); + Assertions.assertEquals( + Collections.emptyList(), + list + ); + Assertions.assertEquals( + Collections.emptyList(), + list.getRemovedElements() + ); + } + + @Test + void should_create_tacked_list_from_standard_list() { + TrackedList list = TrackedList.merge(delegate, Collections.singletonList("1"), Collections.singletonList("2")); + Assertions.assertEquals( + Collections.singletonList("1"), + list.getRemovedElements() + ); + Assertions.assertEquals( + Collections.singletonList("2"), + list + ); + } + + @Test + void should_merge_previous_tracked_list() { + TrackedList list = new TrackedList<>(delegate, Collections.singletonList("1"), Collections.singletonList("2")); + TrackedList newList = TrackedList.merge(delegate, list, Collections.singletonList("3")); + Assertions.assertEquals( + List.of("2", "1"), + newList.getRemovedElements() + ); + Assertions.assertEquals(Collections.singletonList("3"), newList); + } + + @Test + void should_add_to_removed_for_set() { + TrackedList list = getNew("2"); + Object removed = list.set(0, "3"); + + Assertions.assertEquals("2", removed); + Assertions.assertEquals( + List.of("3"), + list + ); + Assertions.assertEquals( + List.of("2"), + list.getRemovedElements() + ); + } + + @Test + void should_add_element_to_delegate() { + TrackedList list = getNew("1"); + list.add("2"); + Assertions.assertEquals( + List.of("1", "2"), + list + ); + } + + @Test + void should_remove_element_and_add_it_to_removed_list() { + TrackedList list = getNew("1"); + Mockito.when(delegate.getTracker()).then(i -> tracker); + Object removed = list.remove(0); + Assertions.assertEquals("1", removed); + Assertions.assertEquals(List.of(), list); + Assertions.assertEquals(List.of("1"), list.getRemovedElements()); + } + + @Test + void should_return_same_hash_code() { + List l = List.of("2", "3", "4"); + Assertions.assertEquals( + l.hashCode(), + new TrackedList<>(delegate, l).hashCode() + ); + } + + @SuppressWarnings("SimplifiableAssertion") + @Test + void should_return_true_for_same_delegate_items() { + List l = List.of("2", "3", "4"); + Assertions.assertTrue(getNew("2", "3", "4").equals(l)); + } + + private TrackedList getNew(Object... elements) { + return new TrackedList<>(delegate, List.of(elements)); + } +} \ No newline at end of file diff --git a/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/converter/EnumConverterTest.java b/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/converter/EnumConverterTest.java new file mode 100644 index 00000000..1b8b1cd2 --- /dev/null +++ b/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/converter/EnumConverterTest.java @@ -0,0 +1,42 @@ +package io.github.ulisse1996.jaorm.entity.converter; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class EnumConverterTest { + + @Test + void should_convert_to_sql() { + Assertions.assertEquals("TEST", new MyEnumConverter().toSql(MyEnum.TEST)); + } + + @Test + void should_convert_to_sql_null() { + Assertions.assertNull(new MyEnumConverter().toSql(null)); + } + + @Test + void should_convert_to_enum() { + Assertions.assertSame(MyEnum.TEST, new MyEnumConverter().fromSql("TEST")); + } + + @Test + void should_throw_exception_for_missing_enum() { + MyEnumConverter converter = new MyEnumConverter(); + Assertions.assertThrows( + EnumConstantNotPresentException.class, + () -> converter.fromSql("NOT_MY_ENUM") + ); + } + + private enum MyEnum { + TEST + } + + private static class MyEnumConverter extends EnumConverter { + + public MyEnumConverter() { + super(MyEnum.class); + } + } +} \ No newline at end of file diff --git a/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/relationship/EntityEventTest.java b/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/relationship/EntityEventTest.java index 78874148..57cf685b 100644 --- a/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/relationship/EntityEventTest.java +++ b/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/relationship/EntityEventTest.java @@ -23,10 +23,10 @@ void should_return_real_class() { try (MockedStatic mk = Mockito.mockStatic(DelegatesService.class)) { mk.when(DelegatesService::getInstance) .thenReturn(delegatesService); - Mockito.when(delegatesService.getEntityClass(Mockito.any())) - .then(invocation -> Object.class); + Mockito.doReturn(Object.class) + .when(delegatesService).getEntityClass(Mockito.any()); - Assertions.assertEquals(Object.class, EntityEvent.forType(EntityEventType.PERSIST).getRealClass(Object.class)); + Assertions.assertEquals(Object.class, EntityEvent.getRealClass(Object.class)); } } } diff --git a/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/relationship/EventTest.java b/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/relationship/EventTest.java index 965a99eb..6fd3e989 100644 --- a/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/relationship/EventTest.java +++ b/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/relationship/EventTest.java @@ -1,10 +1,7 @@ package io.github.ulisse1996.jaorm.entity.relationship; import io.github.ulisse1996.jaorm.MockedProvider; -import io.github.ulisse1996.jaorm.entity.EntityDelegate; -import io.github.ulisse1996.jaorm.entity.EntityMapper; -import io.github.ulisse1996.jaorm.entity.Result; -import io.github.ulisse1996.jaorm.entity.SqlColumn; +import io.github.ulisse1996.jaorm.entity.*; import io.github.ulisse1996.jaorm.schema.TableInfo; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.provider.Arguments; @@ -119,6 +116,11 @@ public boolean isModified() { return false; } + @Override + public void setModified(boolean modified) { + + } + @Override public boolean isDefaultGeneration() { return false; @@ -133,17 +135,42 @@ public Entity initDefault(Entity entity) { public TableInfo toTableInfo() { return null; } + + @Override + public DirtinessTracker getTracker() { + return null; + } + + @Override + public boolean isLazyEntity() { + return false; + } + + @Override + public LazyEntityInfo getLazyInfo() { + return null; + } + + @Override + public void setLazyInfo(LazyEntityInfo info) { + + } + + @Override + public RelationshipManager getRelationshipManager() { + return null; + } } protected static class RelEntity {} protected static Stream getRelationship() { Relationship tree1 = new Relationship<>(Entity.class); - tree1.add(new Relationship.Node<>(RelEntity.class, Entity::getRelEntity, false, false, EntityEventType.values())); + tree1.add(new Relationship.Node<>(RelEntity.class, Entity::getRelEntity, false, false, "name", Collections.emptyList(), EntityEventType.values())); Relationship tree2 = new Relationship<>(Entity.class); - tree2.add(new Relationship.Node<>(RelEntity.class, Entity::getRelEntityOpt, true, false, EntityEventType.values())); + tree2.add(new Relationship.Node<>(RelEntity.class, Entity::getRelEntityOpt, true, false, "name", Collections.emptyList(), EntityEventType.values())); Relationship tree3 = new Relationship<>(Entity.class); - tree3.add(new Relationship.Node<>(RelEntity.class, Entity::getRelEntityColl, false, true, EntityEventType.values())); + tree3.add(new Relationship.Node<>(RelEntity.class, Entity::getRelEntityColl, false, true, "name", Collections.emptyList(), EntityEventType.values())); return Stream.of( Arguments.arguments(tree1), Arguments.arguments(tree2), diff --git a/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/relationship/LazyDeleteEventTest.java b/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/relationship/LazyDeleteEventTest.java new file mode 100644 index 00000000..af3d30b1 --- /dev/null +++ b/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/relationship/LazyDeleteEventTest.java @@ -0,0 +1,60 @@ +package io.github.ulisse1996.jaorm.entity.relationship; + +import io.github.ulisse1996.jaorm.entity.EntityDelegate; +import io.github.ulisse1996.jaorm.entity.sql.SqlParameter; +import io.github.ulisse1996.jaorm.spi.DelegatesService; +import io.github.ulisse1996.jaorm.spi.QueryRunner; +import io.github.ulisse1996.jaorm.spi.RelationshipService; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Collections; +import java.util.List; +import java.util.function.Function; +import java.util.function.Supplier; + +@ExtendWith(MockitoExtension.class) +class LazyDeleteEventTest { + + @Mock private EntityDelegate delegate; + @Mock private EntityDelegate otherDelegate; + @Mock private RelationshipManager manager; + @Mock private DelegatesService delegatesService; + @Mock private RelationshipService relationshipService; + @Mock private RelationshipManager.RelationshipInfo info; + @Mock private Relationship.Node node; + @Mock private QueryRunner simple; + + @Test + void should_do_simple_lazy_delete() { + Function fn = (t) -> 0; + try (MockedStatic mk = Mockito.mockStatic(QueryRunner.class); + MockedStatic mkDel = Mockito.mockStatic(DelegatesService.class); + MockedStatic mkRel = Mockito.mockStatic(RelationshipService.class)) { + mk.when(QueryRunner::getSimple).thenReturn(simple); + mkDel.when(DelegatesService::getInstance).thenReturn(delegatesService); + mkRel.when(RelationshipService::getInstance).thenReturn(relationshipService); + + Mockito.when(node.getLinkedClass()).then(i -> Object.class); + Mockito.when(node.getName()).thenReturn("NAME"); + Mockito.when(delegate.getRelationshipManager()) + .then(i -> manager); + Mockito.when(manager.getRelationshipInfo("NAME")).then(i -> info); + Mockito.when(info.getParameters()) + .then(i -> List.of(fn)); + Mockito.when(info.getWhere()).thenReturn("WHERE EL = ?"); + Mockito.when(delegatesService.searchDelegate(Object.class)) + .then(i -> (Supplier>) () -> otherDelegate); + Mockito.when(otherDelegate.getTable()).thenReturn("OTHER_TABLE"); + + new LazyDeleteEvent().apply(delegate, node); + + Mockito.verify(simple) + .delete("DELETE FROM OTHER_TABLE WHERE EL = ?", Collections.singletonList(new SqlParameter(0))); + } + } +} \ No newline at end of file diff --git a/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/relationship/RemoveEventTest.java b/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/relationship/RemoveEventTest.java index 4d2861eb..bcfd94a2 100644 --- a/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/relationship/RemoveEventTest.java +++ b/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/relationship/RemoveEventTest.java @@ -4,7 +4,7 @@ import io.github.ulisse1996.jaorm.BaseDao; import io.github.ulisse1996.jaorm.entity.event.PostRemove; import io.github.ulisse1996.jaorm.entity.event.PreRemove; -import io.github.ulisse1996.jaorm.exception.PersistEventException; +import io.github.ulisse1996.jaorm.exception.RemoveEventException; import io.github.ulisse1996.jaorm.spi.DelegatesService; import io.github.ulisse1996.jaorm.spi.QueriesService; import io.github.ulisse1996.jaorm.spi.QueryRunner; @@ -130,8 +130,6 @@ void should_apply_pre_remove() throws Exception { .thenReturn(Arguments.empty()); Mockito.when(relationshipService.getRelationships(Mockito.any())) .then(onMock -> fakeRel); - Mockito.doNothing() - .when(subject).doPreApply(Mockito.any(), Mockito.any(), Mockito.anyBoolean()); subject.apply(mock); Mockito.verify(mock).preRemove(); } @@ -158,8 +156,6 @@ void should_apply_post_remove() throws Exception { .thenReturn(Arguments.empty()); Mockito.when(relationshipService.getRelationships(Mockito.any())) .then(onMock -> fakeRel); - Mockito.doNothing() - .when(subject).doPreApply(Mockito.any(), Mockito.any(), Mockito.anyBoolean()); subject.apply(mock); Mockito.verify(mock).postRemove(); } @@ -188,9 +184,7 @@ void should_throw_exception_for_pre_remove() throws Exception { .then(onMock -> fakeRel); Mockito.doThrow(Exception.class) .when(mock).preRemove(); - Mockito.doNothing() - .when(subject).doPreApply(Mockito.any(), Mockito.any(), Mockito.anyBoolean()); - Assertions.assertThrows(PersistEventException.class, () -> subject.apply(mock)); + Assertions.assertThrows(RemoveEventException.class, () -> subject.apply(mock)); } } @@ -217,9 +211,7 @@ void should_throw_exception_for_post_remove() throws Exception { .then(onMock -> fakeRel); Mockito.doThrow(Exception.class) .when(mock).postRemove(); - Mockito.doNothing() - .when(subject).doPreApply(Mockito.any(), Mockito.any(), Mockito.anyBoolean()); - Assertions.assertThrows(PersistEventException.class, () -> subject.apply(mock)); + Assertions.assertThrows(RemoveEventException.class, () -> subject.apply(mock)); } } } diff --git a/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/relationship/UpdateEventTest.java b/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/relationship/UpdateEventTest.java index 2d276b3e..4838b1ef 100644 --- a/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/relationship/UpdateEventTest.java +++ b/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/entity/relationship/UpdateEventTest.java @@ -6,6 +6,7 @@ import io.github.ulisse1996.jaorm.entity.event.PostUpdate; import io.github.ulisse1996.jaorm.entity.event.PreUpdate; import io.github.ulisse1996.jaorm.exception.PersistEventException; +import io.github.ulisse1996.jaorm.exception.UpdateEventException; import io.github.ulisse1996.jaorm.logger.JaormLoggerHandler; import io.github.ulisse1996.jaorm.spi.DelegatesService; import io.github.ulisse1996.jaorm.spi.QueriesService; @@ -18,6 +19,7 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; +import java.util.Collections; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; @@ -95,8 +97,6 @@ void should_apply_pre_update() throws Exception { .thenReturn(Arguments.empty()); Mockito.when(relationshipService.getRelationships(Mockito.any())) .then(onMock -> fakeRel); - Mockito.doNothing() - .when(subject).doPreApply(Mockito.any(), Mockito.any(), Mockito.anyBoolean()); subject.apply(mock); Mockito.verify(mock).preUpdate(); } @@ -125,8 +125,6 @@ void should_apply_post_update() throws Exception { .thenReturn(Arguments.empty()); Mockito.when(relationshipService.getRelationships(Mockito.any())) .then(onMock -> fakeRel); - Mockito.doNothing() - .when(subject).doPreApply(Mockito.any(), Mockito.any(), Mockito.anyBoolean()); subject.apply(mock); Mockito.verify(mock).postUpdate(); } @@ -157,9 +155,7 @@ void should_throw_exception_for_pre_update() throws Exception { .then(onMock -> fakeRel); Mockito.doThrow(Exception.class) .when(mock).preUpdate(); - Mockito.doNothing() - .when(subject).doPreApply(Mockito.any(), Mockito.any(), Mockito.anyBoolean()); - Assertions.assertThrows(PersistEventException.class, () -> subject.apply(mock)); + Assertions.assertThrows(UpdateEventException.class, () -> subject.apply(mock)); } } @@ -167,9 +163,9 @@ void should_throw_exception_for_pre_update() throws Exception { @Test void should_do_insert_for_update_with_0_rows() { Relationship relationship = new Relationship<>(Entity.class); - relationship.add(new Relationship.Node<>(RelEntity.class, Entity::getRelEntity, false, false, EntityEventType.values())); - relationship.add(new Relationship.Node<>(RelEntity.class, Entity::getRelEntityOpt, true, false, EntityEventType.values())); - relationship.add(new Relationship.Node<>(RelEntity.class, Entity::getRelEntityColl, false, true, EntityEventType.values())); + relationship.add(new Relationship.Node<>(RelEntity.class, Entity::getRelEntity, false, false, "name", Collections.emptyList(), EntityEventType.values())); + relationship.add(new Relationship.Node<>(RelEntity.class, Entity::getRelEntityOpt, true, false, "name",Collections.emptyList(), EntityEventType.values())); + relationship.add(new Relationship.Node<>(RelEntity.class, Entity::getRelEntityColl, false, true, "name",Collections.emptyList(), EntityEventType.values())); MyEntityDelegate delegate = new MyEntityDelegate(); DelegatesService delegatedMock = Mockito.mock(DelegatesService.class); QueryRunner mockRunner = Mockito.mock(QueryRunner.class); @@ -213,9 +209,9 @@ void should_do_insert_for_update_with_0_rows() { @Test void should_not_insert_for_update_with_0_rows() { Relationship relationship = new Relationship<>(Entity.class); - relationship.add(new Relationship.Node<>(RelEntity.class, Entity::getRelEntity, false, false, EntityEventType.UPDATE)); - relationship.add(new Relationship.Node<>(RelEntity.class, Entity::getRelEntityOpt, true, false, EntityEventType.UPDATE)); - relationship.add(new Relationship.Node<>(RelEntity.class, Entity::getRelEntityColl, false, true, EntityEventType.UPDATE)); + relationship.add(new Relationship.Node<>(RelEntity.class, Entity::getRelEntity, false, false, "name", Collections.emptyList(), EntityEventType.UPDATE)); + relationship.add(new Relationship.Node<>(RelEntity.class, Entity::getRelEntityOpt, true, false, "name",Collections.emptyList(), EntityEventType.UPDATE)); + relationship.add(new Relationship.Node<>(RelEntity.class, Entity::getRelEntityColl, false, true, "name",Collections.emptyList(), EntityEventType.UPDATE)); MyEntityDelegate delegate = new MyEntityDelegate(); DelegatesService delegatedMock = Mockito.mock(DelegatesService.class); QueryRunner mockRunner = Mockito.mock(QueryRunner.class); @@ -291,9 +287,7 @@ void should_throw_exception_for_post_update() throws Exception { .then(onMock -> fakeRel); Mockito.doThrow(Exception.class) .when(mock).postUpdate(); - Mockito.doNothing() - .when(subject).doPreApply(Mockito.any(), Mockito.any(), Mockito.anyBoolean()); - Assertions.assertThrows(PersistEventException.class, () -> subject.apply(mock)); + Assertions.assertThrows(UpdateEventException.class, () -> subject.apply(mock)); } } } diff --git a/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/spi/RelationshipServiceTest.java b/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/spi/RelationshipServiceTest.java index 93beac03..03a87f9e 100644 --- a/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/spi/RelationshipServiceTest.java +++ b/jaorm-core/src/test/java/io/github/ulisse1996/jaorm/spi/RelationshipServiceTest.java @@ -1,10 +1,13 @@ package io.github.ulisse1996.jaorm.spi; +import io.github.ulisse1996.jaorm.entity.DirtinessTracker; import io.github.ulisse1996.jaorm.entity.EntityDelegate; import io.github.ulisse1996.jaorm.entity.EntityMapper; import io.github.ulisse1996.jaorm.entity.SqlColumn; import io.github.ulisse1996.jaorm.entity.relationship.EntityEventType; +import io.github.ulisse1996.jaorm.entity.relationship.LazyEntityInfo; import io.github.ulisse1996.jaorm.entity.relationship.Relationship; +import io.github.ulisse1996.jaorm.entity.relationship.RelationshipManager; import io.github.ulisse1996.jaorm.schema.TableInfo; import io.github.ulisse1996.jaorm.spi.common.Singleton; import org.junit.jupiter.api.Assertions; @@ -20,6 +23,7 @@ import java.math.BigDecimal; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.function.BiPredicate; @@ -150,6 +154,11 @@ public boolean isModified() { return false; } + @Override + public void setModified(boolean modified) { + + } + @Override public boolean isDefaultGeneration() { return false; @@ -164,6 +173,31 @@ public String initDefault(String entity) { public TableInfo toTableInfo() { return null; } + + @Override + public DirtinessTracker getTracker() { + return null; + } + + @Override + public boolean isLazyEntity() { + return false; + } + + @Override + public LazyEntityInfo getLazyInfo() { + return null; + } + + @Override + public void setLazyInfo(LazyEntityInfo info) { + + } + + @Override + public RelationshipManager getRelationshipManager() { + return null; + } } private static class RelationshipMock extends RelationshipService { @@ -173,7 +207,7 @@ private static class RelationshipMock extends RelationshipService { public RelationshipMock() { this.map = new HashMap<>(); Relationship stringRelationshipTree = new Relationship<>(String.class); - stringRelationshipTree.add(new Relationship.Node<>(String.class, e -> "", false, false, EntityEventType.PERSIST)); + stringRelationshipTree.add(new Relationship.Node<>(String.class, e -> "", false, false, "name", Collections.emptyList(), EntityEventType.PERSIST)); Relationship bigDecimalRelationshipTree = new Relationship<>(BigDecimal.class); this.map.put(stringRelationshipTree.getEntityClass(), stringRelationshipTree); this.map.put(bigDecimalRelationshipTree.getEntityClass(), bigDecimalRelationshipTree); @@ -184,5 +218,10 @@ public RelationshipMock() { public Relationship getRelationships(Class entityClass) { return (Relationship) map.get(entityClass); } + + @Override + public Map, Relationship> getAllRelationships() { + return map; + } } } diff --git a/jaorm-dsl/pom.xml b/jaorm-dsl/pom.xml index 2b468cf9..1abb1643 100644 --- a/jaorm-dsl/pom.xml +++ b/jaorm-dsl/pom.xml @@ -5,7 +5,7 @@ jaorm-pom io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/QueryBuilder.java b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/QueryBuilder.java index 8f0ec6ff..bfbc595a 100644 --- a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/QueryBuilder.java +++ b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/QueryBuilder.java @@ -28,6 +28,10 @@ public static Selected select(Class klass) { return select(klass, QueryConfig.builder().build()); } + public static Selected selectDistinct(Class klass) { + return selectDistinct(klass, QueryConfig.builder().build()); + } + public static Selected select(Class klass, boolean caseInsensitiveLike) { QueryConfig.Builder builder = QueryConfig.builder(); if (caseInsensitiveLike) { @@ -36,17 +40,30 @@ public static Selected select(Class klass, boolean caseInsensitiveLike return select(klass, builder.build()); } + public static Selected selectDistinct(Class klass, QueryConfig config) { + Objects.requireNonNull(klass, ENTITY_CLASS_CAN_T_BE_NULL); + return new SelectedImpl<>(klass, config, true); + } + public static Selected select(Class klass, QueryConfig config) { Objects.requireNonNull(klass, ENTITY_CLASS_CAN_T_BE_NULL); - return new SelectedImpl<>(klass, config); + return new SelectedImpl<>(klass, config, false); } public static Selected subQuery(SqlColumn column) { return subQuery(column, QueryConfig.builder().build()); } + public static Selected subQueryDistinct(SqlColumn column) { + return subQueryDistinct(column, QueryConfig.builder().build()); + } + + public static Selected subQueryDistinct(SqlColumn column, QueryConfig config) { + return new SubQueryImpl<>(column, config, true); + } + public static Selected subQuery(SqlColumn column, QueryConfig config) { - return new SubQueryImpl<>(column, config); + return new SubQueryImpl<>(column, config, false); } public static Inserted insertInto(Class klass) { @@ -76,24 +93,44 @@ public static SimpleSelected select(Selectable... selectables) { return new SimpleSelectedImpl( Arrays.stream(selectables) .map(el -> new AliasColumn(el, null)) - .collect(Collectors.toList()) + .collect(Collectors.toList()), + false + ); + } + + public static SimpleSelected selectDistinct(Selectable... selectables) { + return new SimpleSelectedImpl( + Arrays.stream(selectables) + .map(el -> new AliasColumn(el, null)) + .collect(Collectors.toList()), + true ); } public static IntermediateSelected select(Selectable selectable) { - return new IntermediateSelected(selectable, null); + return new IntermediateSelected(selectable, null, false); } public static IntermediateSelected select(Selectable selectable, String alias) { - return new IntermediateSelected(selectable, alias); + return new IntermediateSelected(selectable, alias, false); + } + + public static IntermediateSelected selectDistinct(Selectable selectable) { + return new IntermediateSelected(selectable, null, true); + } + + public static IntermediateSelected selectDistinct(Selectable selectable, String alias) { + return new IntermediateSelected(selectable, alias, true); } public static class IntermediateSelected implements SimpleSelected { private final List selectables; private QueryConfig config; + private final boolean distinct; - private IntermediateSelected(Selectable selectable, String alias) { + private IntermediateSelected(Selectable selectable, String alias, boolean distinct) { + this.distinct = distinct; this.selectables = new ArrayList<>(); this.selectables.add(new AliasColumn(selectable, alias)); } @@ -120,7 +157,7 @@ public FromSimpleSelected from(String table) { @Override public FromSimpleSelected from(String table, String alias) { - SimpleSelected selected = new SimpleSelectedImpl(this.selectables); + SimpleSelected selected = new SimpleSelectedImpl(this.selectables, this.distinct); if (this.config != null) { selected = selected.withConfiguration(config); } diff --git a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/common/intermediate/IntermediateJoin.java b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/common/intermediate/IntermediateJoin.java index 2a779247..194cb5fa 100644 --- a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/common/intermediate/IntermediateJoin.java +++ b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/common/intermediate/IntermediateJoin.java @@ -1,7 +1,6 @@ package io.github.ulisse1996.jaorm.dsl.query.common.intermediate; import io.github.ulisse1996.jaorm.dsl.query.common.SelectedOn; -import io.github.ulisse1996.jaorm.dsl.query.enums.LikeType; import io.github.ulisse1996.jaorm.entity.SqlColumn; public interface IntermediateJoin { diff --git a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/common/intermediate/IntermediateWhereCondition.java b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/common/intermediate/IntermediateWhereCondition.java index 78f657a2..2c2f90ac 100644 --- a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/common/intermediate/IntermediateWhereCondition.java +++ b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/common/intermediate/IntermediateWhereCondition.java @@ -1,7 +1,6 @@ package io.github.ulisse1996.jaorm.dsl.query.common.intermediate; import io.github.ulisse1996.jaorm.dsl.query.common.SelectedWhere; -import io.github.ulisse1996.jaorm.dsl.query.enums.LikeType; public interface IntermediateWhereCondition { diff --git a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/common/trait/WithResult.java b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/common/trait/WithResult.java index 77ffc6f0..c917f97d 100644 --- a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/common/trait/WithResult.java +++ b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/common/trait/WithResult.java @@ -8,5 +8,7 @@ public interface WithResult { T read(); Optional readOpt(); List readAll(); + long count(); + boolean hasResults(); WithResult union(WithResult union); } diff --git a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/impl/SelectedImpl.java b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/impl/SelectedImpl.java index cdd2a7dd..46d59ff6 100644 --- a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/impl/SelectedImpl.java +++ b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/impl/SelectedImpl.java @@ -41,18 +41,19 @@ public class SelectedImpl extends AbstractLimitOffsetImpl private final List orders; private final boolean caseInsensitiveLike; private final List> unions; + protected final boolean distinct; private WhereChecker checker; private WhereImpl lastWhere; private JoinImpl lastJoin; private int limit; private int offset; - public SelectedImpl(Class klass, QueryConfig config) { - this(klass, config.isCaseInsensitive()); + public SelectedImpl(Class klass, QueryConfig config, boolean distinct) { + this(klass, config.isCaseInsensitive(), distinct); this.checker = config.getChecker(); } - public SelectedImpl(Class klass, boolean caseInsensitiveLike) { + public SelectedImpl(Class klass, boolean caseInsensitiveLike, boolean distinct) { EntityDelegate delegate = DelegatesService.getInstance() .searchDelegate(klass).get(); this.klass = klass; @@ -64,6 +65,7 @@ public SelectedImpl(Class klass, boolean caseInsensitiveLike) { this.orders = new ArrayList<>(); this.unions = new ArrayList<>(); this.checker = new DefaultWhereChecker(); + this.distinct = distinct; } public boolean isCaseInsensitiveLike() { @@ -114,6 +116,11 @@ public long count() { return QueryRunner.getSimple().read(long.class, pair.getKey(), pair.getValue()); } + @Override + public boolean hasResults() { + return count() > 0; + } + public List getParameters() { Stream joinParams = this.joins.stream().flatMap(JoinImpl::getParameters); Stream wheresParams = this.wheres.stream().flatMap(m -> m.getParameters(this.caseInsensitiveLike)); @@ -354,6 +361,7 @@ private Pair> doBuild(boolean count) { public String asString(boolean count) { StringBuilder builder = new StringBuilder("SELECT ") + .append(distinct && !count ? "DISTINCT " : "") .append(count ? createCount() : asSelectColumns()) .append("FROM ") .append(this.table); diff --git a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/impl/SubQueryImpl.java b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/impl/SubQueryImpl.java index 66a0fb7a..9c95d3bd 100644 --- a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/impl/SubQueryImpl.java +++ b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/impl/SubQueryImpl.java @@ -9,14 +9,15 @@ public class SubQueryImpl extends SelectedImpl implements WithSubQue private final String colum; @SuppressWarnings("unchecked") - public SubQueryImpl(SqlColumn column, QueryConfig config) { - super((Class) column.getEntity(), config); + public SubQueryImpl(SqlColumn column, QueryConfig config, boolean distinct) { + super((Class) column.getEntity(), config, distinct); this.colum = column.getName(); } @Override public String getSql() { StringBuilder builder = new StringBuilder("SELECT ") + .append(this.distinct ? "DISTINCT " : "") .append(this.table) .append(".") .append(colum) diff --git a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/impl/simple/AliasColumn.java b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/impl/simple/AliasColumn.java index 757ff304..c565f5a1 100644 --- a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/impl/simple/AliasColumn.java +++ b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/impl/simple/AliasColumn.java @@ -1,5 +1,6 @@ package io.github.ulisse1996.jaorm.dsl.query.impl.simple; +import io.github.ulisse1996.jaorm.InlineValue; import io.github.ulisse1996.jaorm.Selectable; import io.github.ulisse1996.jaorm.dsl.util.Checker; import io.github.ulisse1996.jaorm.entity.SqlColumn; @@ -10,21 +11,32 @@ public class AliasColumn { private final SqlColumn column; private final String tableAlias; private final VendorFunction function; + private final InlineValue inlineValue; public AliasColumn(Selectable selectable, String alias) { Checker.assertNotNull(selectable, "selectable"); if (selectable instanceof SqlColumn) { this.column = (SqlColumn) selectable; this.function = null; - } else if (selectable instanceof VendorFunction){ + this.inlineValue = null; + } else if (selectable instanceof VendorFunction) { this.function = (VendorFunction) selectable; this.column = null; + this.inlineValue = null; + } else if (selectable instanceof InlineValue) { + this.inlineValue = (InlineValue) selectable; + this.column = null; + this.function = null; } else { throw new IllegalArgumentException(String.format("Invalid type %s for selectable", selectable.getClass())); } this.tableAlias = alias; } + public InlineValue getInlineValue() { //NOSONAR + return inlineValue; + } + public VendorFunction getFunction() { //NOSONAR return function; } diff --git a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/impl/simple/SimpleJoinImpl.java b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/impl/simple/SimpleJoinImpl.java index 6f1b10b7..fc97bcec 100644 --- a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/impl/simple/SimpleJoinImpl.java +++ b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/impl/simple/SimpleJoinImpl.java @@ -6,7 +6,7 @@ import io.github.ulisse1996.jaorm.dsl.query.enums.Operation; import io.github.ulisse1996.jaorm.dsl.query.enums.OrderType; import io.github.ulisse1996.jaorm.dsl.query.simple.intermediate.*; -import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithProjectionResult; +import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithResult; import io.github.ulisse1996.jaorm.dsl.util.Pair; import io.github.ulisse1996.jaorm.entity.SqlColumn; import io.github.ulisse1996.jaorm.entity.sql.SqlParameter; @@ -203,7 +203,7 @@ public List readAll(Class klass) { } @Override - public WithProjectionResult union(WithProjectionResult union) { + public WithResult union(WithResult union) { return this.parent.union(union); } diff --git a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/impl/simple/SimpleSelectedImpl.java b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/impl/simple/SimpleSelectedImpl.java index 266c4d0a..6bb11a23 100644 --- a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/impl/simple/SimpleSelectedImpl.java +++ b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/impl/simple/SimpleSelectedImpl.java @@ -1,5 +1,7 @@ package io.github.ulisse1996.jaorm.dsl.query.impl.simple; +import io.github.ulisse1996.jaorm.InlineValue; +import io.github.ulisse1996.jaorm.InlineValueWithAlias; import io.github.ulisse1996.jaorm.dsl.config.QueryConfig; import io.github.ulisse1996.jaorm.dsl.query.enums.JoinType; import io.github.ulisse1996.jaorm.dsl.query.enums.Operation; @@ -7,8 +9,16 @@ import io.github.ulisse1996.jaorm.dsl.query.impl.AbstractLimitOffsetImpl; import io.github.ulisse1996.jaorm.dsl.query.simple.FromSimpleSelected; import io.github.ulisse1996.jaorm.dsl.query.simple.SimpleSelected; -import io.github.ulisse1996.jaorm.dsl.query.simple.intermediate.*; -import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithProjectionResult; +import io.github.ulisse1996.jaorm.dsl.query.simple.intermediate.IntermediateSimpleHaving; +import io.github.ulisse1996.jaorm.dsl.query.simple.intermediate.IntermediateSimpleWhere; +import io.github.ulisse1996.jaorm.dsl.query.simple.intermediate.SimpleGroup; +import io.github.ulisse1996.jaorm.dsl.query.simple.intermediate.SimpleHaving; +import io.github.ulisse1996.jaorm.dsl.query.simple.intermediate.SimpleOn; +import io.github.ulisse1996.jaorm.dsl.query.simple.intermediate.SimpleOrder; +import io.github.ulisse1996.jaorm.dsl.query.simple.intermediate.SimpleSelectedLimit; +import io.github.ulisse1996.jaorm.dsl.query.simple.intermediate.SimpleSelectedOffset; +import io.github.ulisse1996.jaorm.dsl.query.simple.intermediate.SimpleSelectedWhere; +import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithResult; import io.github.ulisse1996.jaorm.dsl.util.Checker; import io.github.ulisse1996.jaorm.dsl.util.Pair; import io.github.ulisse1996.jaorm.entity.SqlColumn; @@ -28,6 +38,7 @@ import java.util.List; import java.util.Optional; import java.util.stream.Collectors; +import java.util.stream.Stream; public class SimpleSelectedImpl extends AbstractLimitOffsetImpl implements SimpleSelected, FromSimpleSelected, SimpleOrder, SimpleSelectedWhere, SimpleSelectedLimit, SimpleSelectedOffset, SimpleGroup, SimpleHaving { @@ -40,6 +51,7 @@ public class SimpleSelectedImpl extends AbstractLimitOffsetImpl implements Simpl private final List> wheres; private final List groups; private final List> havings; + private final boolean distinct; private SimpleWhereImpl lastWhere; private String table; private String alias; @@ -47,7 +59,8 @@ public class SimpleSelectedImpl extends AbstractLimitOffsetImpl implements Simpl private int limit; private int offset; - public SimpleSelectedImpl(List columns) { + public SimpleSelectedImpl(List columns, boolean distinct) { + this.distinct = distinct; this.columns = Collections.unmodifiableList( Checker.assertNotNull(columns, "columns") ); @@ -67,14 +80,14 @@ public FromSimpleSelected from(String table) { @Override public R read(Class klass) { - checkProjection(klass); + checkProjectionOrSimpleType(klass); Pair> build = doBuild(); return QueryRunner.getInstance(klass).read(klass, build.getKey(), build.getValue()); } @Override public Optional readOpt(Class klass) { - checkProjection(klass); + checkProjectionOrSimpleType(klass); Pair> build = doBuild(); return QueryRunner.getInstance(klass).readOpt(klass, build.getKey(), build.getValue()) .toOptional(); @@ -82,13 +95,13 @@ public Optional readOpt(Class klass) { @Override public List readAll(Class klass) { - checkProjection(klass); + checkProjectionOrSimpleType(klass); Pair> build = doBuild(); return QueryRunner.getInstance(klass).readAll(klass, build.getKey(), build.getValue()); } @Override - public WithProjectionResult union(WithProjectionResult union) { + public WithResult union(WithResult union) { SimpleSelectedImpl selected; if (union instanceof SimpleJoinImpl) { selected = ((SimpleJoinImpl) union).getParent(); @@ -99,8 +112,15 @@ public WithProjectionResult union(WithProjectionResult union) { return this; } - private void checkProjection(Class klass) { - ProjectionsService.getInstance().searchDelegate(klass); + private void checkProjectionOrSimpleType(Class klass) { + try { + ProjectionsService.getInstance().searchDelegate(klass); + } catch (IllegalArgumentException ex) { + // Skip for simple read with only one column + if (this.columns.size() != 1) { + throw ex; + } + } } public FromSimpleSelected from(String table, String alias) { @@ -209,19 +229,35 @@ private String asSelectColumns(AliasesSpecific aliasesSpecific, List { if (i.getColumn() != null) { return getSelectColumn(i, aliasesSpecific); + } else if (i.getInlineValue() != null) { + return getInlineColumn(i.getInlineValue(), aliasesSpecific); } else { return getVendorFunctionColumn(i, aliasesSpecific); } }).collect(Collectors.joining(", ")); parameters.addAll( this.columns.stream() - .filter(el -> el.getFunction() != null && el.getFunction().supportParams()) - .map(AliasColumn::getFunction) - .flatMap(el -> ((VendorFunctionWithParams) el).getParams().stream()) + .filter(el -> (el.getFunction() != null && el.getFunction().supportParams()) || el.getInlineValue() != null) + .flatMap(el -> { + if (el.getInlineValue() != null) { + return Stream.of(el.getInlineValue().getValue()); + } else { + VendorFunction function = el.getFunction(); + return ((VendorFunctionWithParams) function).getParams().stream(); + } + }) .map(SqlParameter::new) .collect(Collectors.toList()) ); - return "SELECT " + s; + return "SELECT " + (distinct ? "DISTINCT " : "") + s; + } + + private String getInlineColumn(InlineValue inlineValue, AliasesSpecific aliasesSpecific) { + String columnAlias = ""; + if (inlineValue instanceof InlineValueWithAlias) { + columnAlias = aliasesSpecific.convertToAlias(((InlineValueWithAlias) inlineValue).getAlias()); + } + return String.format("?%s", columnAlias); } private String getVendorFunctionColumn(AliasColumn i, AliasesSpecific aliasesSpecific) { diff --git a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/impl/simple/SimpleWhereImpl.java b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/impl/simple/SimpleWhereImpl.java index 87ef55d0..8a14dcd4 100644 --- a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/impl/simple/SimpleWhereImpl.java +++ b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/impl/simple/SimpleWhereImpl.java @@ -7,7 +7,7 @@ import io.github.ulisse1996.jaorm.dsl.query.impl.AbstractWhereImpl; import io.github.ulisse1996.jaorm.dsl.query.simple.intermediate.IntermediateSimpleWhere; import io.github.ulisse1996.jaorm.dsl.query.simple.intermediate.SimpleSelectedWhere; -import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithProjectionResult; +import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithResult; import io.github.ulisse1996.jaorm.entity.SqlColumn; public class SimpleWhereImpl extends AbstractWhereImpl implements IntermediateSimpleWhere { @@ -81,17 +81,17 @@ public SimpleSelectedWhere greaterOrEqualsTo(R val) { @Override public SimpleSelectedWhere in(Iterable iterable) { - this.valid = this.parent.getConfiguration().getChecker().isValidWhere(this.column, op, iterable); this.op = Operation.IN; this.iterable = iterable; + this.valid = this.parent.getConfiguration().getChecker().isValidWhere(this.column, op, iterable); return this.parent; } @Override public SimpleSelectedWhere notIn(Iterable iterable) { - this.valid = this.parent.getConfiguration().getChecker().isValidWhere(this.column, op, iterable); this.op = Operation.NOT_IN; this.iterable = iterable; + this.valid = this.parent.getConfiguration().getChecker().isValidWhere(this.column, op, iterable); return this.parent; } @@ -106,7 +106,7 @@ public SimpleSelectedWhere notIn(SelectedWhere subQuery) { } @Override - public SimpleSelectedWhere in(WithProjectionResult subQuery) { + public SimpleSelectedWhere in(WithResult subQuery) { this.valid = this.parent.getConfiguration().getChecker().isValidWhere(this.column, op, subQuery); this.op = Operation.IN; this.subQuery = assertIsSubQuery(subQuery); @@ -114,7 +114,7 @@ public SimpleSelectedWhere in(WithProjectionResult subQuery) { } @Override - public SimpleSelectedWhere notIn(WithProjectionResult subQuery) { + public SimpleSelectedWhere notIn(WithResult subQuery) { this.valid = this.parent.getConfiguration().getChecker().isValidWhere(this.column, op, subQuery); this.op = Operation.NOT_IN; this.subQuery = assertIsSubQuery(subQuery); diff --git a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/FromSimpleSelected.java b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/FromSimpleSelected.java index 01066427..2b9746e3 100644 --- a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/FromSimpleSelected.java +++ b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/FromSimpleSelected.java @@ -3,7 +3,7 @@ import io.github.ulisse1996.jaorm.dsl.query.common.trait.WithSubQuerySupport; import io.github.ulisse1996.jaorm.dsl.query.simple.trait.*; -public interface FromSimpleSelected extends WithProjectionResult, +public interface FromSimpleSelected extends WithResult, WithSimpleJoin, WithSimpleOrder, WithSimpleWhere, WithSubQuerySupport, WithSimpleLimit, WithSimpleOffset, WithSimpleGroup { } diff --git a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/IntermediateSimpleWhere.java b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/IntermediateSimpleWhere.java index 6a3699b3..2ddf678d 100644 --- a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/IntermediateSimpleWhere.java +++ b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/IntermediateSimpleWhere.java @@ -1,7 +1,7 @@ package io.github.ulisse1996.jaorm.dsl.query.simple.intermediate; import io.github.ulisse1996.jaorm.dsl.query.common.intermediate.IntermediateWhereCondition; -import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithProjectionResult; +import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithResult; -public interface IntermediateSimpleWhere extends IntermediateWhereCondition { +public interface IntermediateSimpleWhere extends IntermediateWhereCondition { } diff --git a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleGroup.java b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleGroup.java index d8695659..cf1d1471 100644 --- a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleGroup.java +++ b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleGroup.java @@ -3,4 +3,4 @@ import io.github.ulisse1996.jaorm.dsl.query.simple.trait.*; public interface SimpleGroup extends WithSimpleGroup, WithSimpleHaving, - WithProjectionResult, WithSimpleOffset, WithSimpleLimit {} + WithResult, WithSimpleOffset, WithSimpleLimit {} diff --git a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleHaving.java b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleHaving.java index 54774da6..3cd2a3e3 100644 --- a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleHaving.java +++ b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleHaving.java @@ -1,11 +1,11 @@ package io.github.ulisse1996.jaorm.dsl.query.simple.intermediate; -import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithProjectionResult; +import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithResult; import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithSimpleLimit; import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithSimpleOffset; import io.github.ulisse1996.jaorm.vendor.ansi.AggregateFunction; -public interface SimpleHaving extends WithProjectionResult, WithSimpleOffset, WithSimpleLimit { +public interface SimpleHaving extends WithResult, WithSimpleOffset, WithSimpleLimit { IntermediateSimpleHaving andHaving(AggregateFunction function); IntermediateSimpleHaving andHaving(AggregateFunction function, String alias); diff --git a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleOrder.java b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleOrder.java index bbda3409..f1b7e531 100644 --- a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleOrder.java +++ b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleOrder.java @@ -1,9 +1,9 @@ package io.github.ulisse1996.jaorm.dsl.query.simple.intermediate; -import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithProjectionResult; +import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithResult; import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithSimpleLimit; import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithSimpleOffset; import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithSimpleOrder; -public interface SimpleOrder extends WithSimpleOrder, WithProjectionResult, WithSimpleOffset, WithSimpleLimit { +public interface SimpleOrder extends WithSimpleOrder, WithResult, WithSimpleOffset, WithSimpleLimit { } diff --git a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleSelectedLimit.java b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleSelectedLimit.java index 6fbb93bf..553a164d 100644 --- a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleSelectedLimit.java +++ b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleSelectedLimit.java @@ -1,6 +1,6 @@ package io.github.ulisse1996.jaorm.dsl.query.simple.intermediate; -import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithProjectionResult; +import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithResult; -public interface SimpleSelectedLimit extends WithProjectionResult { +public interface SimpleSelectedLimit extends WithResult { } diff --git a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleSelectedOffset.java b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleSelectedOffset.java index 5cb63338..de4cbc26 100644 --- a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleSelectedOffset.java +++ b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleSelectedOffset.java @@ -1,7 +1,7 @@ package io.github.ulisse1996.jaorm.dsl.query.simple.intermediate; -import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithProjectionResult; +import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithResult; import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithSimpleLimit; -public interface SimpleSelectedOffset extends WithSimpleLimit, WithProjectionResult { +public interface SimpleSelectedOffset extends WithSimpleLimit, WithResult { } diff --git a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleSelectedOn.java b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleSelectedOn.java index 66d4a2f0..a235a0c8 100644 --- a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleSelectedOn.java +++ b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleSelectedOn.java @@ -4,6 +4,6 @@ import io.github.ulisse1996.jaorm.dsl.query.simple.trait.*; public interface SimpleSelectedOn extends WithSimpleOn, WithSimpleJoin, - WithProjectionResult, WithSimpleOrder, + WithResult, WithSimpleOrder, WithConfiguration, WithSimpleWhere, WithSimpleGroup { } diff --git a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleSelectedWhere.java b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleSelectedWhere.java index 2e697c7e..f84e845f 100644 --- a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleSelectedWhere.java +++ b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/intermediate/SimpleSelectedWhere.java @@ -1,9 +1,9 @@ package io.github.ulisse1996.jaorm.dsl.query.simple.intermediate; import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithAndOrWhere; -import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithProjectionResult; +import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithResult; import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithSimpleGroup; import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithSimpleOrder; -public interface SimpleSelectedWhere extends WithAndOrWhere, WithSimpleOrder, WithSimpleGroup, WithProjectionResult { +public interface SimpleSelectedWhere extends WithAndOrWhere, WithSimpleOrder, WithSimpleGroup, WithResult { } diff --git a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/trait/WithProjectionResult.java b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/trait/WithResult.java similarity index 69% rename from jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/trait/WithProjectionResult.java rename to jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/trait/WithResult.java index c3c859f7..9aae52d5 100644 --- a/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/trait/WithProjectionResult.java +++ b/jaorm-dsl/src/main/java/io/github/ulisse1996/jaorm/dsl/query/simple/trait/WithResult.java @@ -3,10 +3,10 @@ import java.util.List; import java.util.Optional; -public interface WithProjectionResult { +public interface WithResult { T read(Class klass); Optional readOpt(Class klass); List readAll(Class klass); - WithProjectionResult union(WithProjectionResult union); + WithResult union(WithResult union); } diff --git a/jaorm-dsl/src/test/java/io/github/ulisse1996/jaorm/dsl/query/QueryBuilderSimpleTest.java b/jaorm-dsl/src/test/java/io/github/ulisse1996/jaorm/dsl/query/QueryBuilderSimpleTest.java index b5295169..225043fb 100644 --- a/jaorm-dsl/src/test/java/io/github/ulisse1996/jaorm/dsl/query/QueryBuilderSimpleTest.java +++ b/jaorm-dsl/src/test/java/io/github/ulisse1996/jaorm/dsl/query/QueryBuilderSimpleTest.java @@ -7,7 +7,7 @@ import io.github.ulisse1996.jaorm.dsl.query.enums.OrderType; import io.github.ulisse1996.jaorm.dsl.query.impl.simple.SimpleJoinImpl; import io.github.ulisse1996.jaorm.dsl.query.simple.intermediate.SimpleOrder; -import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithProjectionResult; +import io.github.ulisse1996.jaorm.dsl.query.simple.trait.WithResult; import io.github.ulisse1996.jaorm.entity.Result; import io.github.ulisse1996.jaorm.entity.SqlColumn; import io.github.ulisse1996.jaorm.entity.sql.SqlParameter; @@ -811,7 +811,7 @@ void should_generate_orders(SimpleOrder order, String sql) { @ParameterizedTest @MethodSource("getWheres") - void should_generate_wheres(WithProjectionResult result, String sql) { + void should_generate_wheres(WithResult result, String sql) { withProjectionRunner((runner, service) -> { ArgumentCaptor sqlCapture = ArgumentCaptor.forClass(String.class); @@ -833,7 +833,7 @@ void should_generate_wheres(WithProjectionResult result, String sql) { @ParameterizedTest @MethodSource("getLimitOffset") - void should_generate_limit_offset(WithProjectionResult result, String sql) { + void should_generate_limit_offset(WithResult result, String sql) { withProjectionRunner((runner, service) -> { ArgumentCaptor sqlCapture = ArgumentCaptor.forClass(String.class); @@ -855,7 +855,7 @@ void should_generate_limit_offset(WithProjectionResult result, String sql) { @ParameterizedTest @MethodSource("getHaving") - void should_generate_query_with_having(WithProjectionResult result, String sql) { + void should_generate_query_with_having(WithResult result, String sql) { withProjectionRunner((runner, service) -> { ArgumentCaptor sqlCapture = ArgumentCaptor.forClass(String.class); diff --git a/jaorm-extensions/jaorm-ansi-sql-extension/pom.xml b/jaorm-extensions/jaorm-ansi-sql-extension/pom.xml index 45794dc8..8d4aad47 100644 --- a/jaorm-extensions/jaorm-ansi-sql-extension/pom.xml +++ b/jaorm-extensions/jaorm-ansi-sql-extension/pom.xml @@ -5,7 +5,7 @@ jaorm-extensions io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-extensions/jaorm-cdi-jakarta-extension/pom.xml b/jaorm-extensions/jaorm-cdi-jakarta-extension/pom.xml index 879892e4..d42b4f28 100644 --- a/jaorm-extensions/jaorm-cdi-jakarta-extension/pom.xml +++ b/jaorm-extensions/jaorm-cdi-jakarta-extension/pom.xml @@ -5,7 +5,7 @@ jaorm-extensions io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 @@ -52,6 +52,18 @@ org.apache.maven.plugins maven-compiler-plugin ${maven.compiler.version} + + + -parameters + + + + io.github.ulisse1996 + jaorm-processor + ${project.version} + + + org.apache.maven.plugins diff --git a/jaorm-extensions/jaorm-cdi-jakarta-extension/src/main/java/io/github/ulisse1996/jaorm/extension/cdi/JakartaDaoProvider.java b/jaorm-extensions/jaorm-cdi-jakarta-extension/src/main/java/io/github/ulisse1996/jaorm/extension/cdi/JakartaDaoProvider.java new file mode 100644 index 00000000..4c2c8052 --- /dev/null +++ b/jaorm-extensions/jaorm-cdi-jakarta-extension/src/main/java/io/github/ulisse1996/jaorm/extension/cdi/JakartaDaoProvider.java @@ -0,0 +1,23 @@ +package io.github.ulisse1996.jaorm.extension.cdi; + +import io.github.ulisse1996.jaorm.spi.QueriesService; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.inject.spi.AfterBeanDiscovery; +import jakarta.enterprise.inject.spi.Extension; + +import java.util.Set; + +public class JakartaDaoProvider implements Extension { + + void afterBeanDiscovery(@Observes AfterBeanDiscovery event) { + Set> queries = QueriesService.getInstance().getQueries().keySet(); + for (Class dao : queries) { + event.addBean() + .addTransitiveTypeClosure(dao) + .beanClass(dao) + .scope(ApplicationScoped.class) + .produceWith((instance) -> QueriesService.getInstance().getQuery(dao)); + } + } +} diff --git a/jaorm-extensions/jaorm-cdi-jakarta-extension/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension b/jaorm-extensions/jaorm-cdi-jakarta-extension/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension new file mode 100644 index 00000000..d79b6e64 --- /dev/null +++ b/jaorm-extensions/jaorm-cdi-jakarta-extension/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension @@ -0,0 +1 @@ +io.github.ulisse1996.jaorm.extension.cdi.JakartaDaoProvider \ No newline at end of file diff --git a/jaorm-extensions/jaorm-cdi-jakarta-extension/src/test/java/io/github/ulisse1996/jaorm/extension/cdi/JakartaBeanProviderTest.java b/jaorm-extensions/jaorm-cdi-jakarta-extension/src/test/java/io/github/ulisse1996/jaorm/extension/cdi/JakartaBeanProviderTest.java index 38e6c46f..0dcc9ed5 100644 --- a/jaorm-extensions/jaorm-cdi-jakarta-extension/src/test/java/io/github/ulisse1996/jaorm/extension/cdi/JakartaBeanProviderTest.java +++ b/jaorm-extensions/jaorm-cdi-jakarta-extension/src/test/java/io/github/ulisse1996/jaorm/extension/cdi/JakartaBeanProviderTest.java @@ -52,10 +52,8 @@ class JakartaBeanProviderTest { @Test void should_return_global_listener_instance(WeldContainer container) throws Throwable { - runInEnvironment(container, () -> Assertions.assertTrue( - new JakartaBeanProvider().getBean(GlobalEventListener.class) - instanceof GlobalListenerService - )); + runInEnvironment(container, () -> + Assertions.assertInstanceOf(GlobalListenerService.class, new JakartaBeanProvider().getBean(GlobalEventListener.class))); } @Test diff --git a/jaorm-extensions/jaorm-cdi-jakarta-extension/src/test/java/io/github/ulisse1996/jaorm/extension/cdi/JakartaDaoProviderTest.java b/jaorm-extensions/jaorm-cdi-jakarta-extension/src/test/java/io/github/ulisse1996/jaorm/extension/cdi/JakartaDaoProviderTest.java new file mode 100644 index 00000000..67608cb3 --- /dev/null +++ b/jaorm-extensions/jaorm-cdi-jakarta-extension/src/test/java/io/github/ulisse1996/jaorm/extension/cdi/JakartaDaoProviderTest.java @@ -0,0 +1,83 @@ +package io.github.ulisse1996.jaorm.extension.cdi; + +import io.github.ulisse1996.jaorm.annotation.Dao; +import jakarta.enterprise.inject.Instance; +import jakarta.enterprise.inject.spi.CDI; +import jakarta.enterprise.inject.spi.CDIProvider; +import org.jboss.weld.environment.se.WeldContainer; +import org.jboss.weld.inject.WeldInstance; +import org.jboss.weld.junit5.WeldInitiator; +import org.jboss.weld.junit5.WeldJunit5Extension; +import org.jboss.weld.junit5.WeldSetup; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.function.Executable; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; + +@ExtendWith({WeldJunit5Extension.class, MockitoExtension.class}) +class JakartaDaoProviderTest { + + private final static boolean MULTIPLE_CDI; // Used for Intellij Multiple Module tests + + @Mock + private CDI cdi; + + static { + boolean tmp; + try { + ServiceLoader load = ServiceLoader.load(CDIProvider.class, CDI.class.getClassLoader()); + load.forEach(System.out::println); + tmp = false; + } catch (ServiceConfigurationError e) { + tmp = true; + } + MULTIPLE_CDI = tmp; + } + + @WeldSetup + public WeldInitiator weld = WeldInitiator.of(JakartaDaoProvider.class); + + @Test + void shouldSelectCustomDao(WeldContainer weldContainer) throws Throwable { + runInEnvironment(weldContainer, () -> { + Instance select = CDI.current().select(CustomDao.class); + Assertions.assertTrue(select.isResolvable()); + }); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private void runInEnvironment(WeldContainer container, Executable executable) throws Throwable { + if (MULTIPLE_CDI) { + try (MockedStatic mk = Mockito.mockStatic(CDI.class)) { + mk.when(CDI::current) + .thenReturn(cdi); + Mockito.doAnswer(invocation -> { + Class klass = invocation.getArgument(0); + WeldInstance i = container.select(klass); + return Proxy.newProxyInstance(JakartaBeanProviderTest.class.getClassLoader(), new Class[] {Instance.class}, (proxy, method, args) -> { + CustomInstance instance = new CustomInstance(i); + String methodName = method.getName(); + Method real = instance.getClass().getMethod(methodName, method.getParameterTypes()); + return real.invoke(instance, args); + }); + }) + .when(cdi).select(Mockito.any(Class.class)); + executable.execute(); + } + } else { + executable.execute(); + } + } + + @Dao + interface CustomDao {} +} \ No newline at end of file diff --git a/jaorm-extensions/jaorm-cdi-jakarta-extension/src/test/resources/META-INF/beans.xml b/jaorm-extensions/jaorm-cdi-jakarta-extension/src/test/resources/META-INF/beans.xml new file mode 100644 index 00000000..abca52ca --- /dev/null +++ b/jaorm-extensions/jaorm-cdi-jakarta-extension/src/test/resources/META-INF/beans.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/jaorm-extensions/jaorm-cdi-javax-extension/pom.xml b/jaorm-extensions/jaorm-cdi-javax-extension/pom.xml index ae68f1d9..416e50ac 100644 --- a/jaorm-extensions/jaorm-cdi-javax-extension/pom.xml +++ b/jaorm-extensions/jaorm-cdi-javax-extension/pom.xml @@ -5,7 +5,7 @@ jaorm-extensions io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-extensions/jaorm-cdi-javax-extension/src/main/java/io/github/ulisse1996/jaorm/extension/cdi/JavaxDaoProvider.java b/jaorm-extensions/jaorm-cdi-javax-extension/src/main/java/io/github/ulisse1996/jaorm/extension/cdi/JavaxDaoProvider.java new file mode 100644 index 00000000..ca3191bf --- /dev/null +++ b/jaorm-extensions/jaorm-cdi-javax-extension/src/main/java/io/github/ulisse1996/jaorm/extension/cdi/JavaxDaoProvider.java @@ -0,0 +1,23 @@ +package io.github.ulisse1996.jaorm.extension.cdi; + +import io.github.ulisse1996.jaorm.spi.QueriesService; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.event.Observes; +import javax.enterprise.inject.spi.AfterBeanDiscovery; +import javax.enterprise.inject.spi.Extension; +import java.util.Set; + +public class JavaxDaoProvider implements Extension { + + void afterBeanDiscovery(@Observes AfterBeanDiscovery event) { + Set> queries = QueriesService.getInstance().getQueries().keySet(); + for (Class dao : queries) { + event.addBean() + .addTransitiveTypeClosure(dao) + .beanClass(dao) + .scope(ApplicationScoped.class) + .produceWith((instance) -> QueriesService.getInstance().getQuery(dao)); + } + } +} diff --git a/jaorm-extensions/jaorm-cdi-javax-extension/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension b/jaorm-extensions/jaorm-cdi-javax-extension/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension new file mode 100644 index 00000000..5147f348 --- /dev/null +++ b/jaorm-extensions/jaorm-cdi-javax-extension/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension @@ -0,0 +1 @@ +io.github.ulisse1996.jaorm.extension.cdi.JavaxDaoProvider \ No newline at end of file diff --git a/jaorm-extensions/jaorm-cdi-javax-extension/src/test/java/io/github/ulisse1996/jaorm/extension/cdi/JavaxDaoProviderTest.java b/jaorm-extensions/jaorm-cdi-javax-extension/src/test/java/io/github/ulisse1996/jaorm/extension/cdi/JavaxDaoProviderTest.java new file mode 100644 index 00000000..03213a12 --- /dev/null +++ b/jaorm-extensions/jaorm-cdi-javax-extension/src/test/java/io/github/ulisse1996/jaorm/extension/cdi/JavaxDaoProviderTest.java @@ -0,0 +1,83 @@ +package io.github.ulisse1996.jaorm.extension.cdi; + +import io.github.ulisse1996.jaorm.annotation.Dao; +import org.jboss.weld.environment.se.WeldContainer; +import org.jboss.weld.inject.WeldInstance; +import org.jboss.weld.junit5.WeldInitiator; +import org.jboss.weld.junit5.WeldJunit5Extension; +import org.jboss.weld.junit5.WeldSetup; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.function.Executable; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import javax.enterprise.inject.Instance; +import javax.enterprise.inject.spi.CDI; +import javax.enterprise.inject.spi.CDIProvider; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; + +@ExtendWith({WeldJunit5Extension.class, MockitoExtension.class}) +class JavaxDaoProviderTest { + + private final static boolean MULTIPLE_CDI; // Used for Intellij Multiple Module tests + + @Mock + private CDI cdi; + + static { + boolean tmp; + try { + ServiceLoader load = ServiceLoader.load(CDIProvider.class, CDI.class.getClassLoader()); + load.forEach(System.out::println); + tmp = false; + } catch (ServiceConfigurationError e) { + tmp = true; + } + MULTIPLE_CDI = tmp; + } + + @WeldSetup + public WeldInitiator weld = WeldInitiator.of(JavaxDaoProvider.class); + + @Test + void shouldSelectCustomDao(WeldContainer weldContainer) throws Throwable { + runInEnvironment(weldContainer, () -> { + Instance select = CDI.current().select(CustomDao.class); + Assertions.assertTrue(select.isResolvable()); + }); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private void runInEnvironment(WeldContainer container, Executable executable) throws Throwable { + if (MULTIPLE_CDI) { + try (MockedStatic mk = Mockito.mockStatic(CDI.class)) { + mk.when(CDI::current) + .thenReturn(cdi); + Mockito.doAnswer(invocation -> { + Class klass = invocation.getArgument(0); + WeldInstance i = container.select(klass); + return Proxy.newProxyInstance(JavaxDaoProvider.class.getClassLoader(), new Class[] {Instance.class}, (proxy, method, args) -> { + CustomInstance instance = new CustomInstance(i); + String methodName = method.getName(); + Method real = instance.getClass().getMethod(methodName, method.getParameterTypes()); + return real.invoke(instance, args); + }); + }) + .when(cdi).select(Mockito.any(Class.class)); + executable.execute(); + } + } else { + executable.execute(); + } + } + + @Dao + interface CustomDao {} +} \ No newline at end of file diff --git a/jaorm-extensions/jaorm-di-spring-extension/pom.xml b/jaorm-extensions/jaorm-di-spring-extension/pom.xml index abba2aa2..13f10951 100644 --- a/jaorm-extensions/jaorm-di-spring-extension/pom.xml +++ b/jaorm-extensions/jaorm-di-spring-extension/pom.xml @@ -5,7 +5,7 @@ jaorm-extensions io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-extensions/jaorm-extension-api/pom.xml b/jaorm-extensions/jaorm-extension-api/pom.xml index 93f87135..ab09fa0f 100644 --- a/jaorm-extensions/jaorm-extension-api/pom.xml +++ b/jaorm-extensions/jaorm-extension-api/pom.xml @@ -5,7 +5,7 @@ jaorm-extensions io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-extensions/jaorm-micronaut-extension/pom.xml b/jaorm-extensions/jaorm-micronaut-extension/pom.xml index 93afdf4c..e765a116 100644 --- a/jaorm-extensions/jaorm-micronaut-extension/pom.xml +++ b/jaorm-extensions/jaorm-micronaut-extension/pom.xml @@ -5,7 +5,7 @@ jaorm-extensions io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-extensions/jaorm-quarkus-extension/pom.xml b/jaorm-extensions/jaorm-quarkus-extension/pom.xml index d3845132..b150263b 100644 --- a/jaorm-extensions/jaorm-quarkus-extension/pom.xml +++ b/jaorm-extensions/jaorm-quarkus-extension/pom.xml @@ -6,7 +6,7 @@ io.github.ulisse1996 jaorm-extensions - 2.0.0-RC2 + 2.0.0-RC3 jaorm-quarkus-extension diff --git a/jaorm-extensions/jaorm-quarkus-extension/target/classes/META-INF/services/io.github.ulisse1996.jaorm.spi.FrameworkIntegrationService b/jaorm-extensions/jaorm-quarkus-extension/target/classes/META-INF/services/io.github.ulisse1996.jaorm.spi.FrameworkIntegrationService deleted file mode 100644 index 611b3f55..00000000 --- a/jaorm-extensions/jaorm-quarkus-extension/target/classes/META-INF/services/io.github.ulisse1996.jaorm.spi.FrameworkIntegrationService +++ /dev/null @@ -1 +0,0 @@ -io.github.ulisse1996.jaorm.extension.quarkus.QuarkusFrameworkIntegrationService \ No newline at end of file diff --git a/jaorm-extensions/jaorm-quarkus-extension/target/classes/io/github/ulisse1996/jaorm/extension/quarkus/QuarkusFrameworkIntegrationService.class b/jaorm-extensions/jaorm-quarkus-extension/target/classes/io/github/ulisse1996/jaorm/extension/quarkus/QuarkusFrameworkIntegrationService.class deleted file mode 100644 index 17342acf..00000000 Binary files a/jaorm-extensions/jaorm-quarkus-extension/target/classes/io/github/ulisse1996/jaorm/extension/quarkus/QuarkusFrameworkIntegrationService.class and /dev/null differ diff --git a/jaorm-extensions/jaorm-quarkus-extension/target/jaorm-quarkus-extension-2.0.0-RC2.jar b/jaorm-extensions/jaorm-quarkus-extension/target/jaorm-quarkus-extension-2.0.0-RC2.jar deleted file mode 100644 index 19e0f9e5..00000000 Binary files a/jaorm-extensions/jaorm-quarkus-extension/target/jaorm-quarkus-extension-2.0.0-RC2.jar and /dev/null differ diff --git a/jaorm-extensions/jaorm-quarkus-extension/target/maven-archiver/pom.properties b/jaorm-extensions/jaorm-quarkus-extension/target/maven-archiver/pom.properties deleted file mode 100644 index a823c629..00000000 --- a/jaorm-extensions/jaorm-quarkus-extension/target/maven-archiver/pom.properties +++ /dev/null @@ -1,5 +0,0 @@ -#Generated by Maven -#Sat Jan 07 21:27:24 CET 2023 -groupId=io.github.ulisse1996 -artifactId=jaorm-quarkus-extension -version=2.0.0-RC2 diff --git a/jaorm-extensions/jaorm-quarkus-extension/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/jaorm-extensions/jaorm-quarkus-extension/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst deleted file mode 100644 index e5621c97..00000000 --- a/jaorm-extensions/jaorm-quarkus-extension/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +++ /dev/null @@ -1 +0,0 @@ -io/github/ulisse1996/jaorm/extension/quarkus/QuarkusFrameworkIntegrationService.class diff --git a/jaorm-extensions/jaorm-quarkus-extension/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/jaorm-extensions/jaorm-quarkus-extension/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst deleted file mode 100644 index 6f0be4ad..00000000 --- a/jaorm-extensions/jaorm-quarkus-extension/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +++ /dev/null @@ -1 +0,0 @@ -/Users/donatoleone/projects/jaorm/jaorm-extensions/jaorm-quarkus-extension/src/main/java/io/github/ulisse1996/jaorm/extension/quarkus/QuarkusFrameworkIntegrationService.java diff --git a/jaorm-extensions/jaorm-quarkus-extension/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst b/jaorm-extensions/jaorm-quarkus-extension/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst deleted file mode 100644 index f93386be..00000000 --- a/jaorm-extensions/jaorm-quarkus-extension/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst +++ /dev/null @@ -1 +0,0 @@ -io/github/ulisse1996/jaorm/extension/quarkus/QuarkusFrameworkIntegrationServiceTest.class diff --git a/jaorm-extensions/jaorm-quarkus-extension/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst b/jaorm-extensions/jaorm-quarkus-extension/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst deleted file mode 100644 index 59fc5070..00000000 --- a/jaorm-extensions/jaorm-quarkus-extension/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst +++ /dev/null @@ -1 +0,0 @@ -/Users/donatoleone/projects/jaorm/jaorm-extensions/jaorm-quarkus-extension/src/test/java/io/github/ulisse1996/jaorm/extension/quarkus/QuarkusFrameworkIntegrationServiceTest.java diff --git a/jaorm-extensions/pom.xml b/jaorm-extensions/pom.xml index 7442cdd0..6f9bdf92 100644 --- a/jaorm-extensions/pom.xml +++ b/jaorm-extensions/pom.xml @@ -5,7 +5,7 @@ jaorm-pom io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-lombok/pom.xml b/jaorm-lombok/pom.xml index 85e06361..e4faf3c6 100644 --- a/jaorm-lombok/pom.xml +++ b/jaorm-lombok/pom.xml @@ -5,7 +5,7 @@ jaorm-pom io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-lombok/src/main/java/io/github/ulisse1996/jaorm/external/support/mock/MockAccessor.java b/jaorm-lombok/src/main/java/io/github/ulisse1996/jaorm/external/support/mock/MockAccessor.java index 4551a18c..079db3f7 100644 --- a/jaorm-lombok/src/main/java/io/github/ulisse1996/jaorm/external/support/mock/MockAccessor.java +++ b/jaorm-lombok/src/main/java/io/github/ulisse1996/jaorm/external/support/mock/MockAccessor.java @@ -2,7 +2,14 @@ import io.github.ulisse1996.jaorm.external.LombokElementMock; -import javax.lang.model.element.*; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.Name; +import javax.lang.model.element.TypeParameterElement; +import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import java.util.Collections; import java.util.List; @@ -98,4 +105,9 @@ private boolean isBoolean() { public Element getEnclosingElement() { return element.getEnclosingElement(); } + + @Override + public String toString() { + return getSimpleName().toString(); + } } diff --git a/jaorm-processor/pom.xml b/jaorm-processor/pom.xml index 9084185c..7edeb575 100644 --- a/jaorm-processor/pom.xml +++ b/jaorm-processor/pom.xml @@ -5,7 +5,7 @@ jaorm-pom io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/ConverterGenerator.java b/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/ConverterGenerator.java index 59a76152..d4417645 100644 --- a/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/ConverterGenerator.java +++ b/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/ConverterGenerator.java @@ -61,7 +61,7 @@ private void generateConverters(Set conversions) { private MethodSpec getToStringMethod(ConverterInfo info) { return MethodSpec.overriding(ProcessorUtils.getMethod(this.processingEnvironment, "toString", Object.class)) - .addStatement("return $S", String.format("ConvertProvider{from=%s,to%s}", info.beforeClass, info.afterClass)) + .addStatement("return $S", String.format("ConvertProvider{from=%s,to=%s}", info.beforeClass, info.afterClass)) .build(); } diff --git a/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/DelegationInfo.java b/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/DelegationInfo.java new file mode 100644 index 00000000..1a251ccd --- /dev/null +++ b/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/DelegationInfo.java @@ -0,0 +1,25 @@ +package io.github.ulisse1996.jaorm.processor.generation.impl; + +import com.squareup.javapoet.CodeBlock; +import com.squareup.javapoet.MethodSpec; + +import java.util.List; + +public class DelegationInfo { + + private final List methods; + private final List relationshipsBlocks; + + public DelegationInfo(List methods, List relationshipsBlocks) { + this.methods = methods; + this.relationshipsBlocks = relationshipsBlocks; + } + + public List getRelationshipsBlocks() { + return relationshipsBlocks; + } + + public List getMethods() { + return methods; + } +} diff --git a/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/DslColumnsGenerator.java b/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/DslColumnsGenerator.java index f305f0e9..ab0e1188 100644 --- a/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/DslColumnsGenerator.java +++ b/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/DslColumnsGenerator.java @@ -15,7 +15,6 @@ import javax.lang.model.element.Element; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; -import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import java.util.*; import java.util.function.Function; @@ -129,7 +128,7 @@ private Set getEntityColumns(TypeElement entity) { } if (el.getAnnotation(Converter.class) != null) { - converter = ProcessorUtils.getConverterCaller(processingEnvironment, (VariableElement) el); + converter = ProcessorUtils.getConverterCaller(processingEnvironment, el); } values.add(new EntityColumn(name, result, converter)); } diff --git a/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/EntityGenerator.java b/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/EntityGenerator.java index 6dbdb5bf..b3fb4dac 100644 --- a/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/EntityGenerator.java +++ b/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/EntityGenerator.java @@ -1,11 +1,37 @@ package io.github.ulisse1996.jaorm.processor.generation.impl; -import com.squareup.javapoet.*; -import io.github.ulisse1996.jaorm.annotation.*; -import io.github.ulisse1996.jaorm.entity.*; +import com.squareup.javapoet.AnnotationSpec; +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.CodeBlock; +import com.squareup.javapoet.FieldSpec; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.ParameterSpec; +import com.squareup.javapoet.ParameterizedTypeName; +import com.squareup.javapoet.TypeName; +import com.squareup.javapoet.TypeSpec; +import com.squareup.javapoet.WildcardTypeName; +import io.github.ulisse1996.jaorm.annotation.Column; +import io.github.ulisse1996.jaorm.annotation.Converter; +import io.github.ulisse1996.jaorm.annotation.DefaultNumeric; +import io.github.ulisse1996.jaorm.annotation.DefaultString; +import io.github.ulisse1996.jaorm.annotation.DefaultTemporal; +import io.github.ulisse1996.jaorm.annotation.Id; +import io.github.ulisse1996.jaorm.annotation.Relationship; +import io.github.ulisse1996.jaorm.annotation.Table; +import io.github.ulisse1996.jaorm.entity.ColumnGetter; +import io.github.ulisse1996.jaorm.entity.ColumnSetter; +import io.github.ulisse1996.jaorm.entity.DefaultGenerator; +import io.github.ulisse1996.jaorm.entity.DirtinessTracker; +import io.github.ulisse1996.jaorm.entity.EntityDelegate; +import io.github.ulisse1996.jaorm.entity.EntityMapper; +import io.github.ulisse1996.jaorm.entity.SqlColumn; +import io.github.ulisse1996.jaorm.entity.TrackedList; import io.github.ulisse1996.jaorm.entity.converter.ParameterConverter; +import io.github.ulisse1996.jaorm.entity.relationship.LazyEntityInfo; +import io.github.ulisse1996.jaorm.entity.relationship.RelationshipManager; import io.github.ulisse1996.jaorm.entity.sql.SqlAccessor; import io.github.ulisse1996.jaorm.entity.sql.SqlParameter; +import io.github.ulisse1996.jaorm.external.support.mock.MockGetter; import io.github.ulisse1996.jaorm.processor.exception.ProcessorException; import io.github.ulisse1996.jaorm.processor.generation.Generator; import io.github.ulisse1996.jaorm.processor.util.GeneratedFile; @@ -18,10 +44,23 @@ import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.RoundEnvironment; -import javax.lang.model.element.*; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.Name; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import java.lang.annotation.Annotation; -import java.util.*; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -68,8 +107,15 @@ private GeneratedFile generate(TypeElement entity) { .addModifiers(Modifier.PUBLIC) .superclass(entity.asType()) .addSuperinterface(ParameterizedTypeName.get(ClassName.get(EntityDelegate.class), ClassName.get(entity))); + TypeSpec columns = generateColumns(processingEnvironment, entity); builder.addType(columns); + builder.addStaticBlock(buildEntityMapperGenerator(entity)); + builder.addStaticBlock(buildSelectablesGenerator()); + builder.addStaticBlock(buildKeysWhereGenerator()); + builder.addStaticBlock(buildInsertSqlGenerator(entity)); + builder.addStaticBlock(buildUpdateSqlGenerator(entity)); + builder.addField(ClassName.get(entity), ENTITY, Modifier.PRIVATE); builder.addField( FieldSpec.builder( @@ -82,18 +128,61 @@ private GeneratedFile generate(TypeElement entity) { ); builder.addField(addTableName(entity)); builder.addField(addSchemaName(entity)); - builder.addField(addBaseSql(entity)); + builder.addField(addSelectables()); builder.addField(addKeysWhere()); builder.addField(addInsertSql()); builder.addField(addUpdateSql()); - builder.addField(addDeleteSql()); + builder.addField(addTracker(entity)); + builder.addField(addLazyEntityInfo()); + builder.addField(addRelationshipManager(entity)); + builder.addField(getEntityMapperType(entity), "ENTITY_MAPPER", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL); builder.addField(TypeName.INT, "modifiedRow", Modifier.PRIVATE); builder.addField(boolean.class, "modified", Modifier.PRIVATE); - builder.addMethods(buildDelegation(entity)); + DelegationInfo delegationInfo = buildDelegation(entity); + builder.addMethods(delegationInfo.getMethods()); builder.addMethods(buildOverrideEntity(entity)); + builder.addStaticBlock(getRelationshipManagerBlock(delegationInfo.getRelationshipsBlocks(), entity)); return new GeneratedFile(getPackage(entity), builder.build(), entity.getQualifiedName().toString()); } + private FieldSpec addSelectables() { + return FieldSpec.builder(String.class, "SELECTABLES", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) + .build(); + } + + private CodeBlock getRelationshipManagerBlock(List relationshipsBlocks, TypeElement entity) { + ParameterizedTypeName type = ParameterizedTypeName.get(ClassName.get(RelationshipManager.class), ClassName.get(entity)); + CodeBlock.Builder builder = CodeBlock.builder() + .addStatement("$T manager = new $T()", type, type); + for (CodeBlock block : relationshipsBlocks) { + builder.add(block); + } + builder.addStatement("RELATIONSHIP_MANAGER = manager"); + return builder.build(); + } + + private FieldSpec addRelationshipManager(TypeElement entity) { + return FieldSpec.builder( + ParameterizedTypeName.get(ClassName.get(RelationshipManager.class), ClassName.get(entity)), + "RELATIONSHIP_MANAGER", + Modifier.PRIVATE, + Modifier.STATIC, + Modifier.FINAL + ).build(); + } + + private FieldSpec addTracker(TypeElement entity) { + ParameterizedTypeName type = ParameterizedTypeName.get(ClassName.get(DirtinessTracker.class), ClassName.get(entity)); + return FieldSpec.builder(type, "tracker", Modifier.PRIVATE) + .initializer("new $T(this)", type) + .build(); + } + + private FieldSpec addLazyEntityInfo() { + return FieldSpec.builder(LazyEntityInfo.class, "lazyInfo", Modifier.PRIVATE) + .build(); + } + private FieldSpec addSchemaName(TypeElement entity) { String schema = entity.getAnnotation(Table.class).schema(); return FieldSpec.builder(String.class, "SCHEMA", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) @@ -101,38 +190,55 @@ private FieldSpec addSchemaName(TypeElement entity) { .build(); } - private Iterable buildDelegation(TypeElement entity) { + private DelegationInfo buildDelegation(TypeElement entity) { List elements = ProcessorUtils.getAllValidElements(processingEnvironment, entity); List methods = ProcessorUtils.getAllMethods(processingEnvironment, entity); - List> joins = new ArrayList<>(); + List joins = new ArrayList<>(); for (Element element : elements) { if (hasJoinAnnotation(element)) { ExecutableElement getter = ProcessorUtils.findGetter(processingEnvironment, entity, element.getSimpleName()); - joins.add(new AbstractMap.SimpleImmutableEntry<>(element, getter)); + ExecutableElement setter = ProcessorUtils.findSetter(processingEnvironment, + entity, element.getSimpleName()); + joins.add(new JoinInfo(element, getter, setter)); } } + List blocks = new ArrayList<>(); List specs = new ArrayList<>(); for (ExecutableElement m : methods) { - Optional> join = joins.stream() - .filter(p -> p.getValue().equals(m)) + Optional join = joins.stream() + .filter(p -> isSameMethod(m, p.getter) || isSameMethod(m, p.setter)) .findFirst(); if (join.isPresent()) { - specs.add(buildJoinMethod(entity, join.get())); + if (join.get().getter.equals(m) || m instanceof MockGetter) { + Map.Entry generated = buildJoinMethod(entity, join.get()); + specs.add(generated.getValue()); + blocks.add(generated.getKey()); + } else if (ProcessorUtils.isCollectionType(join.get().field.asType())) { + specs.add(ProcessorUtils.buildDelegateMethod(processingEnvironment, m, entity, true, true, join.get().getter)); + } else { + specs.add(ProcessorUtils.buildDelegateMethod(processingEnvironment, m, entity, true, false, join.get().getter)); + } } else { - specs.add(ProcessorUtils.buildDelegateMethod(m, entity, true)); + specs.add(ProcessorUtils.buildDelegateMethod(processingEnvironment, m, entity, true, false, null)); } } - return specs; + return new DelegationInfo(specs, blocks); + } + + private boolean isSameMethod(ExecutableElement m, ExecutableElement getter) { + Name name1 = m.getSimpleName(); + Name name2 = getter.getSimpleName(); + return name1.contentEquals(name2); } private boolean hasJoinAnnotation(Element ele) { return ele.getAnnotation(Relationship.class) != null; } - private MethodSpec buildJoinMethod(TypeElement entity, Map.Entry join) { - ExecutableElement method = join.getValue(); + private Map.Entry buildJoinMethod(TypeElement entity, JoinInfo join) { + ExecutableElement method = join.getter; ReturnTypeDefinition definition = new ReturnTypeDefinition(processingEnvironment, method.getReturnType()); String runnerMethod; if (definition.isCollection()) { @@ -145,33 +251,90 @@ private MethodSpec buildJoinMethod(TypeElement entity, Map.Entry()", ParameterizedTypeName.get(List.class, SqlParameter.class), ArrayList.class); - for (Relationship.RelationshipColumn column : columns) { + + List sorted = Arrays.stream(columns) + .sorted(defaultComparator().reversed()) // Reversed so empty string (default value) are last + .collect(Collectors.toList()); + for (Relationship.RelationshipColumn column : sorted) { VariableElement targetColumn = ProcessorUtils.getFieldWithColumnName(processingEnvironment, definition.getRealClass(), column.targetColumn()); buildJoinParam(column, builder, targetColumn, entity); } - String wheres = createJoinWhere(columns); - CodeBlock block = builder.addStatement("this.entity.$L($T.getInstance($T.class).$L($T.class, $T.getInstance().getSimpleSql($T.class) + $S, params))", - ProcessorUtils.findSetter(processingEnvironment, entity, join.getKey().getSimpleName()).getSimpleName(), - QueryRunner.class, definition.getRealClass(), runnerMethod, definition.getRealClass(), DelegatesService.class, - definition.getRealClass(), wheres) - .endControlFlow() - .addStatement("return this.entity.$L()", join.getValue().getSimpleName()) - .build(); - - return MethodSpec.overriding(method) + String wheres = createJoinWhere(sorted); + CodeBlock block; + if (ProcessorUtils.isCollectionType(join.field.asType())) { + block = builder.addStatement("this.entity.$L(new $T<$T>(this, $T.getInstance($T.class).$L($T.class, $T.getInstance().getSimpleSql($T.class) + $S, params)))", + ProcessorUtils.findSetter(processingEnvironment, entity, join.field.getSimpleName()).getSimpleName(), + TrackedList.class, definition.getRealClass(), + QueryRunner.class, definition.getRealClass(), runnerMethod, definition.getRealClass(), DelegatesService.class, + definition.getRealClass(), wheres) + .endControlFlow() + .addStatement("return this.entity.$L()", join.getter.getSimpleName()) + .build(); + } else { + block = builder.addStatement("this.entity.$L($T.getInstance($T.class).$L($T.class, $T.getInstance().getSimpleSql($T.class) + $S, params))", + ProcessorUtils.findSetter(processingEnvironment, entity, join.field.getSimpleName()).getSimpleName(), + QueryRunner.class, definition.getRealClass(), runnerMethod, definition.getRealClass(), DelegatesService.class, + definition.getRealClass(), wheres) + .endControlFlow() + .addStatement("return this.entity.$L()", join.getter.getSimpleName()) + .build(); + } + + CodeBlock relationshipInfo = generateRelInfo(join.field.getSimpleName(), wheres, entity, columns); + MethodSpec delegate = MethodSpec.overriding(method) + .addAnnotations(getAllAnnotations(method)) // Add all annotations that are available on getter .addCode(block) .build(); + + return new AbstractMap.SimpleImmutableEntry<>(relationshipInfo, delegate); + } + + private Iterable getAllAnnotations(ExecutableElement method) { + return method.getAnnotationMirrors() + .stream() + .map(AnnotationSpec::get) + .collect(Collectors.toList()); + } + + private Comparator defaultComparator() { + return (c1, c2) -> { + if (!c1.sourceColumn().isEmpty()) { + return -1; + } else if (!c2.sourceColumn().isEmpty()) { + return 1; + } + + return 0; + }; } - private String createJoinWhere(Relationship.RelationshipColumn[] columns) { + private CodeBlock generateRelInfo(Name simpleName, String wheres, + TypeElement entity, + Relationship.RelationshipColumn[] columns) { + CodeBlock.Builder block = CodeBlock.builder() + .add("$T.<$L>builder().where($S)", RelationshipManager.RelationshipInfo.Builder.class, entity, wheres); + for (Relationship.RelationshipColumn column : columns) { + if (!column.sourceColumn().isEmpty()) { + block.add(".param(($T<$T, Object>) Column.findColumn($S).getGetter())", Function.class, entity, column.sourceColumn()); + } else { + block.add(".param((e) -> $T.$L.toValue($S))", ParameterConverter.class, column.converter(), column.defaultValue()); + } + } + return CodeBlock.builder() + .addStatement("manager.addRelationshipInfo($S, $L)", + simpleName, block.add(".build()").build()) + .build(); + } + + private String createJoinWhere(List columns) { boolean first = true; StringBuilder builder = new StringBuilder(); for (Relationship.RelationshipColumn column : columns) { @@ -216,36 +379,18 @@ private VariableElement getReferencedColumn(String sourceColumn, TypeElement ent .orElseThrow(() -> new ProcessorException("Can't find referenced column")); } - - private FieldSpec addDeleteSql() { - return FieldSpec.builder(String.class, "DELETE_SQL", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) - .initializer("$S + $L + $L", "DELETE FROM ", "TABLE", KEYS_WHERE) - .build(); - } - private FieldSpec addUpdateSql() { return FieldSpec.builder(String.class, "UPDATE_SQL", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) - .initializer("Column.getUpdateSql()") .build(); } private FieldSpec addInsertSql() { return FieldSpec.builder(String.class, "INSERT_SQL", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) - .initializer("Column.getInsertSql()") .build(); } private FieldSpec addKeysWhere() { return FieldSpec.builder(String.class, KEYS_WHERE, Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) - .initializer("Column.getKeyWheres()") - .build(); - } - - private FieldSpec addBaseSql(TypeElement entity) { - String select = "SELECT "; - String from = " FROM " + entity.getAnnotation(Table.class).name(); - return FieldSpec.builder(String.class, "BASE_SQL", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) - .initializer("$S + Column.getSelectables() + $S", select, from) .build(); } @@ -262,7 +407,7 @@ private Iterable buildOverrideEntity(TypeElement entity) { .build(); MethodSpec entityMapper = MethodSpec.overriding(ProcessorUtils.getMethod(processingEnvironment, "getEntityMapper", EntityDelegate.class)) .returns(ParameterizedTypeName.get(ClassName.get(EntityMapper.class), ClassName.get(entity))) - .addStatement("return Column.getEntityMapper()") + .addStatement("return ENTITY_MAPPER") .build(); MethodSpec setEntity = MethodSpec.overriding(ProcessorUtils.getMethod(processingEnvironment, "setEntity", EntityDelegate.class)) .addStatement("this.modified = false") @@ -281,7 +426,7 @@ private Iterable buildOverrideEntity(TypeElement entity) { .addStatement("return this.entity") .build(); MethodSpec baseSql = MethodSpec.overriding(ProcessorUtils.getMethod(processingEnvironment, "getBaseSql", EntityDelegate.class)) - .addStatement("return BASE_SQL") + .addStatement("return \"SELECT \" + SELECTABLES + \" FROM \" + TABLE") .build(); MethodSpec keysWhere = MethodSpec.overriding(ProcessorUtils.getMethod(processingEnvironment, "getKeysWhere", EntityDelegate.class)) .addStatement("return KEYS_WHERE") @@ -290,7 +435,7 @@ private Iterable buildOverrideEntity(TypeElement entity) { .addStatement("return INSERT_SQL") .build(); MethodSpec selectables = MethodSpec.overriding(ProcessorUtils.getMethod(processingEnvironment, "getSelectables", EntityDelegate.class)) - .addStatement("return Column.getSelectables().replace($S,$S).replace($S,$S).split($S)", "(", "", ")", "", ",") + .addStatement("return SELECTABLES.replace($S,$S).replace($S,$S).split($S)", "(", "", ")", "", ",") .build(); MethodSpec table = MethodSpec.overriding(ProcessorUtils.getMethod(processingEnvironment, "getTable", EntityDelegate.class)) .addStatement("return TABLE") @@ -299,11 +444,14 @@ private Iterable buildOverrideEntity(TypeElement entity) { .addStatement("return UPDATE_SQL") .build(); MethodSpec deleteSql = MethodSpec.overriding(ProcessorUtils.getMethod(processingEnvironment, "getDeleteSql", EntityDelegate.class)) - .addStatement("return DELETE_SQL") + .addStatement("return \"DELETE FROM \" + TABLE + KEYS_WHERE") .build(); MethodSpec modified = MethodSpec.overriding(ProcessorUtils.getMethod(processingEnvironment, "isModified", EntityDelegate.class)) .addStatement("return this.modified") .build(); + MethodSpec setModified = MethodSpec.overriding(ProcessorUtils.getMethod(processingEnvironment, "setModified", EntityDelegate.class)) + .addStatement("this.modified = arg0") + .build(); MethodSpec isDefaultGeneration = MethodSpec.overriding(ProcessorUtils.getMethod(processingEnvironment, "isDefaultGeneration", EntityDelegate.class)) .addStatement("return $L", hasDefaultGenerated(entity)) .build(); @@ -346,12 +494,41 @@ private Iterable buildOverrideEntity(TypeElement entity) { .addStatement("return new $LDelegate()", entity) .build(); + MethodSpec getTracker = MethodSpec.overriding(ProcessorUtils.getMethod(processingEnvironment, "getTracker", EntityDelegate.class)) + .returns(ParameterizedTypeName.get(ClassName.get(DirtinessTracker.class), ClassName.get(entity))) + .addStatement("return this.tracker") + .build(); + + MethodSpec isLazyEntity = MethodSpec.overriding(ProcessorUtils.getMethod(processingEnvironment, "isLazyEntity", EntityDelegate.class)) + .addStatement("return this.lazyInfo != null") + .build(); + + MethodSpec getLazyInfo = MethodSpec.overriding(ProcessorUtils.getMethod(processingEnvironment, "getLazyInfo", EntityDelegate.class)) + .addStatement("return this.lazyInfo") + .build(); + + MethodSpec setLazyInfo = MethodSpec.methodBuilder("setLazyInfo") + .addModifiers(Modifier.PUBLIC) + .addAnnotation(Override.class) + .addParameter(LazyEntityInfo.class, "info") + .addStatement("this.lazyInfo = info") + .build(); + + MethodSpec getRelationshipManager = MethodSpec.methodBuilder("getRelationshipManager") + .addModifiers(Modifier.PUBLIC) + .addAnnotation(Override.class) + .returns(ParameterizedTypeName.get(ClassName.get(RelationshipManager.class), ClassName.get(entity))) + .addStatement("return RELATIONSHIP_MANAGER") + .build(); + return Stream.of(supplierEntity, entityMapper, setEntity, setEntityObj, baseSql, keysWhere, insertSql, selectables, table, updateSql, getEntity, deleteSql, modified, isDefaultGeneration, initDefault, generateDelegate, - setFullEntityFullColumns, getKeyWhere, toTableInfo) + setFullEntityFullColumns, getKeyWhere, toTableInfo, getTracker, + getLazyInfo, isLazyEntity, setLazyInfo, getRelationshipManager, + setModified) .collect(Collectors.toList()); } @@ -470,135 +647,61 @@ private TypeSpec generateColumns(ProcessingEnvironment processingEnvironment, Ty builder.addField(String.class, COL_NAME, Modifier.PRIVATE, Modifier.FINAL); builder.addField(boolean.class, "key", Modifier.PRIVATE, Modifier.FINAL); builder.addField(boolean.class, "autoGenerated", Modifier.PRIVATE, Modifier.FINAL); - builder.addField(getEntityMapperType(entity), "ENTITY_MAPPER", Modifier.PRIVATE, Modifier.STATIC); - builder.addField(String.class, "SELECTABLES", Modifier.PRIVATE, Modifier.STATIC); - builder.addField(String.class, KEYS_WHERE, Modifier.PRIVATE, Modifier.STATIC); - builder.addField(String.class, "INSERT_SQL", Modifier.PRIVATE, Modifier.STATIC); - builder.addField(String.class, "UPDATE_SQL", Modifier.PRIVATE, Modifier.STATIC); builder.addMethod(addColumnConstructor(entity)); for (Accessor accessor : accessors) { builder.addEnumConstant(accessor.name, enumImpl(accessor, entity)); } + addColumnsMethods(builder, entity); return builder.build(); } - private void addColumnsMethods(TypeSpec.Builder builder, TypeElement entity) { - builder.addMethod( - MethodSpec.methodBuilder("getGetter") - .returns(ParameterizedTypeName.get(ClassName.get(ColumnGetter.class), ClassName.get(entity), WildcardTypeName.subtypeOf(Object.class))) - .addModifiers(Modifier.PUBLIC) - .addStatement("return this.getter") - .build() - ); - builder.addMethod( - MethodSpec.methodBuilder("getEntityMapper") - .returns(getEntityMapperType(entity)) - .addModifiers(Modifier.STATIC, Modifier.SYNCHRONIZED) - .addCode(buildEntityMapperCodeBlock(entity)) - .build() - ); - builder.addMethod( - MethodSpec.methodBuilder("getSelectables") - .returns(String.class) - .addModifiers(Modifier.STATIC, Modifier.SYNCHRONIZED) - .addCode(buildSelectablesCodeBlock()) - .build() - ); - builder.addMethod( - MethodSpec.methodBuilder("getKeyWheres") - .returns(String.class) - .addModifiers(Modifier.STATIC, Modifier.SYNCHRONIZED) - .addCode(buildKeysWhereCodeBlock()) - .build() - ); - builder.addMethod( - MethodSpec.methodBuilder("getInsertSql") - .returns(String.class) - .addModifiers(Modifier.STATIC, Modifier.SYNCHRONIZED) - .addCode(buildInsertSqlCodeBlock(entity.getAnnotation(Table.class).name(), entity)) - .build() - ); - builder.addMethod( - MethodSpec.methodBuilder("getUpdateSql") - .returns(String.class) - .addModifiers(Modifier.STATIC, Modifier.SYNCHRONIZED) - .addCode(buildUpdateSql(entity.getAnnotation(Table.class).name())) - .build() - ); - builder.addMethod( - MethodSpec.methodBuilder("findColumn") - .addModifiers(Modifier.PUBLIC) - .returns(ClassName.bestGuess("Column")) - .addParameter(String.class, COL_NAME) - .addModifiers(Modifier.STATIC) - .addCode(buildFindColumnCodeBlock()) - .build() - ); - } - - private CodeBlock buildUpdateSql(String table) { + private CodeBlock buildUpdateSqlGenerator(TypeElement entity) { + String table = entity.getAnnotation(Table.class).name(); return CodeBlock.builder() - .beginControlFlow("if (UPDATE_SQL == null)") .addStatement(BUILDER_INSTANCE, StringBuilder.class, StringBuilder.class) .addStatement(BUILDER_SIMPLE_APPEND, "UPDATE " + table + " ") .addStatement(SET_FIRST) - .beginControlFlow("for (int i = 0; i < values().length; i++)") - .addStatement("Column column = values()[i]") + .beginControlFlow("for (int i = 0; i < Column.values().length; i++)") + .addStatement("Column column = Column.values()[i]") .beginControlFlow(CHECK_FIRST) .addStatement(BUILDER_SIMPLE_APPEND, "SET ") .addStatement(RESET_FIRST) .endControlFlow() .addStatement("builder.append(column.colName).append($S)", " = ?") - .beginControlFlow("if (i != values().length - 1)") + .beginControlFlow("if (i != Column.values().length - 1)") .addStatement(BUILDER_SIMPLE_APPEND, ", ") .endControlFlow() .endControlFlow() .addStatement("UPDATE_SQL = builder.toString()") - .endControlFlow() - .addStatement("return UPDATE_SQL") .build(); } - private CodeBlock buildInsertSqlCodeBlock(String table, TypeElement entity) { + private CodeBlock buildInsertSqlGenerator(TypeElement entity) { + String table = entity.getAnnotation(Table.class).name(); return CodeBlock.builder() - .beginControlFlow("if (INSERT_SQL == null)") .addStatement("$T genInstance = $T.getInstance()", GeneratorsService.class, GeneratorsService.class) .addStatement(BUILDER_INSTANCE, StringBuilder.class, StringBuilder.class) .addStatement(BUILDER_SIMPLE_APPEND, "INSERT INTO " + table + " (") - .beginControlFlow("for (int i = 0; i < values().length; i++)") - .addStatement("Column column = values()[i]") + .beginControlFlow("for (int i = 0; i < Column.values().length; i++)") + .addStatement("Column column = Column.values()[i]") .beginControlFlow("if (column.autoGenerated && !genInstance.canGenerateValue($T.class, column.colName))", entity) .addStatement("continue") .endControlFlow() .addStatement("builder.append(column.colName)") - .beginControlFlow("if (i != values().length - 1)") + .beginControlFlow("if (i != Column.values().length - 1)") .addStatement(BUILDER_SIMPLE_APPEND, ",") .endControlFlow() .endControlFlow() - .addStatement("String wildcards = Stream.of(values()).filter(c -> !c.autoGenerated || genInstance.canGenerateValue($T.class, c.colName)).map(c -> $S).collect($T.joining($S))", entity, "?", Collectors.class, ",") + .addStatement("String wildcards = Stream.of(Column.values()).filter(c -> !c.autoGenerated || genInstance.canGenerateValue($T.class, c.colName)).map(c -> $S).collect($T.joining($S))", entity, "?", Collectors.class, ",") .addStatement("builder.append($S).append(wildcards).append($S)", ") VALUES (", ")") .addStatement("INSERT_SQL = builder.toString()") - .endControlFlow() - .addStatement("return INSERT_SQL") - .build(); - } - - private CodeBlock buildFindColumnCodeBlock() { - return CodeBlock.builder() - .beginControlFlow("for (Column col : values())") - .beginControlFlow("if (col.colName.equals(colName))") - .addStatement("return col") - .endControlFlow() - .endControlFlow() - .addStatement("throw new $T($S + colName)", IllegalArgumentException.class, "Can't find column ") .build(); } - private CodeBlock buildKeysWhereCodeBlock() { + private CodeBlock buildKeysWhereGenerator() { return CodeBlock.builder() - .beginControlFlow("if (KEYS_WHERE == null)") - .addStatement("$L keys = $T.of(values()).filter(col -> col.key).map(col -> col.colName).collect($T.toList())", + .addStatement("$L keys = $T.of(Column.values()).filter(col -> col.key).map(col -> col.colName).collect($T.toList())", ParameterizedTypeName.get(List.class, String.class), Stream.class, Collectors.class) .addStatement(SET_FIRST) .addStatement(BUILDER_INSTANCE, StringBuilder.class, StringBuilder.class) @@ -611,31 +714,53 @@ private CodeBlock buildKeysWhereCodeBlock() { .endControlFlow() .endControlFlow() .addStatement("KEYS_WHERE = builder.toString()") - .endControlFlow() - .addStatement("return KEYS_WHERE") .build(); } - private CodeBlock buildSelectablesCodeBlock() { + private CodeBlock buildSelectablesGenerator() { return CodeBlock.builder() - .beginControlFlow("if (SELECTABLES == null)") - .addStatement("SELECTABLES = $T.of(values()).map(col -> col.colName).collect($T.joining($S))", + .addStatement("SELECTABLES = $T.of(Column.values()).map(col -> col.colName).collect($T.joining($S))", Stream.class, Collectors.class, ",") - .endControlFlow() - .addStatement("return SELECTABLES") .build(); } - private CodeBlock buildEntityMapperCodeBlock(TypeElement entity) { + private CodeBlock buildEntityMapperGenerator(TypeElement entity) { return CodeBlock.builder() - .beginControlFlow("if (ENTITY_MAPPER == null)") .addStatement("$T.Builder<$T> builder = new $T.Builder<>()", EntityMapper.class, entity, EntityMapper.class) .beginControlFlow("for (Column col : Column.values())") .addStatement("builder.add(col.colName, col.type, col.setter, col.getter, col.key, col.autoGenerated)") .endControlFlow() .addStatement("ENTITY_MAPPER = builder.build()") + .build(); + } + + private void addColumnsMethods(TypeSpec.Builder builder, TypeElement entity) { + builder.addMethod( + MethodSpec.methodBuilder("getGetter") + .returns(ParameterizedTypeName.get(ClassName.get(ColumnGetter.class), ClassName.get(entity), WildcardTypeName.subtypeOf(Object.class))) + .addModifiers(Modifier.PUBLIC) + .addStatement("return this.getter") + .build() + ); + builder.addMethod( + MethodSpec.methodBuilder("findColumn") + .addModifiers(Modifier.PUBLIC) + .returns(ClassName.bestGuess("Column")) + .addParameter(String.class, COL_NAME) + .addModifiers(Modifier.STATIC) + .addCode(buildFindColumnCodeBlock()) + .build() + ); + } + + private CodeBlock buildFindColumnCodeBlock() { + return CodeBlock.builder() + .beginControlFlow("for (Column col : values())") + .beginControlFlow("if (col.colName.equals(colName))") + .addStatement("return col") .endControlFlow() - .addStatement("return ENTITY_MAPPER") + .endControlFlow() + .addStatement("throw new $T($S + colName)", IllegalArgumentException.class, "Can't find column ") .build(); } @@ -704,6 +829,19 @@ private String getPackage(TypeElement entity) { return ClassName.get(entity).packageName(); } + private static class JoinInfo { + + private final Element field; + private final ExecutableElement getter; + private final ExecutableElement setter; + + private JoinInfo(Element field, ExecutableElement getter, ExecutableElement setter) { + this.field = field; + this.getter = getter; + this.setter = setter; + } + } + private static class Accessor { private final String name; @@ -736,8 +874,8 @@ private static Accessor of(ProcessingEnvironment processingEnvironment, Element ExecutableElement getter = ProcessorUtils.findGetter(processingEnvironment, entity, element.getSimpleName()); ExecutableElement setter = ProcessorUtils.findSetter(processingEnvironment, entity, element.getSimpleName()); if (converter != null) { - converterInstance = ProcessorUtils.getConverterCaller(processingEnvironment, (VariableElement) element); - beforeConverterClass = ProcessorUtils.getConverterTypes(processingEnvironment, (VariableElement) element) + converterInstance = ProcessorUtils.getConverterCaller(processingEnvironment, element); + beforeConverterClass = ProcessorUtils.getConverterTypes(processingEnvironment, element) .get(0); } return new Accessor( diff --git a/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/ProjectionsGenerator.java b/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/ProjectionsGenerator.java index ca602a08..9d3309e4 100644 --- a/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/ProjectionsGenerator.java +++ b/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/ProjectionsGenerator.java @@ -67,7 +67,7 @@ private GeneratedFile toProjection(TypeElement element) { private Iterable generateDelegations(TypeElement element) { List methods = ProcessorUtils.getAllMethods(processingEnvironment, element); return methods.stream() - .map(m -> ProcessorUtils.buildDelegateMethod(m, element, false)) + .map(m -> ProcessorUtils.buildDelegateMethod(processingEnvironment, m, element, false, false, null)) .collect(Collectors.toList()); } @@ -102,7 +102,7 @@ private Iterable generateDelegate(TypeElement element, String name) private CodeBlock buildSetEntityCode(TypeElement element) { CodeBlock.Builder builder = CodeBlock.builder(); - List annotated = element.getEnclosedElements() + List annotated = ProcessorUtils.getAllValidElements(processingEnvironment, element) .stream() .filter(e -> e.getAnnotation(Column.class) != null) .map(VariableElement.class::cast) diff --git a/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/QueryGenerator.java b/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/QueryGenerator.java index bfcd1b2c..9a13c10a 100644 --- a/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/QueryGenerator.java +++ b/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/QueryGenerator.java @@ -324,9 +324,9 @@ private CodeBlock getReplaceCollection(String name) { private Map.Entry checkType(List entities, String sql, ExecutableElement method, boolean collectionNames) { String sqlParam = collectionNames ? "$L" : "$S"; - if (sql.toUpperCase().startsWith("SELECT")) { + if (isValidSelect(sql)) { return checkSelect(entities, sql, method, collectionNames, sqlParam); - } else if (sql.toUpperCase().startsWith("DELETE")) { + } else if (sql.trim().toUpperCase().startsWith("DELETE")) { return new AbstractMap.SimpleImmutableEntry<>(String.format("$T.getSimple().delete(%s, params)", sqlParam), new Object[] {QueryRunner.class, collectionNames ? "sql" : sql}); } else { @@ -335,6 +335,10 @@ private Map.Entry checkType(List entities, String } } + private boolean isValidSelect(String sql) { + return sql.trim().toUpperCase().startsWith("SELECT") || sql.trim().toUpperCase().startsWith("WITH"); + } + private Map.Entry checkSelect(List entities, String sql, ExecutableElement method, boolean collectionName, String sqlParam) { ReturnTypeDefinition definition = new ReturnTypeDefinition(processingEnvironment, method.getReturnType()); diff --git a/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/RelationshipGenerator.java b/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/RelationshipGenerator.java index f468a942..7e642068 100644 --- a/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/RelationshipGenerator.java +++ b/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/generation/impl/RelationshipGenerator.java @@ -4,13 +4,11 @@ import com.squareup.javapoet.ClassName; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.TypeSpec; -import io.github.ulisse1996.jaorm.annotation.Cascade; -import io.github.ulisse1996.jaorm.annotation.CascadeType; -import io.github.ulisse1996.jaorm.annotation.Converter; -import io.github.ulisse1996.jaorm.annotation.Table; +import io.github.ulisse1996.jaorm.annotation.*; import io.github.ulisse1996.jaorm.entity.converter.ParameterConverter; import io.github.ulisse1996.jaorm.entity.relationship.EntityEventType; import io.github.ulisse1996.jaorm.entity.relationship.Relationship; +import io.github.ulisse1996.jaorm.entity.relationship.RelationshipManager; import io.github.ulisse1996.jaorm.processor.exception.ProcessorException; import io.github.ulisse1996.jaorm.processor.generation.Generator; import io.github.ulisse1996.jaorm.processor.util.GeneratedFile; @@ -25,6 +23,7 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import java.util.*; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -116,13 +115,15 @@ private Iterable getProviderMethods(RelationshipInfo info) { String events; EntityEventType[] values = getEvents(relationship); events = asVarArgs(values); - builder.addStatement("rel.add(new $T<>($T.class, e -> (($T)e).$L(), $L, $L, $L))", + builder.addStatement("rel.add(new $T<>($T.class, e -> (($T)e).$L(), $L, $L, $S, $L, $L))", Relationship.Node.class, relationship.returnTypeDefinition.getRealClass(), info.entity, relationship.getter.getSimpleName(), relationship.returnTypeDefinition.isOptional() ? "true" : "false", relationship.returnTypeDefinition.isCollection() ? "true" : "false", + relationship.relationship.getSimpleName(), + getKeys(info.entity, relationship), events ); addAutoSetNode(info.entity, relationship, builder); @@ -131,6 +132,34 @@ private Iterable getProviderMethods(RelationshipInfo info) { return Arrays.asList(getEntityClass, builder.addStatement("return rel").build()); } + private String getKeys(TypeElement entity, RelationshipAccessor relationship) { + io.github.ulisse1996.jaorm.annotation.Relationship currRel = relationship.relationship.getAnnotation(io.github.ulisse1996.jaorm.annotation.Relationship.class); + Set relColumns = Stream.of(currRel.columns()) + .map(io.github.ulisse1996.jaorm.annotation.Relationship.RelationshipColumn::sourceColumn) + .filter(s -> !s.isEmpty()) + .collect(Collectors.toSet()); + List entityKeys = ProcessorUtils.getAllValidElements(processingEnvironment, entity) + .stream() + .filter(el -> el.getKind().isField()) + .filter(el -> el.getAnnotation(Id.class) != null) + .map(el -> el.getAnnotation(Column.class).name()) + .collect(Collectors.toList()); + List keys = new ArrayList<>(); + for (String key : entityKeys) { + if (relColumns.contains(key)) { + keys.add(key); + } + } + if (keys.size() == 0) { + return String.format("%s.emptyList()", Collections.class.getName()); + } else if (keys.size() == 1) { + return String.format("%s.singletonList(\"%s\")", Collections.class.getName(), keys.get(0)); + } else { + return String.format("%s.asList(%s)", Arrays.class.getName(), + keys.stream().map(el -> String.format("\"%s\"", el)).collect(Collectors.joining(","))); + } + } + private String getPackage(TypeElement entity) { return ClassName.get(entity).packageName(); } @@ -139,20 +168,24 @@ private void addAutoSetNode(TypeElement entity, RelationshipAccessor relationshi TypeElement rel = relationship.returnTypeDefinition.getRealClass(); VariableElement variable = relationship.relationship; io.github.ulisse1996.jaorm.annotation.Relationship currRel = variable.getAnnotation(io.github.ulisse1996.jaorm.annotation.Relationship.class); - Map> params = new HashMap<>(); + RelationshipStatements params = new RelationshipStatements(); for (io.github.ulisse1996.jaorm.annotation.Relationship.RelationshipColumn column : currRel.columns()) { VariableElement toSet = ProcessorUtils.getFieldWithColumnName(processingEnvironment, rel, column.targetColumn()); if (column.sourceColumn().isEmpty()) { if (toSet.getAnnotation(Converter.class) == null) { params.put( - "(($T)link).$L($T.$L.toValue($S))", + "$T.applyCallback($S, null, link, null, $T.class, $T::$L, null, $T.$L.toValue($S))", Arrays.asList( + RelationshipManager.class, + String.format("%s -> %s", entity.getSimpleName(), rel.getSimpleName()), + rel, rel, ProcessorUtils.findSetter(processingEnvironment, rel, toSet.getSimpleName()).getSimpleName(), ParameterConverter.class, column.converter().name(), column.defaultValue() - )); + ) + ); } else { params.put( "(($T)link).$L($L.fromSql($T.$L.toValue($S)))", @@ -168,29 +201,65 @@ private void addAutoSetNode(TypeElement entity, RelationshipAccessor relationshi } else { VariableElement fromSet = ProcessorUtils.getFieldWithColumnName(processingEnvironment, entity, column.sourceColumn()); if (fromSet.getAnnotation(Converter.class) == null || noNeedForConversion(fromSet, toSet)) { + // Setter from params.put( - "(($T)link).$L((($T)entity).$L())", + "$T.applyCallback($S, link, entity, $T.class, $T.class, $T::$L, $T::$L, null)", Arrays.asList( + RelationshipManager.class, + String.format("%s -> %s", entity.getSimpleName(), rel.getSimpleName()), + entity, + rel, rel, ProcessorUtils.findSetter(processingEnvironment, rel, toSet.getSimpleName()).getSimpleName(), entity, ProcessorUtils.findGetter(processingEnvironment, entity, fromSet.getSimpleName()).getSimpleName() - )); + ) + ); + + // Setter to + params.put( + "$T.applyCallback($S, entity, link, $T.class, $T.class, $T::$L, $T::$L, null)", + Arrays.asList( + RelationshipManager.class, + String.format("%s -> %s", rel.getSimpleName(), entity.getSimpleName()), + rel, + entity, + entity, + ProcessorUtils.findSetter(processingEnvironment, entity, fromSet.getSimpleName()).getSimpleName(), + rel, + ProcessorUtils.findGetter(processingEnvironment, rel, toSet.getSimpleName()).getSimpleName() + ) + ); } else { params.put( - "(($T)link).$L($L.toSql((($T)entity).$L()))", + "$T.applyCallback($S, link, entity, $T.class, $T.class, $T::$L, (t) -> $L.fromSql(t).$L(), null)", Arrays.asList( + RelationshipManager.class, + String.format("%s -> %s", entity.getSimpleName(), rel.getSimpleName()), + entity, + rel, rel, ProcessorUtils.findSetter(processingEnvironment, rel, toSet.getSimpleName()).getSimpleName(), - ProcessorUtils.getConverterCaller(processingEnvironment, fromSet), - entity, + handleExceptionInfoPropagation(fromSet, toSet, () -> ProcessorUtils.getConverterCaller(processingEnvironment, toSet)), ProcessorUtils.findGetter(processingEnvironment, entity, fromSet.getSimpleName()).getSimpleName() - )); + ) + ); } } } - params.forEach((code, p) -> builder.addStatement( - String.format("rel.getLast().appendThen((entity, link) -> %s)", code), p.toArray())); + params.statements.forEach((e) -> builder.addStatement( + String.format("rel.getLast().appendThen((entity, link) -> %s)", e.statement), e.params.toArray())); + } + + private String handleExceptionInfoPropagation(VariableElement fromSet, VariableElement toSet, Supplier converterCallerGetter) { + try { + return converterCallerGetter.get(); + } catch (ProcessorException ex) { + if (toSet.getAnnotation(Converter.class) == null) { + throw new ProcessorException(String.format("%s referenced by %s.%s", ex.getMessage(), fromSet.getEnclosingElement().getSimpleName(), fromSet.getSimpleName())); + } + throw ex; + } } private boolean noNeedForConversion(VariableElement fromSet, VariableElement toSet) { @@ -206,6 +275,8 @@ private EntityEventType[] getEvents(RelationshipAccessor relationship) { events.add(EntityEventType.REMOVE); } else if (cascadeType.equals(CascadeType.PERSIST)) { events.add(EntityEventType.PERSIST); + } else if (cascadeType.equals(CascadeType.MERGE)) { + events.add(EntityEventType.MERGE); } else { events.add(EntityEventType.UPDATE); } @@ -276,4 +347,26 @@ private RelationshipAccessor(ReturnTypeDefinition returnTypeDefinition, this.relationship = relationship; } } + + private static class RelationshipStatements { + private final List statements; + + public RelationshipStatements() { + this.statements = new ArrayList<>(); + } + + public void put(String statement, List params) { + this.statements.add(new RelationshipStatement(statement, params)); + } + } + + private static class RelationshipStatement { + private final String statement; + private final List params; + + public RelationshipStatement(String statement, List params) { + this.statement = statement; + this.params = params; + } + } } diff --git a/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/strategy/QueryStrategy.java b/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/strategy/QueryStrategy.java index 98e23b5a..07937fd9 100644 --- a/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/strategy/QueryStrategy.java +++ b/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/strategy/QueryStrategy.java @@ -170,8 +170,13 @@ public String getRegex() { } private static String replaceNamedQuery(String regex, String query, Set collectionNames) { + List words = getWords(regex, query); return Pattern.compile(regex).matcher(query).replaceAll(matchResult -> { - String res = matchResult.group().substring(1); + String matched = matchResult.group(); + String res = matched.substring(1); + if (!words.contains(res)) { + return matched; + } if (collectionNames.contains(res)) { return String.format("{%s}", res); } @@ -193,15 +198,29 @@ private static VariableElement getVariableElement(ExecutableElement method, Stri .orElseThrow(() -> new ProcessorException("Can't find parameter with name " + s)); } - static List getWords(String regex, String query) { + public static List getWords(String regex, String query) { List foundWords = new ArrayList<>(); Matcher matcher = Pattern.compile(regex).matcher(query); - while (matcher.find()) { - foundWords.add(matcher.group().substring(1)); + List matches = toList(matcher); + for (int i = 0; i < matches.size(); i++) { + String matched = matches.get(i); + if (matched.isEmpty()) { + i++; // Skip next + continue; // We don't want to match special functions likes ::date used for example in Postgre + } + foundWords.add(matched); } return foundWords; } + private static List toList(Matcher matcher) { + List values = new ArrayList<>(); + while (matcher.find()) { + values.add(matcher.group().substring(1)); + } + return values; + } + private static CodeBlock getListStatement(VariableElement element, TypeMirror type, ProcessingEnvironment environment) { TypeElement typeElement = ProcessorUtils.extractTypeFromCollection(environment, type); return CodeBlock.builder() diff --git a/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/util/ProcessorUtils.java b/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/util/ProcessorUtils.java index 5b860a5c..1e743bd3 100644 --- a/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/util/ProcessorUtils.java +++ b/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/util/ProcessorUtils.java @@ -1,14 +1,18 @@ package io.github.ulisse1996.jaorm.processor.util; +import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.TypeName; import io.github.ulisse1996.jaorm.BaseDao; import io.github.ulisse1996.jaorm.annotation.Column; import io.github.ulisse1996.jaorm.annotation.Converter; import io.github.ulisse1996.jaorm.annotation.Dao; import io.github.ulisse1996.jaorm.annotation.Query; import io.github.ulisse1996.jaorm.entity.Result; +import io.github.ulisse1996.jaorm.entity.TrackedList; +import io.github.ulisse1996.jaorm.entity.converter.EnumConverter; import io.github.ulisse1996.jaorm.entity.converter.ValueConverter; import io.github.ulisse1996.jaorm.external.LombokMock; import io.github.ulisse1996.jaorm.external.LombokSupport; @@ -18,7 +22,13 @@ import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.RoundEnvironment; -import javax.lang.model.element.*; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.Name; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; import javax.lang.model.type.MirroredTypeException; import javax.lang.model.type.NoType; import javax.lang.model.type.PrimitiveType; @@ -38,7 +48,16 @@ import java.nio.file.StandardOpenOption; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -import java.util.*; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import java.util.function.Predicate; @@ -205,11 +224,11 @@ public static PrimitiveType getUnboxed(ProcessingEnvironment processingEnvironme public static List getGenericTypes(ProcessingEnvironment processingEnvironment, TypeMirror mirror, String name) { TypeElement typeElement = (TypeElement) processingEnvironment.getTypeUtils().asElement(mirror); TypeMirror converterType = typeElement.getInterfaces().get(0); - return getParameters(processingEnvironment, converterType, name); + return getParameters(processingEnvironment, converterType.toString(), name); } - private static List getParameters(ProcessingEnvironment processingEnvironment, TypeMirror mirror, String name) { - String[] param = mirror.toString().replace(name, "") + private static List getParameters(ProcessingEnvironment processingEnvironment, String mirror, String name) { + String[] param = mirror.replace(name, "") .replace(STARTING_GENERIC, "").replace(ENDING_GENERIC, "") .split(","); return Stream.of(param) @@ -291,12 +310,33 @@ public static List getConverterTypes(ProcessingEnvironment processi } else { converterClass = element.asType(); } + TypeElement typeElement = (TypeElement) processingEnvironment.getTypeUtils().asElement(converterClass); + if (typeElement.getInterfaces().isEmpty()) { + return extractSuperClassTypes(processingEnvironment, typeElement); + } return getGenericTypes(processingEnvironment, converterClass, ValueConverter.class.getName()); } + private static List extractSuperClassTypes(ProcessingEnvironment processingEnvironment, TypeElement typeElement) { + TypeMirror superclass = typeElement.getSuperclass(); + if (!superclass.toString().contains(EnumConverter.class.getName())) { + throw new ProcessorException("ValueConverter must be a direct interface of the converter! Please implement the interface or use EnumConverter for simple Enum-name based conversion"); + } + String param = superclass.toString().replace(EnumConverter.class.getName(), "") + .replace(STARTING_GENERIC, "").replace(ENDING_GENERIC, "") + .split(",")[0]; + String mirror = String.format("%s", ValueConverter.class.getName(), param); + return getParameters(processingEnvironment, mirror, ValueConverter.class.getName()); + } + private static TypeMirror getConverterClass(ProcessingEnvironment environment, VariableElement variableElement) { TypeMirror converterClass; Converter converter = variableElement.getAnnotation(Converter.class); + + if (converter == null) { + throw new ProcessorException(String.format("Can't find converter class for required conversion on %s.%s", variableElement.getEnclosingElement().getSimpleName(), variableElement.getSimpleName())); + } + try { // Only way to get class Class klass = converter.value(); @@ -572,8 +612,17 @@ public static String extractParameterNames(ExecutableElement m) { return ""; } - public static MethodSpec buildDelegateMethod(ExecutableElement m, TypeElement entity, boolean forEntity) { + public static MethodSpec buildDelegateMethod(ProcessingEnvironment environment, + ExecutableElement m, TypeElement entity, + boolean forEntity, boolean wrappedList, ExecutableElement getter) { MethodSpec.Builder builder = MethodSpec.overriding(m) + .addAnnotations( + m.getAnnotationMirrors() + .stream() + .map(AnnotationSpec::get) + .filter(el -> !el.type.equals(TypeName.get(Override.class))) + .collect(Collectors.toList()) + ) .addStatement(REQUIRE_NON_NULL, Objects.class); String variables = ProcessorUtils.extractParameterNames(m); @@ -581,15 +630,32 @@ public static MethodSpec buildDelegateMethod(ExecutableElement m, TypeElement en builder.addCode(buildCustomEquals(m.getParameters().get(0).getSimpleName().toString(), entity)); } else if (m.getReturnType() instanceof NoType) { if (forEntity) { - builder.addStatement("this.modified = true"); + if (!m.getParameters().isEmpty()) { + String modifiedGetter = ProcessorUtils.findGetter(environment, entity, m.getParameters().get(0).getSimpleName()) + .getSimpleName().toString(); + builder.addStatement("this.modified = this.modified || !$T.equals($L(), $L)", Objects.class, modifiedGetter, m.getParameters().get(0).getSimpleName().toString()); + } else { + builder.addStatement("this.modified = true"); + } } - builder.addStatement("this.entity.$L($L)", m.getSimpleName(), variables); + buildWrappedList(m, wrappedList, getter, builder, variables); } else { builder.addStatement("return this.entity.$L($L)", m.getSimpleName(), variables); } return builder.build(); } + private static void buildWrappedList(ExecutableElement m, boolean wrappedList, ExecutableElement getter, MethodSpec.Builder builder, String variables) { + if (wrappedList && getter != null) { + builder.addStatement("this.entity.$L($T.merge(this, this.entity.$L(), $L))", m.getSimpleName(), TrackedList.class, getter.getSimpleName(), variables); + } else { + if (getter != null) { + builder.addStatement("this.tracker.registerRemoved(this.entity.$L())", getter.getSimpleName()); + } + builder.addStatement("this.entity.$L($L)", m.getSimpleName(), variables); + } + } + private static CodeBlock buildCustomEquals(String paramName, TypeElement entity) { return CodeBlock.builder() .beginControlFlow("if (getClass().isInstance($L))", paramName) diff --git a/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/validation/impl/ConverterProviderValidator.java b/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/validation/impl/ConverterProviderValidator.java index 9a3acc6f..453364c3 100644 --- a/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/validation/impl/ConverterProviderValidator.java +++ b/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/validation/impl/ConverterProviderValidator.java @@ -1,5 +1,6 @@ package io.github.ulisse1996.jaorm.processor.validation.impl; +import io.github.ulisse1996.jaorm.entity.converter.EnumConverter; import io.github.ulisse1996.jaorm.entity.converter.ValueConverter; import io.github.ulisse1996.jaorm.processor.exception.ProcessorException; import io.github.ulisse1996.jaorm.processor.validation.Validator; @@ -21,7 +22,8 @@ public void validate(List annotated) { TypeElement typeElement = (TypeElement) element; TypeElement converterElement = processingEnvironment.getElementUtils().getTypeElement(ValueConverter.class.getName()); if (typeElement.getInterfaces().stream() - .noneMatch(el -> processingEnvironment.getTypeUtils().asElement(el).equals(converterElement))) { + .noneMatch(el -> processingEnvironment.getTypeUtils().asElement(el).equals(converterElement)) && + !typeElement.getSuperclass().toString().contains(EnumConverter.class.getName())) { throw new ProcessorException(String.format("Type %s is not a valid ValueConverter instance !", typeElement.getSimpleName())); } } diff --git a/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/validation/impl/QueryValidator.java b/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/validation/impl/QueryValidator.java index bc4c1f08..5cccf163 100644 --- a/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/validation/impl/QueryValidator.java +++ b/jaorm-processor/src/main/java/io/github/ulisse1996/jaorm/processor/validation/impl/QueryValidator.java @@ -14,14 +14,9 @@ import io.github.ulisse1996.jaorm.specialization.TripleKeyDao; import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.element.Element; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.TypeElement; +import javax.lang.model.element.*; import javax.lang.model.type.TypeKind; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Optional; +import java.util.*; import java.util.stream.Collectors; public class QueryValidator extends Validator { @@ -86,11 +81,12 @@ private void checkQuery(ExecutableElement executableElement) { debugMessage("Check validation for Query " + executableElement.getSimpleName()); Query query = executableElement.getAnnotation(Query.class); String sql = getSql(query); + debugMessage("Processing query: " + sql); for (QueryStrategy queryStrategy : QueryStrategy.values()) { if (queryStrategy.isValid(sql, query.noArgs())) { int paramNumber = queryStrategy.getParamNumber(sql); if (paramNumber != executableElement.getParameters().size()) { - throw new ProcessorException("Mismatch between parameters and query parameters for method " + executableElement.getSimpleName()); + throw mismatchParameters(queryStrategy, executableElement, sql); } checkSpecs(sql, executableElement); if (executableElement.getAnnotation(ExcludeExternalValidation.class) == null) { @@ -108,21 +104,38 @@ private void checkQuery(ExecutableElement executableElement) { throw new ProcessorException(String.format("Can't find query strategy for method %s", executableElement.getSimpleName())); } + private ProcessorException mismatchParameters(QueryStrategy queryStrategy, ExecutableElement executableElement, String sql) { + if (queryStrategy.getRegex() != null) { + Set words = new HashSet<>(QueryStrategy.getWords(queryStrategy.getRegex(), sql)); + List params = executableElement.getParameters().stream() + .map(VariableElement::getSimpleName) + .map(Name::toString) + .collect(Collectors.toList()); + return new ProcessorException(String.format("Mismatch between parameters and query parameters for methods %s! Found: %s, required %s", + executableElement.getSimpleName(), words, params)); + } else { + int paramNumber = queryStrategy.getParamNumber(sql); + return new ProcessorException(String.format("Mismatch between parameters and query parameters for method %s! Found: %d, required %d", + executableElement.getSimpleName(), executableElement.getParameters().size(), paramNumber)); + } + } + private String getSql(Query query) { String sql = query.sql(); - return ProcessorUtils.getSqlOrSqlFromFile(sql, this.processingEnvironment); + sql = ProcessorUtils.getSqlOrSqlFromFile(sql, this.processingEnvironment); + return sql.trim(); } private void checkSpecs(String sql, ExecutableElement method) { - if (sql.toUpperCase().startsWith("SELECT")) { + if (isValidSelect(sql)) { checkReturnMethod(method); - return; - } else if (sql.toUpperCase().startsWith("DELETE") || sql.toUpperCase().startsWith("UPDATE")) { + } else if (sql.trim().toUpperCase().startsWith("DELETE") || sql.trim().toUpperCase().startsWith("UPDATE")) { assertVoid(method); - return; } + } - throw new ProcessorException(String.format("Operation not supported for sql %s in method %s", sql, method)); + private boolean isValidSelect(String sql) { + return sql.trim().toUpperCase().startsWith("SELECT") || sql.trim().toUpperCase().startsWith("WITH"); } private void checkReturnMethod(ExecutableElement method) { diff --git a/jaorm-processor/src/test/java/io/github/ulisse1996/jaorm/processor/generation/impl/EntityGeneratorTest.java b/jaorm-processor/src/test/java/io/github/ulisse1996/jaorm/processor/generation/impl/EntityGeneratorTest.java index e9356dc3..954eca71 100644 --- a/jaorm-processor/src/test/java/io/github/ulisse1996/jaorm/processor/generation/impl/EntityGeneratorTest.java +++ b/jaorm-processor/src/test/java/io/github/ulisse1996/jaorm/processor/generation/impl/EntityGeneratorTest.java @@ -78,4 +78,15 @@ void should_generate_entity_with_converter() { ) ); } + + @Test + void should_generate_enum_with_abstract_class() { + checkCompilation( + Compiler.javac() + .withProcessors(new JaormProcessor()) + .compile( + getFile("entity", "EntityWithEnum.java") + ) + ); + } } diff --git a/jaorm-processor/src/test/java/io/github/ulisse1996/jaorm/processor/util/ProcessorUtilsTest.java b/jaorm-processor/src/test/java/io/github/ulisse1996/jaorm/processor/util/ProcessorUtilsTest.java index 12184d8d..eb8a559f 100644 --- a/jaorm-processor/src/test/java/io/github/ulisse1996/jaorm/processor/util/ProcessorUtilsTest.java +++ b/jaorm-processor/src/test/java/io/github/ulisse1996/jaorm/processor/util/ProcessorUtilsTest.java @@ -860,7 +860,7 @@ void should_return_annotated_field_in_super_class() { Mockito.when(variableElement.getSimpleName()) .thenReturn(nameOf("variable")); Mockito.when(variableElement.getAnnotation(Mockito.any())) - .then(invocation -> Mockito.mock(invocation.getArgument(0))); + .then(invocation -> Mockito.mock(Annotation.class)); List found = ProcessorUtils.getAnnotated(environment, element, Column.class); Assertions.assertEquals(Collections.singletonList(variableElement), found); } @@ -1011,7 +1011,7 @@ void should_throw_exception_during_spi_creation_with_nio() throws IOException { Mockito.when(provider.newByteChannel(Mockito.any(), Mockito.any())) .thenThrow(IOException.class); Mockito.doThrow(IOException.class) - .when(provider).checkAccess(Mockito.any(), Mockito.any()); + .when(provider).checkAccess(Mockito.any()); Assertions.assertThrows(ProcessorException.class, //NOSONAR () -> ProcessorUtils.generateSpi(environment, Collections.singletonList(file), Object.class)); //NOSONAR diff --git a/jaorm-processor/src/test/java/io/github/ulisse1996/jaorm/processor/validation/impl/EntityValidatorTest.java b/jaorm-processor/src/test/java/io/github/ulisse1996/jaorm/processor/validation/impl/EntityValidatorTest.java index 5c41baae..5c27329b 100644 --- a/jaorm-processor/src/test/java/io/github/ulisse1996/jaorm/processor/validation/impl/EntityValidatorTest.java +++ b/jaorm-processor/src/test/java/io/github/ulisse1996/jaorm/processor/validation/impl/EntityValidatorTest.java @@ -140,7 +140,7 @@ void should_throw_exception_for_missing_getter() { VariableElement column = Mockito.mock(VariableElement.class); Mockito.when(column.getSimpleName()) .thenReturn(new CustomName("col1")); - mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any())) + mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(Collections.singletonList(column)); mk.when(() -> ProcessorUtils.findGetterOpt(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(Optional.empty()); @@ -164,7 +164,7 @@ void should_throw_exception_for_missing_setter() { VariableElement column = Mockito.mock(VariableElement.class); Mockito.when(column.getSimpleName()) .thenReturn(new CustomName("col1")); - mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any())) + mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(Collections.singletonList(column)); mk.when(() -> ProcessorUtils.findGetterOpt(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(Optional.of(Mockito.mock(ExecutableElement.class))); @@ -190,7 +190,7 @@ void should_validate_entity_with_valid_columns() { VariableElement column = Mockito.mock(VariableElement.class); Mockito.when(column.getSimpleName()) .thenReturn(new CustomName("col1")); - mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any())) + mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(Collections.singletonList(column)); mockGetterAndSetter(mk); testSubject.validate(Collections.singletonList(entity)); @@ -211,7 +211,7 @@ void should_throw_exception_for_missing_table_annotation_on_relationship_entity( mockGetterAndSetter(mk); Mockito.when(relField.getAnnotation(Relationship.class)) .thenReturn(Mockito.mock(Relationship.class)); - mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any())) + mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(Collections.singletonList(relField)); mk.when(() -> ProcessorUtils.getFieldType(Mockito.any(), Mockito.any())) .thenReturn(relEntity); @@ -227,7 +227,9 @@ void should_throw_exception_for_missing_table_annotation_on_relationship_entity( testSubject.validate(Collections.singletonList(entity)); } catch (ProcessorException ex) { Assertions.assertTrue(ex.getMessage().contains("Type RelEntity is not a valid Entity at field col1 in Entity Entity")); + return; } + Assertions.fail("Should throw exception !"); } @Test @@ -242,7 +244,7 @@ void should_validate_entity_with_a_valid_relationship_entity() { mockGetterAndSetter(mk); Mockito.when(relField.getAnnotation(Relationship.class)) .thenReturn(Mockito.mock(Relationship.class)); - mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any())) + mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(Collections.singletonList(relField)); mk.when(() -> ProcessorUtils.getFieldType(Mockito.any(), Mockito.any())) .thenReturn(relEntity); @@ -280,7 +282,7 @@ void should_throw_exception_for_wrong_converter_type() { return null; } }); - mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any())) + mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(Collections.singletonList(field)); mk.when(() -> ProcessorUtils.getConverterTypes(Mockito.any(), Mockito.any())) .thenReturn(Arrays.asList(gen1, gen2)); @@ -327,7 +329,7 @@ void should_validate_field_with_valid_converter_type() { return null; } }); - mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any())) + mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(Collections.singletonList(field)); mk.when(() -> ProcessorUtils.getConverterTypes(Mockito.any(), Mockito.any())) .thenReturn(Arrays.asList(gen1, gen2)); @@ -368,7 +370,7 @@ void should_throw_exception_for_default_temporal_with_a_wrong_type() { return null; } }); - mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any())) + mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(Collections.singletonList(field)); mk.when(() -> ProcessorUtils.getFieldType(Mockito.any(), Mockito.any())) .thenReturn(gen); @@ -409,7 +411,7 @@ void should_throw_exception_for_default_temporal_with_format_and_with_a_wrong_ty }); Mockito.when(temporal.format()) .thenReturn("format"); - mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any())) + mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(Collections.singletonList(field)); mk.when(() -> ProcessorUtils.getFieldType(Mockito.any(), Mockito.any())) .thenReturn(gen); @@ -452,7 +454,7 @@ void should_throw_exception_for_default_temporal_with_format_and_empty_value() { .thenReturn("format"); Mockito.when(temporal.value()) .thenReturn(""); - mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any())) + mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(Collections.singletonList(field)); mk.when(() -> ProcessorUtils.getFieldType(Mockito.any(), Mockito.any())) .thenReturn(gen); @@ -495,7 +497,7 @@ void should_validate_entity_with_default_temporal() { .thenReturn("dd-MM-yyyy'T'HH:mm:ss"); Mockito.when(temporal.value()) .thenReturn("20-10-2022T00:00:00"); - mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any())) + mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(Collections.singletonList(field)); mk.when(() -> ProcessorUtils.getFieldType(Mockito.any(), Mockito.any())) .thenReturn(gen); @@ -536,7 +538,7 @@ void should_throw_exception_for_default_temporal_with_format_and_bad_value() { .thenReturn("format"); Mockito.when(temporal.value()) .thenReturn("bad_value"); - mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any())) + mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(Collections.singletonList(field)); mk.when(() -> ProcessorUtils.getFieldType(Mockito.any(), Mockito.any())) .thenReturn(gen); @@ -576,7 +578,7 @@ void should_throw_exception_for_default_string_and_wrong_type() { return null; } }); - mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any())) + mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(Collections.singletonList(field)); mk.when(() -> ProcessorUtils.getFieldType(Mockito.any(), Mockito.any())) .thenReturn(gen); @@ -615,7 +617,7 @@ void should_throw_exception_for_default_numeric_and_wrong_type() { return null; } }); - mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any())) + mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(Collections.singletonList(field)); mk.when(() -> ProcessorUtils.getFieldType(Mockito.any(), Mockito.any())) .thenReturn(gen); @@ -654,7 +656,7 @@ void should_validate_entity_with_default_string() { return null; } }); - mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any())) + mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(Collections.singletonList(field)); mk.when(() -> ProcessorUtils.getFieldType(Mockito.any(), Mockito.any())) .thenReturn(gen); @@ -691,7 +693,7 @@ void should_validate_entity_with_default_numeric() { return null; } }); - mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any())) + mk.when(() -> ProcessorUtils.getAnnotated(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(Collections.singletonList(field)); mk.when(() -> ProcessorUtils.getFieldType(Mockito.any(), Mockito.any())) .thenReturn(gen); diff --git a/jaorm-processor/src/test/resources/entity/EntityWithEnum.java b/jaorm-processor/src/test/resources/entity/EntityWithEnum.java new file mode 100644 index 00000000..9ccd22d1 --- /dev/null +++ b/jaorm-processor/src/test/resources/entity/EntityWithEnum.java @@ -0,0 +1,37 @@ +package io.test; + +import io.github.ulisse1996.jaorm.annotation.Column; +import io.github.ulisse1996.jaorm.annotation.Converter; +import io.github.ulisse1996.jaorm.annotation.ConverterProvider; +import io.github.ulisse1996.jaorm.annotation.Id; +import io.github.ulisse1996.jaorm.annotation.Table; +import io.github.ulisse1996.jaorm.entity.converter.EnumConverter; + +@Table(name = "TABLE") +public class EntityWithEnum { + + @Id + @Column(name = "COL1") + @Converter(MyEnumConverter.class) + private MyEnum col1; + + public MyEnum getCol1() { + return col1; + } + + public void setCol1(MyEnum col1) { + this.col1 = col1; + } + + public enum MyEnum { + TEST + } + + @ConverterProvider + public static class MyEnumConverter extends EnumConverter { + + public MyEnumConverter() { + super(MyEnum.class); + } + } +} \ No newline at end of file diff --git a/jaorm-processor/src/test/resources/entity/RelEntityCustom.java b/jaorm-processor/src/test/resources/entity/RelEntityCustom.java index 18f9d69a..c87653ff 100644 --- a/jaorm-processor/src/test/resources/entity/RelEntityCustom.java +++ b/jaorm-processor/src/test/resources/entity/RelEntityCustom.java @@ -19,13 +19,14 @@ public class RelEntityCustom { private CustomEnum colRel2; @Column(name = "COL_REL_3") - private String colRel3; + @Converter(StringToBigDecimalConverter.class) + private BigDecimal colRel3; - public String getColRel3() { + public BigDecimal getColRel3() { return colRel3; } - public void setColRel3(String colRel3) { + public void setColRel3(BigDecimal colRel3) { this.colRel3 = colRel3; } diff --git a/jaorm-sql-specific/jaorm-sql-specific-db2/pom.xml b/jaorm-sql-specific/jaorm-sql-specific-db2/pom.xml index 41cfea09..eb393fb7 100644 --- a/jaorm-sql-specific/jaorm-sql-specific-db2/pom.xml +++ b/jaorm-sql-specific/jaorm-sql-specific-db2/pom.xml @@ -6,7 +6,7 @@ io.github.ulisse1996 jaorm-sql-specific - 2.0.0-RC2 + 2.0.0-RC3 jaorm-sql-specific-db2 diff --git a/jaorm-sql-specific/jaorm-sql-specific-db2/src/test/java/io/github/ulisse1996/jaorm/vendor/db2/Db2GeneratedKeysSpecificTest.java b/jaorm-sql-specific/jaorm-sql-specific-db2/src/test/java/io/github/ulisse1996/jaorm/vendor/db2/Db2GeneratedKeysSpecificTest.java index 0042e8f8..2a7c83f6 100644 --- a/jaorm-sql-specific/jaorm-sql-specific-db2/src/test/java/io/github/ulisse1996/jaorm/vendor/db2/Db2GeneratedKeysSpecificTest.java +++ b/jaorm-sql-specific/jaorm-sql-specific-db2/src/test/java/io/github/ulisse1996/jaorm/vendor/db2/Db2GeneratedKeysSpecificTest.java @@ -186,6 +186,7 @@ public void setAsciiStream(int parameterIndex, InputStream x, int length) throws } @Override + @Deprecated public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { } diff --git a/jaorm-sql-specific/jaorm-sql-specific-mysql/pom.xml b/jaorm-sql-specific/jaorm-sql-specific-mysql/pom.xml index a7c61f6e..e5c40050 100644 --- a/jaorm-sql-specific/jaorm-sql-specific-mysql/pom.xml +++ b/jaorm-sql-specific/jaorm-sql-specific-mysql/pom.xml @@ -5,7 +5,7 @@ jaorm-sql-specific io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-sql-specific/jaorm-sql-specific-oracle/pom.xml b/jaorm-sql-specific/jaorm-sql-specific-oracle/pom.xml index b638c596..41eded31 100644 --- a/jaorm-sql-specific/jaorm-sql-specific-oracle/pom.xml +++ b/jaorm-sql-specific/jaorm-sql-specific-oracle/pom.xml @@ -5,7 +5,7 @@ jaorm-sql-specific io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-sql-specific/jaorm-sql-specific-postgre/pom.xml b/jaorm-sql-specific/jaorm-sql-specific-postgre/pom.xml index 1e643d34..17193df7 100644 --- a/jaorm-sql-specific/jaorm-sql-specific-postgre/pom.xml +++ b/jaorm-sql-specific/jaorm-sql-specific-postgre/pom.xml @@ -5,7 +5,7 @@ jaorm-sql-specific io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-sql-specific/jaorm-sql-specific-postgre/src/main/java/io/github/ulisse1996/jaorm/vendor/postgre/PostgreMergeSpecific.java b/jaorm-sql-specific/jaorm-sql-specific-postgre/src/main/java/io/github/ulisse1996/jaorm/vendor/postgre/PostgreMergeSpecific.java index 4f20ba32..19c9837d 100644 --- a/jaorm-sql-specific/jaorm-sql-specific-postgre/src/main/java/io/github/ulisse1996/jaorm/vendor/postgre/PostgreMergeSpecific.java +++ b/jaorm-sql-specific/jaorm-sql-specific-postgre/src/main/java/io/github/ulisse1996/jaorm/vendor/postgre/PostgreMergeSpecific.java @@ -16,11 +16,13 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; public class PostgreMergeSpecific extends MergeSpecific { private static final Singleton SERVER_VERSION_SINGLETON = Singleton.instance(); + private final ReentrantLock lock = new ReentrantLock(); @Override public String fromUsing() { @@ -34,13 +36,15 @@ public String appendAdditionalSql() { @Override public boolean isStandardMerge() { - synchronized (PostgreMergeSpecific.class) { - + lock.lock(); + try { if (!SERVER_VERSION_SINGLETON.isPresent()) { SERVER_VERSION_SINGLETON.set(ServerVersion.fromString(fetchVersion())); } return SERVER_VERSION_SINGLETON.get().getMajor() >= 15; + } finally { + lock.unlock(); } } diff --git a/jaorm-sql-specific/jaorm-sql-specific-sql-server/pom.xml b/jaorm-sql-specific/jaorm-sql-specific-sql-server/pom.xml index 34743015..324e4a4a 100644 --- a/jaorm-sql-specific/jaorm-sql-specific-sql-server/pom.xml +++ b/jaorm-sql-specific/jaorm-sql-specific-sql-server/pom.xml @@ -5,7 +5,7 @@ jaorm-sql-specific io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-sql-specific/jaorm-sql-specific-sql-server/src/main/java/io/github/ulisse1996/jaorm/vendor/sqlserver/functions/TrimFunction.java b/jaorm-sql-specific/jaorm-sql-specific-sql-server/src/main/java/io/github/ulisse1996/jaorm/vendor/sqlserver/functions/TrimFunction.java index f6a402ec..259308b3 100644 --- a/jaorm-sql-specific/jaorm-sql-specific-sql-server/src/main/java/io/github/ulisse1996/jaorm/vendor/sqlserver/functions/TrimFunction.java +++ b/jaorm-sql-specific/jaorm-sql-specific-sql-server/src/main/java/io/github/ulisse1996/jaorm/vendor/sqlserver/functions/TrimFunction.java @@ -13,12 +13,14 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; +import java.util.concurrent.locks.ReentrantLock; public class TrimFunction implements VendorFunctionWithParams { private static final Singleton SERVER_VERSION_SINGLETON = Singleton.instance(); private static final char SPACE = ' '; + private final ReentrantLock lock = new ReentrantLock(); private final TrimType type; private final char character; private final Selectable selectable; @@ -77,8 +79,8 @@ private String simpleTrim(String alias) { } private void initServerVersion() { - synchronized (TrimFunction.class) { - + lock.lock(); + try { if (!SERVER_VERSION_SINGLETON.isPresent()) { DataSource dataSource = DataSourceProvider.getCurrent().getDataSource(); try (Connection connection = dataSource.getConnection(); @@ -90,6 +92,8 @@ private void initServerVersion() { throw new IllegalArgumentException("Can't read server version !"); } } + } finally { + lock.unlock(); } } diff --git a/jaorm-sql-specific/pom.xml b/jaorm-sql-specific/pom.xml index d889feb4..44af6175 100644 --- a/jaorm-sql-specific/pom.xml +++ b/jaorm-sql-specific/pom.xml @@ -5,7 +5,7 @@ jaorm-pom io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 pom diff --git a/jaorm-test/jaorm-test-cdi/pom.xml b/jaorm-test/jaorm-test-cdi/pom.xml index 31037ea6..1b79bea1 100644 --- a/jaorm-test/jaorm-test-cdi/pom.xml +++ b/jaorm-test/jaorm-test-cdi/pom.xml @@ -5,7 +5,7 @@ jaorm-test io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-test/jaorm-test-core/pom.xml b/jaorm-test/jaorm-test-core/pom.xml index 074ab79e..3a2f4c5b 100644 --- a/jaorm-test/jaorm-test-core/pom.xml +++ b/jaorm-test/jaorm-test-core/pom.xml @@ -5,7 +5,7 @@ jaorm-test io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/CoreIT.java b/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/CoreIT.java index d1f0e7d3..69cca1de 100644 --- a/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/CoreIT.java +++ b/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/CoreIT.java @@ -6,9 +6,7 @@ import io.github.ulisse1996.jaorm.entity.Page; import io.github.ulisse1996.jaorm.entity.Result; import io.github.ulisse1996.jaorm.integration.test.entity.*; -import io.github.ulisse1996.jaorm.integration.test.query.AutoGenDao; -import io.github.ulisse1996.jaorm.integration.test.query.ProgressiveDao; -import io.github.ulisse1996.jaorm.integration.test.query.UserDAO; +import io.github.ulisse1996.jaorm.integration.test.query.*; import io.github.ulisse1996.jaorm.spi.FeatureConfigurator; import io.github.ulisse1996.jaorm.spi.QueriesService; import org.jetbrains.annotations.NotNull; @@ -16,6 +14,7 @@ import org.junit.jupiter.api.Test; import java.math.BigDecimal; +import java.math.BigInteger; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -25,6 +24,12 @@ public abstract class CoreIT extends AbstractIT { private final UserDAO userDAO = QueriesService.getInstance().getQuery(UserDAO.class); + private final RoleDAO roleDAO = QueriesService.getInstance().getQuery(RoleDAO.class); + private final UserRoleDAO userRoleDAO = QueriesService.getInstance().getQuery(UserRoleDAO.class); + private final CityDAO cityDAO = QueriesService.getInstance().getQuery(CityDAO.class); + private final SellerDao sellerDao = QueriesService.getInstance().getQuery(SellerDao.class); + private final StoreDAO storeDAO = QueriesService.getInstance().getQuery(StoreDAO.class); + private final TransactionDao transactionDao = QueriesService.getInstance().getQuery(TransactionDao.class); @Override protected void afterInit() throws Exception { @@ -113,10 +118,10 @@ void should_update_with_batch() { void should_delete_user() { User user = userDAO.readByKey(99); // 1 - userDAO.delete(user); // 2 + userDAO.delete(user); // 4 Delete Role, Specific, User - Assertions.assertFalse(userDAO.readOptByKey(99).isPresent()); // 3 - assertTotalInvocations(3); + Assertions.assertFalse(userDAO.readOptByKey(99).isPresent()); // 5 + assertTotalInvocations(5); } // CURD - Create @@ -215,16 +220,21 @@ void should_do_insert_for_missing_update() { @Test void should_get_full_user() { User user = createGraph(); + Role role = new Role(); + role.setRoleName("NAME"); + role.setRoleId(1); - userDAO.insert(user); // 3 User, UserRole and UserSpecific + roleDAO.insert(role); // 1 Role - Optional result = User.USER_FULL.fetchOpt(createPair().getKey()); // 4 + userDAO.insert(user); // 4 User, UserRole and UserSpecific + + Optional result = User.USER_FULL.fetchOpt(createPair().getKey()); // 5 Assertions.assertTrue(result.isPresent()); Assertions.assertTrue(result.get().getUserSpecific().isPresent()); Assertions.assertEquals(1, result.get().getRoles().size()); assertSame(user, result.get()); - assertTotalInvocations(4); + assertTotalInvocations(5); } //@Test Should be available when we implement sub graphs @@ -247,6 +257,161 @@ void should_get_full_user_with_roles() { assertTotalInvocations(5); } + // Relationships + @Test + void should_delete_not_fetched_relationships() { + User user = new User(); + Role role = new Role(); + role.setRoleId(1); + role.setRoleName("NAME"); + Role role2 = new Role(); + role2.setRoleId(2); + role2.setRoleName("NAME2"); + Role role3 = new Role(); + role3.setRoleId(3); + role3.setRoleName("NAME3"); + + user.setId(1); + user.setName("NAME"); + user.setRoles(List.of(new UserRole(1, 1), new UserRole(1, 2), new UserRole(1, 3))); + + + roleDAO.insert(List.of(role, role2, role3)); // 3 Roles + userDAO.insert(user); // 7 User, UserRole (3) + + Optional optUserRole = userRoleDAO.readOptByKeys(1, 1); // 8 + Optional optUserRole2 = userRoleDAO.readOptByKeys(2, 1); // 9 + Optional optUserRole3 = userRoleDAO.readOptByKeys(3, 1); // 10 + + Assertions.assertTrue(optUserRole.isPresent()); + Assertions.assertTrue(optUserRole2.isPresent()); + Assertions.assertTrue(optUserRole3.isPresent()); + + User u = userDAO.readByKey(1); // 11 + + userDAO.delete(u); // 14 Delete UserRole (1 query for 3 delete), Delete UserSpecific, Delete User + + optUserRole = userRoleDAO.readOptByKeys(1, 1); // 15 + optUserRole2 = userRoleDAO.readOptByKeys(2, 1); // 16 + optUserRole3 = userRoleDAO.readOptByKeys(3, 1); // 17 + + Assertions.assertFalse(optUserRole.isPresent()); + Assertions.assertFalse(optUserRole2.isPresent()); + Assertions.assertFalse(optUserRole3.isPresent()); + assertTotalInvocations(17); + } + + @Test + void should_delete_not_fetched_relationships_recursively() { + City city = createCityWithStores(); + + cityDAO.insert(city); // 7 City, Store (2), Seller (4) + + Assertions.assertEquals(4, sellerDao.readAll().size()); // 8 + + cityDAO.deleteByKey(1); // 11 City, Store (1), Seller (1) + + Assertions.assertEquals(0, sellerDao.readAll().size()); // 12 + assertTotalInvocations(12); + } + + @Test + void should_merge_city_with_removed_stores() { + City city = createCityWithStores(); + + cityDAO.insert(city); // 7 City, Store (2), Seller (4) + + Assertions.assertEquals(4, sellerDao.readAll().size()); // 8 + + city = cityDAO.read(city); // 9 + city.getStores().clear(); // 10 + cityDAO.merge(city); // 18 + + Assertions.assertEquals(0, sellerDao.readAll().size()); // 19 + assertTotalInvocations(19); + } + + @Test + void should_merge_city_with_removed_store_and_new_store() { + Store store3 = new Store(3, "NAME3", 1); + store3.setSellers(List.of(new Seller(4, "SELLER1", 3), new Seller(5, "SELLER2", 3))); + City city = createCityWithStores(); + + cityDAO.insert(city); // 7 City, Store (2), Seller (4) + + Assertions.assertEquals(2, storeDAO.readAll().size()); // 8 + + city = cityDAO.read(city); // 9 + city.getStores().set(1, store3); // 10 + + cityDAO.merge(city); // 15, 2 delete (store2 + seller), 3 insert (store3 + seller) + + Assertions.assertFalse(storeDAO.readOpt(new Store(2)).isPresent()); // 16 + + Optional s = storeDAO.readOpt(store3); // 17 + + Assertions.assertTrue(s.isPresent()); + Assertions.assertEquals(2, s.get().getSellers().size()); // 18 + + assertTotalInvocations(18); + } + + @Test + void should_create_a_full_transaction_with_relationships() { + Transaction transaction = getTransaction(); + + Transaction t1 = new Transaction(); + t1.setId(BigInteger.ONE); + Optional opt = transactionDao.readOpt(t1); + + Assertions.assertAll( + () -> Assertions.assertTrue(opt.isPresent()), + () -> Assertions.assertEquals(BigInteger.ONE, transaction.getReservationId()), + () -> Assertions.assertEquals(BigInteger.ONE, transaction.getPaymentId()), + () -> Assertions.assertEquals(BigInteger.ONE, transaction.getPayment().getId()), + () -> Assertions.assertEquals(BigInteger.ONE, transaction.getReservation().getId()) + ); + + assertTotalInvocations(4); + } + + @Test + void should_delete_transaction_with_payment_and_reservation() { + Transaction transaction = getTransaction(); + transactionDao.delete(transaction); + + assertTotalInvocations(6); + } + + private Transaction getTransaction() { + Reservation reservation = new Reservation(); + reservation.setName("NAME"); + + Payment payment = new Payment(); + payment.setType("TYPE"); + + Transaction transaction = new Transaction(); + transaction.setReservation(reservation); + transaction.setPayment(payment); + + transaction = transactionDao.merge(transaction); + return transaction; + } + + @NotNull + private City createCityWithStores() { + Store store = new Store(1, "NAME", 1); + store.setSellers(List.of(new Seller(1, "SELLER1", 1), new Seller(2, "SELLER2", 1))); + Store store2 = new Store(2, "NAME2", 1); + store2.setSellers(List.of(new Seller(3, "SELLER1", 2), new Seller(4, "SELLER2", 2))); + + City city = new City(); + city.setCityId(1); + city.setName("NAME"); + city.setStores(List.of(store, store2)); + return city; + } + // Utils @NotNull diff --git a/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/QueryBuilderIT.java b/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/QueryBuilderIT.java index a2a8ec40..d047baa2 100644 --- a/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/QueryBuilderIT.java +++ b/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/QueryBuilderIT.java @@ -414,15 +414,21 @@ void should_merge_entity() { @Test void should_read_using_simple_join() { UserDAO userDAO = QueriesService.getInstance().getQuery(UserDAO.class); + RoleDAO roleDAO = QueriesService.getInstance().getQuery(RoleDAO.class); UserRoleDAO userRoleDAO = QueriesService.getInstance().getQuery(UserRoleDAO.class); User user = createUser(1); - UserRole role = new UserRole(); + Role role = new Role(); role.setRoleId(3); - role.setUserId(1); + role.setRoleName("ROLE_NAME3"); + + UserRole userRole = new UserRole(); + userRole.setRoleId(3); + userRole.setUserId(1); userDAO.insert(user); - userRoleDAO.insert(role); + roleDAO.insert(role); + userRoleDAO.insert(userRole); Optional optUser = QueryBuilder.select(User.class) .join(UserRole.class, "B").on(UserRoleColumns.USER_ID).eq(1) diff --git a/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/entity/Payment.java b/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/entity/Payment.java new file mode 100644 index 00000000..18cf0f3f --- /dev/null +++ b/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/entity/Payment.java @@ -0,0 +1,20 @@ +package io.github.ulisse1996.jaorm.integration.test.entity; + +import io.github.ulisse1996.jaorm.annotation.Column; +import io.github.ulisse1996.jaorm.annotation.Id; +import io.github.ulisse1996.jaorm.annotation.Table; +import lombok.Data; + +import java.math.BigInteger; + +@Table(name = "PAYMENTS") +@Data +public class Payment { + + @Id(autoGenerated = true) + @Column(name = "PAYMENT_ID") + private BigInteger id; + + @Column(name = "TYPE") + private String type; +} diff --git a/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/entity/Reservation.java b/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/entity/Reservation.java new file mode 100644 index 00000000..1a368598 --- /dev/null +++ b/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/entity/Reservation.java @@ -0,0 +1,20 @@ +package io.github.ulisse1996.jaorm.integration.test.entity; + +import io.github.ulisse1996.jaorm.annotation.Column; +import io.github.ulisse1996.jaorm.annotation.Id; +import io.github.ulisse1996.jaorm.annotation.Table; +import lombok.Data; + +import java.math.BigInteger; + +@Table(name = "RESERVATIONS") +@Data +public class Reservation { + + @Id(autoGenerated = true) + @Column(name = "RESERVATION_ID") + private BigInteger id; + + @Column(name = "NAME") + private String name; +} diff --git a/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/entity/Seller.java b/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/entity/Seller.java index 8f0fece6..e688cc8d 100644 --- a/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/entity/Seller.java +++ b/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/entity/Seller.java @@ -7,6 +7,14 @@ @Table(name = "SELLER") public class Seller { + public Seller() {} + + public Seller(int id, String name, int storeId) { + this.id = id; + this.name = name; + this.storeId = storeId; + } + @Id @Column(name = "SELLER_ID") private int id; diff --git a/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/entity/Store.java b/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/entity/Store.java index b55ec906..d064768d 100644 --- a/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/entity/Store.java +++ b/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/entity/Store.java @@ -8,6 +8,18 @@ @Table(name = "STORE") public class Store { + public Store() {} + + public Store(int storeId) { + this(storeId, null, 0); + } + + public Store(int storeId, String name, int cityId) { + this.storeId = storeId; + this.name = name; + this.cityId = cityId; + } + @Id @Column(name = "STORE_ID") private int storeId; diff --git a/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/entity/Transaction.java b/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/entity/Transaction.java new file mode 100644 index 00000000..a6be20c9 --- /dev/null +++ b/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/entity/Transaction.java @@ -0,0 +1,29 @@ +package io.github.ulisse1996.jaorm.integration.test.entity; + +import io.github.ulisse1996.jaorm.annotation.*; +import lombok.Data; + +import java.math.BigInteger; + +@Table(name = "TRANSACTIONS") +@Data +public class Transaction { + + @Id(autoGenerated = true) + @Column(name = "TRANSACTION_ID") + private BigInteger id; + + @Column(name = "RESERVATION_ID") + private BigInteger reservationId; + + @Column(name = "PAYMENT_ID") + private BigInteger paymentId; + + @Cascade(CascadeType.ALL) + @Relationship(columns = @Relationship.RelationshipColumn(sourceColumn = "RESERVATION_ID", targetColumn = "RESERVATION_ID")) + private Reservation reservation; + + @Cascade(CascadeType.ALL) + @Relationship(columns = @Relationship.RelationshipColumn(sourceColumn = "PAYMENT_ID", targetColumn = "PAYMENT_ID")) + private Payment payment; +} diff --git a/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/entity/UserRole.java b/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/entity/UserRole.java index ca2451f9..18a7bfb9 100644 --- a/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/entity/UserRole.java +++ b/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/entity/UserRole.java @@ -14,12 +14,31 @@ public class UserRole { @Column(name = "ROLE_ID") private int roleId; - @Cascade(CascadeType.ALL) + public UserRole() {} + + public UserRole(int userId, int roleId) { + this.userId = userId; + this.roleId = roleId; + } + + @Relationship( + columns = @Relationship.RelationshipColumn(sourceColumn = "USER_ID", targetColumn = "USER_ID") + ) + private User user; + @Relationship( columns = @Relationship.RelationshipColumn(sourceColumn = "ROLE_ID", targetColumn = "ROLE_ID") ) private Role role; + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + public int getUserId() { return userId; } diff --git a/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/query/CityDAO.java b/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/query/CityDAO.java index 83a50056..8ac600ea 100644 --- a/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/query/CityDAO.java +++ b/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/query/CityDAO.java @@ -1,14 +1,14 @@ package io.github.ulisse1996.jaorm.integration.test.query; -import io.github.ulisse1996.jaorm.BaseDao; import io.github.ulisse1996.jaorm.annotation.Dao; import io.github.ulisse1996.jaorm.annotation.Query; import io.github.ulisse1996.jaorm.integration.test.entity.City; +import io.github.ulisse1996.jaorm.specialization.SingleKeyDao; import java.util.stream.Stream; @Dao -public interface CityDAO extends BaseDao { +public interface CityDAO extends SingleKeyDao { @Query(sql = "SELECT * FROM CITY WHERE CITY_ID > ?") Stream getCityHigherThanId(Integer id); diff --git a/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/query/PaymentDao.java b/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/query/PaymentDao.java new file mode 100644 index 00000000..209abb89 --- /dev/null +++ b/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/query/PaymentDao.java @@ -0,0 +1,9 @@ +package io.github.ulisse1996.jaorm.integration.test.query; + +import io.github.ulisse1996.jaorm.BaseDao; +import io.github.ulisse1996.jaorm.annotation.Dao; +import io.github.ulisse1996.jaorm.integration.test.entity.Payment; + +@Dao +public interface PaymentDao extends BaseDao { +} diff --git a/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/query/ReservationDao.java b/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/query/ReservationDao.java new file mode 100644 index 00000000..c6dcdcc0 --- /dev/null +++ b/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/query/ReservationDao.java @@ -0,0 +1,9 @@ +package io.github.ulisse1996.jaorm.integration.test.query; + +import io.github.ulisse1996.jaorm.BaseDao; +import io.github.ulisse1996.jaorm.annotation.Dao; +import io.github.ulisse1996.jaorm.integration.test.entity.Reservation; + +@Dao +public interface ReservationDao extends BaseDao { +} diff --git a/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/query/TransactionDao.java b/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/query/TransactionDao.java new file mode 100644 index 00000000..0d7fee28 --- /dev/null +++ b/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/query/TransactionDao.java @@ -0,0 +1,9 @@ +package io.github.ulisse1996.jaorm.integration.test.query; + +import io.github.ulisse1996.jaorm.BaseDao; +import io.github.ulisse1996.jaorm.annotation.Dao; +import io.github.ulisse1996.jaorm.integration.test.entity.Transaction; + +@Dao +public interface TransactionDao extends BaseDao { +} diff --git a/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/query/UserRoleDAO.java b/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/query/UserRoleDAO.java index 69f8ee5e..8b5b9f79 100644 --- a/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/query/UserRoleDAO.java +++ b/jaorm-test/jaorm-test-core/src/main/java/io/github/ulisse1996/jaorm/integration/test/query/UserRoleDAO.java @@ -3,6 +3,9 @@ import io.github.ulisse1996.jaorm.integration.test.entity.UserRole; import io.github.ulisse1996.jaorm.BaseDao; import io.github.ulisse1996.jaorm.annotation.Dao; +import io.github.ulisse1996.jaorm.specialization.DoubleKeyDao; +import io.github.ulisse1996.jaorm.specialization.SingleKeyDao; +import org.springframework.data.relational.core.sql.In; @Dao -public interface UserRoleDAO extends BaseDao {} +public interface UserRoleDAO extends DoubleKeyDao {} diff --git a/jaorm-test/jaorm-test-core/src/main/resources/logback.xml b/jaorm-test/jaorm-test-core/src/main/resources/logback.xml new file mode 100644 index 00000000..b3110d4c --- /dev/null +++ b/jaorm-test/jaorm-test-core/src/main/resources/logback.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jaorm-test/jaorm-test-db2/pom.xml b/jaorm-test/jaorm-test-db2/pom.xml index e194197b..ab753c52 100644 --- a/jaorm-test/jaorm-test-db2/pom.xml +++ b/jaorm-test/jaorm-test-db2/pom.xml @@ -6,7 +6,7 @@ io.github.ulisse1996 jaorm-test - 2.0.0-RC2 + 2.0.0-RC3 jaorm-test-db2 diff --git a/jaorm-test/jaorm-test-micronaut/pom.xml b/jaorm-test/jaorm-test-micronaut/pom.xml index 65d73426..61ebddda 100644 --- a/jaorm-test/jaorm-test-micronaut/pom.xml +++ b/jaorm-test/jaorm-test-micronaut/pom.xml @@ -5,7 +5,7 @@ jaorm-test io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-test/jaorm-test-mysql/pom.xml b/jaorm-test/jaorm-test-mysql/pom.xml index bcbdad02..d4cb7bcc 100644 --- a/jaorm-test/jaorm-test-mysql/pom.xml +++ b/jaorm-test/jaorm-test-mysql/pom.xml @@ -5,7 +5,7 @@ jaorm-test io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-test/jaorm-test-oracle/pom.xml b/jaorm-test/jaorm-test-oracle/pom.xml index 316dd35b..bbeb3ced 100644 --- a/jaorm-test/jaorm-test-oracle/pom.xml +++ b/jaorm-test/jaorm-test-oracle/pom.xml @@ -5,7 +5,7 @@ jaorm-test io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-test/jaorm-test-postgre/pom.xml b/jaorm-test/jaorm-test-postgre/pom.xml index 2aefb8ba..fb0a9300 100644 --- a/jaorm-test/jaorm-test-postgre/pom.xml +++ b/jaorm-test/jaorm-test-postgre/pom.xml @@ -5,7 +5,7 @@ jaorm-test io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-test/jaorm-test-postgre/src/test/resources/init.sql b/jaorm-test/jaorm-test-postgre/src/test/resources/init.sql index c5b14920..0badc50d 100644 --- a/jaorm-test/jaorm-test-postgre/src/test/resources/init.sql +++ b/jaorm-test/jaorm-test-postgre/src/test/resources/init.sql @@ -10,12 +10,15 @@ CREATE TABLE ROLE (ROLE_ID NUMERIC, ROLE_NAME VARCHAR(30)); ALTER TABLE ROLE ADD CONSTRAINT ROLE_PK PRIMARY KEY (ROLE_ID); CREATE TABLE USER_ROLE (ROLE_ID NUMERIC, USER_ID NUMERIC); ALTER TABLE USER_ROLE ADD CONSTRAINT USER_ROLE_PK PRIMARY KEY (ROLE_ID, USER_ID); +ALTER TABLE USER_ROLE ADD CONSTRAINT USER_FK FOREIGN KEY (USER_ID) REFERENCES USER_ENTITY(USER_ID); +ALTER TABLE USER_ROLE ADD CONSTRAINT ROLE_FK FOREIGN KEY (ROLE_ID) REFERENCES ROLE(ROLE_ID); CREATE TABLE USER_SPECIFIC (USER_ID NUMERIC, SPECIFIC_ID NUMERIC); ALTER TABLE USER_SPECIFIC ADD CONSTRAINT USER_SPECIFIC_PK PRIMARY KEY (SPECIFIC_ID, USER_ID); CREATE TABLE CITY (CITY_ID NUMERIC, CITY_NAME VARCHAR(30)); ALTER TABLE CITY ADD CONSTRAINT CITY_PK PRIMARY KEY (CITY_ID); CREATE TABLE STORE(STORE_ID NUMERIC, STORE_NAME VARCHAR(30), CITY_ID NUMERIC); ALTER TABLE STORE ADD CONSTRAINT STORE_PK PRIMARY KEY (STORE_ID); +ALTER TABLE STORE ADD CONSTRAINT STORE_CITY_FK FOREIGN KEY (CITY_ID) REFERENCES CITY(CITY_ID); CREATE TABLE WELD (COL1 VARCHAR(30), COL2 VARCHAR(30)); ALTER TABLE WELD ADD CONSTRAINT WELD_PK PRIMARY KEY (COL1); CREATE TABLE SPRING (COL1 VARCHAR(30), COL2 VARCHAR(30)); @@ -36,8 +39,12 @@ INSERT INTO SEQ_TABLE (ID_SEQ, SEQ_VAL) VALUES ('ENTITY',1); CREATE TABLE LONG_TABLE (ID_COL INTEGER, NAME VARCHAR(30), SUB_NAME VARCHAR(30), CREATE_DATE DATE, VALID NUMERIC(1,0)); INSERT INTO LONG_TABLE (ID_COL, NAME, SUB_NAME, CREATE_DATE, VALID) VALUES (1, 'NAME', 'SUB_NAME', CURRENT_DATE, 1); CREATE TABLE SELLER(SELLER_ID NUMERIC, NAME VARCHAR(30), STORE_ID NUMERIC); +ALTER TABLE SELLER ADD CONSTRAINT SELLER_STORE_FK FOREIGN KEY (STORE_ID) REFERENCES STORE(STORE_ID); CREATE TABLE ENTITY_WITH_DEFAULTS(E_ID NUMERIC, E_STR VARCHAR(30), E_NUM NUMERIC(6, 3), E_DATE TIMESTAMP, E_DATE_FORMAT TIMESTAMP); CREATE TABLE SCHOOL(SCHOOL_ID NUMERIC PRIMARY KEY , NAME VARCHAR(30)); CREATE TABLE STUDENT(STUDENT_ID NUMERIC PRIMARY KEY , SCHOOL_ID NUMERIC, NAME VARCHAR(30), LAST_NAME VARCHAR(30), FOREIGN KEY (SCHOOL_ID) REFERENCES SCHOOL(SCHOOL_ID)); CREATE TABLE ACTIVITY(ACTIVITY_ID NUMERIC PRIMARY KEY, NAME VARCHAR(30), ACTIVITY_DATE DATE); -CREATE TABLE POSTGIS_ENTITY(ID NUMERIC PRIMARY KEY , GEOG GEOGRAPHY); \ No newline at end of file +CREATE TABLE POSTGIS_ENTITY(ID NUMERIC PRIMARY KEY , GEOG GEOGRAPHY); +CREATE TABLE PAYMENTS(PAYMENT_ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, TYPE TEXT); +CREATE TABLE RESERVATIONS(RESERVATION_ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, NAME TEXT); +CREATE TABLE TRANSACTIONS(TRANSACTION_ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, RESERVATION_ID BIGINT, PAYMENT_ID BIGINT, FOREIGN KEY (RESERVATION_ID) REFERENCES RESERVATIONS(RESERVATION_ID), FOREIGN KEY (PAYMENT_ID) REFERENCES PAYMENTS(PAYMENT_ID)); \ No newline at end of file diff --git a/jaorm-test/jaorm-test-spring/pom.xml b/jaorm-test/jaorm-test-spring/pom.xml index abd432b6..7364be73 100644 --- a/jaorm-test/jaorm-test-spring/pom.xml +++ b/jaorm-test/jaorm-test-spring/pom.xml @@ -5,7 +5,7 @@ jaorm-test io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-test/jaorm-test-sql-server/pom.xml b/jaorm-test/jaorm-test-sql-server/pom.xml index bd1c2d3f..f008e39e 100644 --- a/jaorm-test/jaorm-test-sql-server/pom.xml +++ b/jaorm-test/jaorm-test-sql-server/pom.xml @@ -5,7 +5,7 @@ jaorm-test io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-test/pom.xml b/jaorm-test/pom.xml index e3af670c..f00bb30a 100644 --- a/jaorm-test/pom.xml +++ b/jaorm-test/pom.xml @@ -5,7 +5,7 @@ jaorm-pom io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-tools/jaorm-maven-plugin/pom.xml b/jaorm-tools/jaorm-maven-plugin/pom.xml index 867286c2..cec9a971 100644 --- a/jaorm-tools/jaorm-maven-plugin/pom.xml +++ b/jaorm-tools/jaorm-maven-plugin/pom.xml @@ -5,7 +5,7 @@ jaorm-tools io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-tools/jaorm-tools-core/pom.xml b/jaorm-tools/jaorm-tools-core/pom.xml index ca4ef12e..503c4a00 100644 --- a/jaorm-tools/jaorm-tools-core/pom.xml +++ b/jaorm-tools/jaorm-tools-core/pom.xml @@ -5,7 +5,7 @@ jaorm-tools io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-tools/pom.xml b/jaorm-tools/pom.xml index 9314458f..3c59557b 100644 --- a/jaorm-tools/pom.xml +++ b/jaorm-tools/pom.xml @@ -5,7 +5,7 @@ jaorm-pom io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 pom diff --git a/jaorm-transaction/pom.xml b/jaorm-transaction/pom.xml index edf3296b..55e5a46b 100644 --- a/jaorm-transaction/pom.xml +++ b/jaorm-transaction/pom.xml @@ -5,7 +5,7 @@ jaorm-pom io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm-transaction/src/main/java/io/github/ulisse1996/jaorm/transaction/TransactionManagerImpl.java b/jaorm-transaction/src/main/java/io/github/ulisse1996/jaorm/transaction/TransactionManagerImpl.java index c81f71d6..61766f61 100644 --- a/jaorm-transaction/src/main/java/io/github/ulisse1996/jaorm/transaction/TransactionManagerImpl.java +++ b/jaorm-transaction/src/main/java/io/github/ulisse1996/jaorm/transaction/TransactionManagerImpl.java @@ -10,12 +10,12 @@ public class TransactionManagerImpl implements TransactionManager { new InheritableThreadLocal<>(); @Override - public synchronized Transaction getCurrentTransaction() { + public Transaction getCurrentTransaction() { return TRANSACTION_THREAD_LOCAL.get(); } @Override - public synchronized void createTransaction() { + public void createTransaction() { TRANSACTION_THREAD_LOCAL.set(new TransactionImpl()); } diff --git a/jaorm-validation/pom.xml b/jaorm-validation/pom.xml index 6b6e7ebb..13c796ee 100644 --- a/jaorm-validation/pom.xml +++ b/jaorm-validation/pom.xml @@ -5,7 +5,7 @@ jaorm-pom io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm/pom.xml b/jaorm/pom.xml index 5ffa407a..826e3140 100644 --- a/jaorm/pom.xml +++ b/jaorm/pom.xml @@ -5,7 +5,7 @@ jaorm-pom io.github.ulisse1996 - 2.0.0-RC2 + 2.0.0-RC3 4.0.0 diff --git a/jaorm/src/test/java/io/github/ulisse1996/jaorm/DelegatesMock.java b/jaorm/src/test/java/io/github/ulisse1996/jaorm/DelegatesMock.java index a5b3ddbd..5e64ecbf 100644 --- a/jaorm/src/test/java/io/github/ulisse1996/jaorm/DelegatesMock.java +++ b/jaorm/src/test/java/io/github/ulisse1996/jaorm/DelegatesMock.java @@ -1,10 +1,13 @@ package io.github.ulisse1996.jaorm; +import io.github.ulisse1996.jaorm.entity.DirtinessTracker; +import io.github.ulisse1996.jaorm.entity.EntityDelegate; +import io.github.ulisse1996.jaorm.entity.EntityMapper; import io.github.ulisse1996.jaorm.entity.SqlColumn; +import io.github.ulisse1996.jaorm.entity.relationship.LazyEntityInfo; +import io.github.ulisse1996.jaorm.entity.relationship.RelationshipManager; import io.github.ulisse1996.jaorm.schema.TableInfo; import io.github.ulisse1996.jaorm.spi.DelegatesService; -import io.github.ulisse1996.jaorm.entity.EntityDelegate; -import io.github.ulisse1996.jaorm.entity.EntityMapper; import java.math.BigDecimal; import java.sql.ResultSet; @@ -139,6 +142,11 @@ public boolean isModified() { return false; } + @Override + public void setModified(boolean modified) { + + } + @Override public boolean isDefaultGeneration() { return false; @@ -153,5 +161,30 @@ public MyEntity initDefault(MyEntity entity) { public TableInfo toTableInfo() { return null; } + + @Override + public DirtinessTracker getTracker() { + return null; + } + + @Override + public boolean isLazyEntity() { + return false; + } + + @Override + public LazyEntityInfo getLazyInfo() { + return null; + } + + @Override + public void setLazyInfo(LazyEntityInfo info) { + + } + + @Override + public RelationshipManager getRelationshipManager() { + return null; + } } } diff --git a/pom.xml b/pom.xml index aa5a46af..5e32f8dc 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ io.github.ulisse1996 jaorm-pom pom - 2.0.0-RC2 + 2.0.0-RC3 jaorm-processor jaorm-core @@ -45,14 +45,14 @@ 2.7.5 3.7.3 1.13.0 - 1.18.24 + 1.18.30 0.19 2.0.1.Final 2.15.0.Final 5.9.0 - 4.8.0 + 5.1.1 1.17.5 1.2.11 2.0.2.Final