├── settings.gradle
├── photon-core
├── infinitest.filters
├── src
│ ├── main
│ │ └── java
│ │ │ └── com
│ │ │ └── github
│ │ │ └── molcikas
│ │ │ └── photon
│ │ │ ├── options
│ │ │ ├── DefaultTableName.java
│ │ │ └── PhotonOptions.java
│ │ │ ├── converters
│ │ │ ├── Converter.java
│ │ │ ├── EnumConverterFactory.java
│ │ │ ├── ConvertersProvider.java
│ │ │ ├── ConverterException.java
│ │ │ ├── number
│ │ │ │ ├── ByteConverter.java
│ │ │ │ ├── LongConverter.java
│ │ │ │ ├── ShortConverter.java
│ │ │ │ ├── FloatConverter.java
│ │ │ │ ├── DoubleConverter.java
│ │ │ │ ├── IntegerConverter.java
│ │ │ │ ├── BigDecimalConverter.java
│ │ │ │ └── NumberConverter.java
│ │ │ ├── InputStreamConverter.java
│ │ │ ├── UUIDConverter.java
│ │ │ ├── date
│ │ │ │ ├── InstantConverter.java
│ │ │ │ ├── LocalDateConverter.java
│ │ │ │ ├── ZonedDateTimeConverter.java
│ │ │ │ ├── LocalDateTimeConverter.java
│ │ │ │ ├── DateConverter.java
│ │ │ │ └── TimestampConverter.java
│ │ │ ├── IOUtils.java
│ │ │ ├── DefaultEnumConverterFactory.java
│ │ │ ├── BooleanConverter.java
│ │ │ ├── StringConverter.java
│ │ │ └── ByteArrayConverter.java
│ │ │ ├── blueprints
│ │ │ ├── entity
│ │ │ │ ├── FieldType.java
│ │ │ │ ├── ChildCollectionConstructor.java
│ │ │ │ ├── EntityBlueprintAndKey.java
│ │ │ │ ├── FieldBlueprintAndKey.java
│ │ │ │ ├── EntityClassDiscriminator.java
│ │ │ │ ├── CompoundEntityFieldValueMapping.java
│ │ │ │ ├── EntityFieldValueMapping.java
│ │ │ │ ├── MappedClassBlueprint.java
│ │ │ │ └── FlattenedCollectionBlueprint.java
│ │ │ ├── table
│ │ │ │ ├── JoinType.java
│ │ │ │ ├── TableBlueprintAndKey.java
│ │ │ │ ├── DefaultColumnDataTypeResult.java
│ │ │ │ ├── DatabaseColumnDefinition.java
│ │ │ │ ├── ColumnDataType.java
│ │ │ │ ├── TableValue.java
│ │ │ │ ├── JoinedTableBlueprintBuilder.java
│ │ │ │ └── ColumnBlueprint.java
│ │ │ └── AggregateBlueprint.java
│ │ │ ├── exceptions
│ │ │ ├── PhotonOptimisticConcurrencyException.java
│ │ │ └── PhotonException.java
│ │ │ ├── sqlbuilders
│ │ │ ├── SqlBuilderApplyOptionsService.java
│ │ │ └── SqlJoinClauseBuilderService.java
│ │ │ ├── PhotonUtils.java
│ │ │ ├── query
│ │ │ ├── GetParameterValuesResult.java
│ │ │ ├── PhotonQueryResultRow.java
│ │ │ ├── ParameterValue.java
│ │ │ ├── PhotonSqlParameter.java
│ │ │ └── PhotonAggregateFilterQuery.java
│ │ │ └── datasource
│ │ │ ├── ExistingConnectionDataSource.java
│ │ │ └── GenericDataSource.java
│ └── test
│ │ ├── java
│ │ └── com
│ │ │ └── github
│ │ │ └── molcikas
│ │ │ └── photon
│ │ │ └── tests
│ │ │ ├── unit
│ │ │ ├── entities
│ │ │ │ ├── someaggregate
│ │ │ │ │ ├── SomeClass.java
│ │ │ │ │ └── SomeAggregate.java
│ │ │ │ ├── fieldtest
│ │ │ │ │ ├── TestEnum.java
│ │ │ │ │ └── FieldTest.java
│ │ │ │ ├── shape
│ │ │ │ │ ├── Drawing.java
│ │ │ │ │ ├── CornerCoordinates.java
│ │ │ │ │ ├── Circle.java
│ │ │ │ │ ├── Rectangle.java
│ │ │ │ │ ├── ShapeColorHistory.java
│ │ │ │ │ └── Shape.java
│ │ │ │ ├── twoaggregates
│ │ │ │ │ ├── AggregateTwo.java
│ │ │ │ │ └── AggregateOne.java
│ │ │ │ ├── myonetomanytable
│ │ │ │ │ ├── MyOneToManyMapTable.java
│ │ │ │ │ ├── MyThirdTable.java
│ │ │ │ │ ├── MyOneToManyTable.java
│ │ │ │ │ └── MyManyTable.java
│ │ │ │ ├── mytable
│ │ │ │ │ ├── MyOtherTable.java
│ │ │ │ │ └── MyTable.java
│ │ │ │ ├── product
│ │ │ │ │ └── Product.java
│ │ │ │ └── recipe
│ │ │ │ │ ├── RecipeInstruction.java
│ │ │ │ │ ├── RecipeIngredient.java
│ │ │ │ │ └── Recipe.java
│ │ │ ├── h2
│ │ │ │ ├── H2TestUtil.java
│ │ │ │ ├── someaggregate
│ │ │ │ │ ├── SomeAggregateDbSetup.java
│ │ │ │ │ └── SomeAggregateTests.java
│ │ │ │ ├── shape
│ │ │ │ │ ├── ShapeMultiTableDbSetup.java
│ │ │ │ │ ├── ShapeDbSetup.java
│ │ │ │ │ ├── ShapeChildEntityTests.java
│ │ │ │ │ └── ShapeQueryTests.java
│ │ │ │ ├── product
│ │ │ │ │ └── ProductDbSetup.java
│ │ │ │ ├── fieldtest
│ │ │ │ │ └── FieldTestDbSetup.java
│ │ │ │ ├── recipe
│ │ │ │ │ └── RecipeDeleteTests.java
│ │ │ │ ├── mytable
│ │ │ │ │ └── MyTableDbSetup.java
│ │ │ │ ├── twoaggregates
│ │ │ │ │ └── TwoAggregatesDbSetup.java
│ │ │ │ └── myonetomanytable
│ │ │ │ │ └── MyOneToManyTableDbSetup.java
│ │ │ └── blueprints
│ │ │ │ ├── TableValueTest.java
│ │ │ │ └── MyTableBlueprintTests.java
│ │ │ └── integration
│ │ │ ├── PhotonTestTable.java
│ │ │ ├── MySqlIntegrationTest.java
│ │ │ └── OracleIntegrationTest.java
│ │ └── resources
│ │ ├── setup-some-aggregate.sql
│ │ ├── logback-test.xml
│ │ └── setup-shape-multi-table.sql
└── build.gradle
├── findbugs-exclude-filter.xml
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .idea
├── copyright
│ └── profiles_settings.xml
├── vcs.xml
├── compiler.xml
├── misc.xml
├── libraries
│ ├── Gradle__junit_junit_4_12.xml
│ ├── Gradle__com_h2database_h2_1_4_193.xml
│ ├── Gradle__joda_time_joda_time_2_9_7.xml
│ ├── Gradle__org_hamcrest_hamcrest_core_1_3.xml
│ ├── Gradle__org_mockito_mockito_all_1_10_19.xml
│ ├── Gradle__org_apache_commons_commons_lang3_3_4.xml
│ ├── Gradle__mysql_mysql_connector_java_5_1_40.xml
│ └── Gradle__org_codehaus_groovy_groovy_all_2_3_11.xml
├── modules.xml
├── gradle.xml
├── runConfigurations
│ ├── All_Core_Unit_Tests.xml
│ └── Integration_Tests.xml
└── codeStyles
│ └── Project.xml
├── findbugs-include-filter.xml
├── .gitignore
├── .travis.yml
├── photon.iml
├── photon-perf-test
├── src
│ └── test
│ │ ├── resources
│ │ ├── logback-test.xml
│ │ └── META-INF
│ │ │ └── persistence.xml
│ │ └── java
│ │ └── com
│ │ └── github
│ │ └── molcikas
│ │ └── photon
│ │ └── perf
│ │ ├── photon
│ │ ├── RecipeInstruction.java
│ │ ├── RecipeIngredient.java
│ │ └── Recipe.java
│ │ ├── hibernate
│ │ ├── RecipeInstructionEntity.java
│ │ ├── RecipeIngredientEntity.java
│ │ └── RecipeEntity.java
│ │ ├── ReflectionTest.java
│ │ └── RecipeDbSetup.java
└── build.gradle
├── docker-compose.yml
├── LICENSE
├── classes
└── test
│ └── photon-perf-test
│ └── META-INF
│ └── persistence.xml
├── gradlew.bat
└── docs
├── AdvancedLoadingAndSaving.md
├── ValueObjects.md
└── ViewModels.md
/settings.gradle:
--------------------------------------------------------------------------------
1 | include 'photon-core'
2 | include 'photon-perf-test'
--------------------------------------------------------------------------------
/photon-core/infinitest.filters:
--------------------------------------------------------------------------------
1 | com\.github\.molcikas\.photon\.tests\.integration\..*
--------------------------------------------------------------------------------
/findbugs-exclude-filter.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | ossrhUsername=
2 | ossrhPassword=
3 | developerId=
4 | developerName=
5 | developerEmailAddress=
6 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/molcikas/photon/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/findbugs-include-filter.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/options/DefaultTableName.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.options;
2 |
3 | public enum DefaultTableName
4 | {
5 | ClassName,
6 | ClassNameLowerCase
7 | }
8 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/converters/Converter.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.converters;
2 |
3 | /**
4 | * Represents a converter.
5 | */
6 | public interface Converter {
7 |
8 | T convert(Object val) throws ConverterException;
9 | }
10 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/blueprints/entity/FieldType.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.blueprints.entity;
2 |
3 | public enum FieldType
4 | {
5 | Primitive,
6 | Entity,
7 | EntityList,
8 | CustomValueMapper,
9 | CompoundCustomValueMapper,
10 | FlattenedCollection,
11 | }
12 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/converters/EnumConverterFactory.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.converters;
2 |
3 | /**
4 | * Used by sql2o to convert a value from the database into an {@link Enum}.
5 | */
6 | public interface EnumConverterFactory {
7 | Converter newConverter(Class enumClass);
8 | }
9 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/converters/ConvertersProvider.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.converters;
2 |
3 | import java.util.Map;
4 |
5 | /**
6 | * User: dimzon
7 | * Date: 4/24/14
8 | * Time: 12:53 AM
9 | */
10 | public interface ConvertersProvider {
11 | void fill(Map, Converter>> mapToFill);
12 | }
13 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/entities/someaggregate/SomeClass.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.entities.someaggregate;
2 |
3 | public class SomeClass
4 | {
5 | private int id;
6 |
7 | private SomeClass()
8 | {
9 | }
10 |
11 | public SomeClass(int id)
12 | {
13 | this.id = id;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/exceptions/PhotonOptimisticConcurrencyException.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.exceptions;
2 |
3 | public class PhotonOptimisticConcurrencyException extends RuntimeException
4 | {
5 | public PhotonOptimisticConcurrencyException()
6 | {
7 | super("An optimistic concurrency exception occurred.");
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/blueprints/entity/ChildCollectionConstructor.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.blueprints.entity;
2 |
3 | import java.util.Collection;
4 |
5 | public interface ChildCollectionConstructor
6 | {
7 | Collection toCollection(F fieldValue, P parentEntityInstance);
8 |
9 | F toFieldValue(Collection collection, P parentEntityInstance);
10 | }
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 |
3 | # Mobile Tools for Java (J2ME)
4 | .mtj.tmp/
5 |
6 | # Package Files #
7 | *.jar
8 | *.war
9 | *.ear
10 |
11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
12 | hs_err_pid*
13 |
14 | /.idea/workspace.xml
15 | /.idea/tasks.xml
16 | /.gradle
17 | !gradle-wrapper.jar
18 | /classes
19 | test/libs/*
20 |
21 | *.iml
22 | !/photon.iml
23 |
24 | /photon-core/build
25 | /photon-perf-test/build
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/entities/fieldtest/TestEnum.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.entities.fieldtest;
2 |
3 | public enum TestEnum
4 | {
5 | VALUE_ZERO,
6 | VALUE_ONE,
7 | VALUE_TWO,
8 | VALUE_THREE;
9 |
10 | @Override
11 | public String toString()
12 | {
13 | return "Hopefully nothing relies on toString() returning the enum value because it's not!";
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/entities/shape/Drawing.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.entities.shape;
2 |
3 | import java.util.List;
4 |
5 | public class Drawing
6 | {
7 | private int id;
8 |
9 | private List shapes;
10 |
11 | public int getId()
12 | {
13 | return id;
14 | }
15 |
16 | public List getShapes()
17 | {
18 | return shapes;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/blueprints/table/JoinType.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.blueprints.table;
2 |
3 | public enum JoinType
4 | {
5 | InnerJoin("JOIN"),
6 | LeftOuterJoin("LEFT JOIN");
7 |
8 | private final String joinSql;
9 |
10 | public String getJoinSql()
11 | {
12 | return joinSql;
13 | }
14 |
15 | JoinType(String joinSql)
16 | {
17 | this.joinSql = joinSql;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/converters/ConverterException.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.converters;
2 |
3 | /**
4 | * Represents an exception thrown from a converter.
5 | */
6 | public class ConverterException extends RuntimeException
7 | {
8 | public ConverterException(String message) {
9 | super(message);
10 | }
11 |
12 | public ConverterException(String message, Throwable cause) {
13 | super(message, cause);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/exceptions/PhotonException.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.exceptions;
2 |
3 | public class PhotonException extends RuntimeException
4 | {
5 | public PhotonException(String message, Object... args)
6 | {
7 | super(String.format(message, args));
8 | }
9 |
10 | public PhotonException(Throwable cause, String message, Object... args)
11 | {
12 | super(String.format(message, args), cause);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: required
2 | dist: trusty
3 | services:
4 | - docker
5 |
6 | language: java
7 | jdk:
8 | - oraclejdk8
9 |
10 | before_cache:
11 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
12 |
13 | cache:
14 | directories:
15 | - "$HOME/.gradle/caches/"
16 | - "$HOME/.gradle/wrapper/"
17 |
18 | before_install:
19 | - chmod +x gradlew
20 |
21 | before_script:
22 | - docker-compose up -d
23 | - sleep 30
24 |
25 | after_success:
26 | - bash <(curl -s https://codecov.io/bash)
27 |
--------------------------------------------------------------------------------
/photon.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/blueprints/entity/EntityBlueprintAndKey.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.blueprints.entity;
2 |
3 | import com.github.molcikas.photon.blueprints.table.TableValue;
4 | import lombok.AllArgsConstructor;
5 | import lombok.EqualsAndHashCode;
6 | import lombok.Getter;
7 |
8 | @AllArgsConstructor
9 | @Getter
10 | @EqualsAndHashCode
11 | public class EntityBlueprintAndKey
12 | {
13 | private final EntityBlueprint entityBlueprint;
14 | private final TableValue primaryKey;
15 | }
16 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/entities/someaggregate/SomeAggregate.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.entities.someaggregate;
2 |
3 | import java.util.List;
4 |
5 | public class SomeAggregate
6 | {
7 | private int id;
8 |
9 | private List fieldOne;
10 |
11 | private SomeAggregate()
12 | {
13 | }
14 |
15 | public SomeAggregate(int id, List fieldOne)
16 | {
17 | this.id = id;
18 | this.fieldOne = fieldOne;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__junit_junit_4_12.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/sqlbuilders/SqlBuilderApplyOptionsService.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.sqlbuilders;
2 |
3 | import com.github.molcikas.photon.options.PhotonOptions;
4 |
5 | public final class SqlBuilderApplyOptionsService
6 | {
7 | public static String applyPhotonOptionsToSql(String sql, PhotonOptions photonOptions)
8 | {
9 | return sql
10 | .replaceAll("\\[", photonOptions.getDelimitIdentifierStart())
11 | .replaceAll("\\]", photonOptions.getDelimitIdentifierEnd());
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/entities/twoaggregates/AggregateTwo.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.entities.twoaggregates;
2 |
3 | import java.util.UUID;
4 |
5 | public class AggregateTwo
6 | {
7 | private UUID aggregateTwoId;
8 |
9 | private String myValue;
10 |
11 | public UUID getAggregateTwoId()
12 | {
13 | return aggregateTwoId;
14 | }
15 |
16 | public String getMyValue()
17 | {
18 | return myValue;
19 | }
20 |
21 | private AggregateTwo()
22 | {
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__com_h2database_h2_1_4_193.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__joda_time_joda_time_2_9_7.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/blueprints/table/TableBlueprintAndKey.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.blueprints.table;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.EqualsAndHashCode;
5 | import lombok.Getter;
6 |
7 | @AllArgsConstructor
8 | @Getter
9 | @EqualsAndHashCode
10 | public class TableBlueprintAndKey
11 | {
12 | private final TableBlueprint tableBlueprint;
13 | private final TableValue primaryKey;
14 |
15 | @Override
16 | public String toString()
17 | {
18 | return tableBlueprint.getTableName() + ":" + primaryKey;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/photon-core/src/test/resources/setup-some-aggregate.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS `someaggregate`;
2 | DROP TABLE IF EXISTS `someclass`;
3 |
4 | CREATE TABLE `someaggregate` (
5 | `id` int(11) NOT NULL AUTO_INCREMENT,
6 | PRIMARY KEY (`id`)
7 | );
8 |
9 | CREATE TABLE `someclass` (
10 | `id` int(11) NOT NULL AUTO_INCREMENT,
11 | `someAggregateId` int(11) NOT NULL,
12 | PRIMARY KEY (`id`),
13 | CONSTRAINT `someclass_someaggregate` FOREIGN KEY (`someAggregateId`) REFERENCES `someaggregate` (`id`)
14 | );
15 |
16 | insert into `someaggregate` (`id`) values (1);
17 | insert into `someclass` (`id`, `someAggregateId`) values (1, 1);
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__org_hamcrest_hamcrest_core_1_3.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__org_mockito_mockito_all_1_10_19.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/photon-core/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/photon-perf-test/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__org_apache_commons_commons_lang3_3_4.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__mysql_mysql_connector_java_5_1_40.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__org_codehaus_groovy_groovy_all_2_3_11.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/blueprints/entity/FieldBlueprintAndKey.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.blueprints.entity;
2 |
3 | import com.github.molcikas.photon.blueprints.table.TableValue;
4 | import lombok.AllArgsConstructor;
5 | import lombok.EqualsAndHashCode;
6 | import lombok.Getter;
7 |
8 | @AllArgsConstructor
9 | @Getter
10 | @EqualsAndHashCode
11 | public class FieldBlueprintAndKey
12 | {
13 | private final FieldBlueprint fieldBlueprint;
14 | private final TableValue primaryKey;
15 |
16 | @Override
17 | public String toString()
18 | {
19 | return fieldBlueprint.getFieldClass().getSimpleName() + "." + fieldBlueprint.getFieldName() + ":" + primaryKey;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/entities/myonetomanytable/MyOneToManyMapTable.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.entities.myonetomanytable;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Getter;
5 |
6 | import java.util.Map;
7 |
8 | @AllArgsConstructor
9 | public class MyOneToManyMapTable
10 | {
11 | @Getter
12 | private Integer myOneToManyMapTableId;
13 |
14 | @Getter
15 | private String myvalue;
16 |
17 | @Getter
18 | private Map myManyTables;
19 |
20 | private MyOneToManyMapTable()
21 | {
22 | }
23 |
24 | public void setMyvalue(String myvalue)
25 | {
26 | this.myvalue = myvalue;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/entities/mytable/MyOtherTable.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.entities.mytable;
2 |
3 | public class MyOtherTable
4 | {
5 | private int id;
6 |
7 | private String myOtherValueWithDiffName;
8 |
9 | public int getId()
10 | {
11 | return id;
12 | }
13 |
14 | public String getMyOtherValueWithDiffName()
15 | {
16 | return myOtherValueWithDiffName;
17 | }
18 |
19 | private MyOtherTable()
20 | {
21 | }
22 |
23 | public MyOtherTable(int id, String myOtherValueWithDiffName)
24 | {
25 | this.id = id;
26 | this.myOtherValueWithDiffName = myOtherValueWithDiffName;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/blueprints/table/DefaultColumnDataTypeResult.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.blueprints.table;
2 |
3 | public class DefaultColumnDataTypeResult
4 | {
5 | public final boolean foundDataType;
6 | public final ColumnDataType dataType;
7 |
8 | public DefaultColumnDataTypeResult(ColumnDataType dataType)
9 | {
10 | this.foundDataType = true;
11 | this.dataType = dataType;
12 | }
13 |
14 | public DefaultColumnDataTypeResult()
15 | {
16 | this.foundDataType = false;
17 | this.dataType = null;
18 | }
19 |
20 | public static DefaultColumnDataTypeResult notFound()
21 | {
22 | return new DefaultColumnDataTypeResult();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/PhotonUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon;
2 |
3 | import java.util.Set;
4 | import java.util.stream.Collectors;
5 | import java.util.stream.Stream;
6 |
7 | public class PhotonUtils
8 | {
9 | private static final Set> primitiveNumbers = Stream
10 | .of(int.class, long.class, float.class, double.class, byte.class, short.class)
11 | .collect(Collectors.toSet());
12 |
13 | public static boolean isNumericType(Class> cls)
14 | {
15 | if (cls.isPrimitive())
16 | {
17 | return primitiveNumbers.contains(cls);
18 | }
19 | else
20 | {
21 | return Number.class.isAssignableFrom(cls);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/entities/myonetomanytable/MyThirdTable.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.entities.myonetomanytable;
2 |
3 | public class MyThirdTable
4 | {
5 | private Integer id;
6 |
7 | private Integer parent;
8 |
9 | private String val;
10 |
11 | public Integer getId()
12 | {
13 | return id;
14 | }
15 |
16 | public Integer getParent()
17 | {
18 | return parent;
19 | }
20 |
21 | public String getVal()
22 | {
23 | return val;
24 | }
25 |
26 | private MyThirdTable()
27 | {
28 | }
29 |
30 | public MyThirdTable(Integer parent, String val)
31 | {
32 | this.parent = parent;
33 | this.val = val;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/converters/number/ByteConverter.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.converters.number;
2 |
3 | /**
4 | * Used by sql2o to convert a value from the database into a {@link Byte}.
5 | */
6 | public class ByteConverter extends NumberConverter {
7 |
8 | public ByteConverter(boolean primitive) {
9 | super(primitive);
10 | }
11 |
12 | @Override
13 | protected Byte convertNumberValue(Number val) {
14 | return val.byteValue();
15 | }
16 |
17 | @Override
18 | protected Byte convertStringValue(String val) {
19 | return Byte.parseByte(val);
20 | }
21 |
22 | @Override
23 | protected String getTypeDescription() {
24 | return Byte.class.toString();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/converters/number/LongConverter.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.converters.number;
2 |
3 | /**
4 | * Used by sql2o to convert a value from the database into a {@link Long}.
5 | */
6 | public class LongConverter extends NumberConverter {
7 |
8 | public LongConverter(boolean primitive) {
9 | super(primitive);
10 | }
11 |
12 | @Override
13 | protected Long convertNumberValue(Number val) {
14 | return val.longValue();
15 | }
16 |
17 | @Override
18 | protected Long convertStringValue(String val) {
19 | return Long.parseLong(val);
20 | }
21 |
22 | @Override
23 | protected String getTypeDescription() {
24 | return Long.class.toString();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/converters/number/ShortConverter.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.converters.number;
2 |
3 | /**
4 | * Used by sql2o to convert a value from the database into a {@link Short}.
5 | */
6 | public class ShortConverter extends NumberConverter {
7 |
8 | public ShortConverter(boolean primitive) {
9 | super(primitive);
10 | }
11 |
12 | @Override
13 | protected Short convertNumberValue(Number val) {
14 | return val.shortValue();
15 | }
16 |
17 | @Override
18 | protected Short convertStringValue(String val) {
19 | return Short.parseShort(val);
20 | }
21 |
22 | @Override
23 | protected String getTypeDescription() {
24 | return Short.class.toString();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/converters/number/FloatConverter.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.converters.number;
2 |
3 | /**
4 | * Used by sql2o to convert a value from the database into a {@link Float}.
5 | */
6 | public class FloatConverter extends NumberConverter
7 | {
8 |
9 | public FloatConverter(boolean primitive) {
10 | super(primitive);
11 | }
12 |
13 | @Override
14 | protected Float convertNumberValue(Number val) {
15 | return val.floatValue();
16 | }
17 |
18 | @Override
19 | protected Float convertStringValue(String val) {
20 | return Float.parseFloat(val);
21 | }
22 |
23 | @Override
24 | protected String getTypeDescription() {
25 | return Float.class.toString();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/blueprints/entity/EntityClassDiscriminator.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.blueprints.entity;
2 |
3 | import java.util.Map;
4 |
5 | /**
6 | * The interface for custom entity class discriminators.
7 | */
8 | public interface EntityClassDiscriminator
9 | {
10 | /**
11 | * Called after entity column values are retrieved but just before the entity is constructed. This allows
12 | * dynamically determining the entity class based on the column values. Typically, this is used to implement
13 | * single-table inheritance.
14 | *
15 | * @param valueMap - the column values for the entity instance
16 | * @return - the entity class to construct
17 | */
18 | Class getClassForEntity(Map valueMap);
19 | }
20 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/query/GetParameterValuesResult.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.query;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Getter;
5 |
6 | import java.util.Collections;
7 | import java.util.Map;
8 |
9 | @AllArgsConstructor
10 | @Getter
11 | public class GetParameterValuesResult
12 | {
13 | private boolean skipped;
14 | private boolean changed;
15 | private Map values;
16 |
17 | public static GetParameterValuesResult skipped()
18 | {
19 | return new GetParameterValuesResult(true, false, Collections.emptyMap());
20 | }
21 |
22 | public static GetParameterValuesResult unchanged()
23 | {
24 | return new GetParameterValuesResult(false, false, Collections.emptyMap());
25 | }
26 | }
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
20 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/converters/number/DoubleConverter.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.converters.number;
2 |
3 | /**
4 | * Used by sql2o to convert a value from the database into a {@link Double}.
5 | */
6 | public class DoubleConverter extends NumberConverter
7 | {
8 |
9 | public DoubleConverter(boolean primitive) {
10 | super(primitive);
11 | }
12 |
13 | @Override
14 | protected Double convertNumberValue(Number val) {
15 | return val.doubleValue();
16 | }
17 |
18 | @Override
19 | protected Double convertStringValue(String val) {
20 | return Double.parseDouble(val);
21 | }
22 |
23 | @Override
24 | protected String getTypeDescription() {
25 | return Double.class.toString();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/converters/number/IntegerConverter.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.converters.number;
2 |
3 | /**
4 | * Used by sql2o to convert a value from the database into an {@link Integer}.
5 | */
6 | public class IntegerConverter extends NumberConverter
7 | {
8 |
9 | public IntegerConverter(boolean primitive) {
10 | super(primitive);
11 | }
12 |
13 | @Override
14 | protected Integer convertNumberValue(Number val) {
15 | return val.intValue();
16 | }
17 |
18 | @Override
19 | protected Integer convertStringValue(String val) {
20 | return Integer.parseInt(val);
21 | }
22 |
23 | @Override
24 | protected String getTypeDescription() {
25 | return Integer.class.toString();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/converters/InputStreamConverter.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.converters;
2 |
3 | import java.io.ByteArrayInputStream;
4 |
5 | /**
6 | * Created with IntelliJ IDEA.
7 | * User: lars
8 | * Date: 6/13/13
9 | * Time: 11:40 PM
10 | * To change this template use File | Settings | File Templates.
11 | */
12 | public class InputStreamConverter implements Converter
13 | {
14 | public ByteArrayInputStream convert(Object val) throws ConverterException {
15 | if (val == null) return null;
16 |
17 | try {
18 | return new ByteArrayInputStream( new ByteArrayConverter().convert(val) );
19 | } catch( ConverterException e) {
20 | throw new ConverterException("Error converting Blob to InputSteam");
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/entities/product/Product.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.entities.product;
2 |
3 | import org.apache.commons.lang3.math.Fraction;
4 |
5 | public class Product
6 | {
7 | private int theProductId;
8 |
9 | private Fraction quantity;
10 |
11 | public int getTheProductId()
12 | {
13 | return theProductId;
14 | }
15 |
16 | public Fraction getQuantity()
17 | {
18 | return quantity;
19 | }
20 |
21 | public void setQuantity(Fraction quantity)
22 | {
23 | this.quantity = quantity;
24 | }
25 |
26 | private Product()
27 | {
28 | }
29 |
30 | public Product(int theProductId, Fraction quantity)
31 | {
32 | this.theProductId = theProductId;
33 | this.quantity = quantity;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/entities/shape/CornerCoordinates.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.entities.shape;
2 |
3 | public class CornerCoordinates
4 | {
5 | private Integer id;
6 |
7 | private Integer x;
8 |
9 | private Integer y;
10 |
11 | public Integer getId()
12 | {
13 | return id;
14 | }
15 |
16 | public Integer getX()
17 | {
18 | return x;
19 | }
20 |
21 | public Integer getY()
22 | {
23 | return y;
24 | }
25 |
26 | private CornerCoordinates()
27 | {
28 | }
29 |
30 | public CornerCoordinates(Integer id, Integer x, Integer y)
31 | {
32 | this.id = id;
33 | this.x = x;
34 | this.y = y;
35 | }
36 |
37 | public void setX(Integer x)
38 | {
39 | this.x = x;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2.3'
2 |
3 | services:
4 |
5 | mysql57:
6 | image: mysql:5.7.21
7 | restart: always
8 | environment:
9 | MYSQL_ROOT_PASSWORD: bears
10 | TZ: "America/Chicago"
11 | ports:
12 | - 13306:3306
13 |
14 | postgres104:
15 | image: postgres:10.4
16 | environment:
17 | POSTGRES_PASSWORD: bears
18 | ports:
19 | - 15432:5432
20 |
21 | mssql2017:
22 | image: microsoft/mssql-server-linux:2017-latest
23 | environment:
24 | SA_PASSWORD: Gobears123
25 | ACCEPT_EULA: Y
26 | ports:
27 | - 11433:1433
28 |
29 | # This container takes a VERY long time to start (10+ minutes). Leave commented out so it doesn't run during CI build.
30 | # oracle12c:
31 | # image: sath89/oracle-12c
32 | # environment:
33 | # DBCA_TOTAL_MEMORY: 1024
34 | # WEB_CONSOLE: "false"
35 | # ports:
36 | # - 11521:1521
37 | # - 8080:8080
--------------------------------------------------------------------------------
/photon-perf-test/build.gradle:
--------------------------------------------------------------------------------
1 |
2 | description = "Photon performance testing"
3 |
4 | dependencies {
5 | compile project(':photon-core')
6 |
7 | compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.4'
8 |
9 | // Database
10 | testCompile group: 'com.h2database', name: 'h2', version: '1.4.193'
11 | testCompile group: 'mysql', name: 'mysql-connector-java', version: '5.1.40'
12 | testCompile group: 'com.zaxxer', name: 'HikariCP', version: '2.4.7'
13 | testCompile group: 'org.hibernate', name: 'hibernate-core', version: '5.2.3.Final'
14 | testCompile group: 'org.hibernate', name: 'hibernate-entitymanager', version: '5.2.3.Final'
15 | testCompile group: 'org.hibernate', name: 'hibernate-hikaricp', version: '5.2.3.Final'
16 |
17 | // Unit testing
18 | testCompile group: 'junit', name: 'junit', version: '4.12'
19 | testCompile group: 'org.mockito', name: 'mockito-all', version: '1.10.19'
20 | }
21 |
22 | test.enabled = false
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/entities/shape/Circle.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.entities.shape;
2 |
3 | import java.util.List;
4 |
5 | public class Circle extends Shape
6 | {
7 | private int radius;
8 |
9 | public int getRadius()
10 | {
11 | return radius;
12 | }
13 |
14 | private Circle()
15 | {
16 | }
17 |
18 | public Circle(Integer id, String color, Integer drawingId, int radius)
19 | {
20 | super(id, "circle", color, drawingId);
21 | this.radius = radius;
22 | }
23 |
24 | public Circle(
25 | Integer id,
26 | String color,
27 | Integer drawingId,
28 | int radius,
29 | List colorHistory)
30 | {
31 | super(id, "circle", color, drawingId, colorHistory);
32 | this.radius = radius;
33 | }
34 |
35 | public void setRadius(int radius)
36 | {
37 | this.radius = radius;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/entities/twoaggregates/AggregateOne.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.entities.twoaggregates;
2 |
3 | import java.util.List;
4 | import java.util.UUID;
5 |
6 | public class AggregateOne
7 | {
8 | private UUID aggregateOneId;
9 |
10 | private String myValue;
11 |
12 | private List aggregateTwos;
13 |
14 | public UUID getAggregateOneId()
15 | {
16 | return aggregateOneId;
17 | }
18 |
19 | public String getMyValue()
20 | {
21 | return myValue;
22 | }
23 |
24 | public List getAggregateTwos()
25 | {
26 | return aggregateTwos;
27 | }
28 |
29 | private AggregateOne()
30 | {
31 | }
32 |
33 | public AggregateOne(UUID aggregateOneId, String myValue, List aggregateTwos)
34 | {
35 | this.aggregateOneId = aggregateOneId;
36 | this.myValue = myValue;
37 | this.aggregateTwos = aggregateTwos;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/converters/UUIDConverter.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.converters;
2 |
3 | import java.nio.ByteBuffer;
4 | import java.util.UUID;
5 |
6 | /**
7 | * Used by sql2o to convert a value from the database into a {@link UUID}.
8 | */
9 | public class UUIDConverter implements Converter
10 | {
11 | public UUID convert(Object val) throws ConverterException {
12 | if (val == null){
13 | return null;
14 | }
15 |
16 | if (val instanceof UUID){
17 | return (UUID)val;
18 | }
19 |
20 | if(val instanceof String){
21 | return UUID.fromString((String) val);
22 | }
23 |
24 | if(val instanceof byte[]) {
25 | ByteBuffer buffer = ByteBuffer.wrap((byte[]) val);
26 | return new UUID(buffer.getLong(), buffer.getLong());
27 | }
28 |
29 | throw new ConverterException("Cannot convert type " + val.getClass() + " " + UUID.class);
30 | }
31 | }
32 |
33 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/converters/number/BigDecimalConverter.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.converters.number;
2 |
3 | import java.math.BigDecimal;
4 |
5 | /**
6 | * Used by sql2o to convert a value from the database into a {@link BigDecimal}.
7 | */
8 | public class BigDecimalConverter extends NumberConverter{
9 |
10 | public BigDecimalConverter() {
11 | super(false);
12 | }
13 |
14 | @Override
15 | protected BigDecimal convertNumberValue(Number val) {
16 | if (val instanceof BigDecimal){
17 | return (BigDecimal)val;
18 | }
19 | else{
20 | return BigDecimal.valueOf(val.doubleValue());
21 | }
22 | }
23 |
24 | @Override
25 | protected BigDecimal convertStringValue(String val) {
26 | return BigDecimal.valueOf(Double.parseDouble(val));
27 | }
28 |
29 | @Override
30 | protected String getTypeDescription() {
31 | return BigDecimal.class.toString();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/h2/H2TestUtil.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.h2;
2 |
3 | import org.apache.commons.io.FileUtils;
4 |
5 | import java.io.File;
6 | import java.io.IOException;
7 |
8 | public class H2TestUtil
9 | {
10 | public static final String h2Url = "jdbc:h2:mem:test;MODE=MySql;DB_CLOSE_DELAY=-1";
11 | public static final String h2User = "sa";
12 | public static final String h2Password = "";
13 |
14 | public static File getResourceFile(String name)
15 | {
16 | String filePath = H2TestUtil.class.getResource("/" + name).getFile();
17 | return new File(filePath);
18 | }
19 |
20 | public static String readResourceFile(String name)
21 | {
22 | File resourceFile = getResourceFile(name);
23 | try
24 | {
25 | return FileUtils.readFileToString(resourceFile, "UTF-8");
26 | }
27 | catch(IOException ex)
28 | {
29 | throw new RuntimeException(ex);
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/entities/myonetomanytable/MyOneToManyTable.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.entities.myonetomanytable;
2 |
3 | import java.util.List;
4 |
5 | public class MyOneToManyTable
6 | {
7 | private Integer id;
8 | private String myvalue;
9 | private List myManyTables;
10 |
11 | public Integer getId()
12 | {
13 | return id;
14 | }
15 |
16 | public String getMyvalue()
17 | {
18 | return myvalue;
19 | }
20 |
21 | public List getMyManyTables()
22 | {
23 | return myManyTables;
24 | }
25 |
26 | private MyOneToManyTable()
27 | {
28 | }
29 |
30 | public MyOneToManyTable(Integer id, String myvalue, List myManyTables)
31 | {
32 | this.id = id;
33 | this.myvalue = myvalue;
34 | this.myManyTables = myManyTables;
35 | }
36 |
37 | public void setMyvalue(String myvalue)
38 | {
39 | this.myvalue = myvalue;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/entities/shape/Rectangle.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.entities.shape;
2 |
3 | import lombok.EqualsAndHashCode;
4 |
5 | import java.util.List;
6 |
7 | @EqualsAndHashCode(callSuper = true)
8 | public class Rectangle extends Shape
9 | {
10 | private int width;
11 |
12 | private int height;
13 |
14 | private List corners;
15 |
16 | public int getWidth()
17 | {
18 | return width;
19 | }
20 |
21 | public int getHeight()
22 | {
23 | return height;
24 | }
25 |
26 | public List getCorners()
27 | {
28 | return corners;
29 | }
30 |
31 | private Rectangle()
32 | {
33 | }
34 |
35 | public Rectangle(Integer id, String color, Integer drawingId, int width, int height, List corners)
36 | {
37 | super(id, "rectangle", color, drawingId);
38 | this.width = width;
39 | this.height = height;
40 | this.corners = corners;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/photon-perf-test/src/test/java/com/github/molcikas/photon/perf/photon/RecipeInstruction.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.perf.photon;
2 |
3 | import java.util.UUID;
4 |
5 | public class RecipeInstruction
6 | {
7 | private UUID recipeInstructionId;
8 |
9 | private int stepNumber;
10 |
11 | private String description;
12 |
13 | public UUID getRecipeInstructionId()
14 | {
15 | return recipeInstructionId;
16 | }
17 |
18 | public int getStepNumber()
19 | {
20 | return stepNumber;
21 | }
22 |
23 | public String getDescription()
24 | {
25 | return description;
26 | }
27 |
28 | public void setDescription(String description)
29 | {
30 | this.description = description;
31 | }
32 |
33 | private RecipeInstruction()
34 | {
35 | }
36 |
37 | public RecipeInstruction(UUID recipeInstructionId, int stepNumber, String description)
38 | {
39 | this.recipeInstructionId = recipeInstructionId;
40 | this.stepNumber = stepNumber;
41 | this.description = description;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/h2/someaggregate/SomeAggregateDbSetup.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.h2.someaggregate;
2 |
3 | import com.github.molcikas.photon.Photon;
4 | import com.github.molcikas.photon.PhotonTransaction;
5 | import com.github.molcikas.photon.tests.unit.h2.H2TestUtil;
6 |
7 | public class SomeAggregateDbSetup
8 | {
9 | public static Photon setupDatabase()
10 | {
11 | Photon photon = new Photon(H2TestUtil.h2Url, H2TestUtil.h2User, H2TestUtil.h2Password);
12 | return setupDatabase(photon);
13 | }
14 |
15 | public static Photon setupDatabase(Photon photon)
16 | {
17 | try(PhotonTransaction transaction = photon.beginTransaction())
18 | {
19 | String sql = H2TestUtil.readResourceFile("setup-some-aggregate.sql");
20 |
21 | for(String statement : sql.split(";"))
22 | {
23 | transaction.query(statement.trim()).executeUpdate();
24 | }
25 |
26 | transaction.commit();
27 | }
28 |
29 | return photon;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/blueprints/table/DatabaseColumnDefinition.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.blueprints.table;
2 |
3 | import com.github.molcikas.photon.exceptions.PhotonException;
4 | import org.apache.commons.lang3.StringUtils;
5 |
6 | public class DatabaseColumnDefinition
7 | {
8 | private final String columnName;
9 |
10 | private final ColumnDataType columnDataType;
11 |
12 | public String getColumnName()
13 | {
14 | return columnName;
15 | }
16 |
17 | public ColumnDataType getColumnDataType()
18 | {
19 | return columnDataType;
20 | }
21 |
22 | public DatabaseColumnDefinition(String columnName, ColumnDataType columnDataType)
23 | {
24 | if(StringUtils.isBlank(columnName))
25 | {
26 | throw new PhotonException("Column name cannot be blank.");
27 | }
28 |
29 | this.columnName = columnName;
30 | this.columnDataType = columnDataType;
31 | }
32 |
33 | public DatabaseColumnDefinition(String columnName)
34 | {
35 | this(columnName, null);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 molcikas
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/h2/shape/ShapeMultiTableDbSetup.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.h2.shape;
2 |
3 | import com.github.molcikas.photon.Photon;
4 | import com.github.molcikas.photon.PhotonTransaction;
5 | import com.github.molcikas.photon.tests.unit.h2.H2TestUtil;
6 |
7 | import java.util.List;
8 |
9 | public class ShapeMultiTableDbSetup
10 | {
11 | public static Photon setupDatabase()
12 | {
13 | Photon photon = new Photon(H2TestUtil.h2Url, H2TestUtil.h2User, H2TestUtil.h2Password);
14 | return setupDatabase(photon);
15 | }
16 |
17 | public static Photon setupDatabase(Photon photon)
18 | {
19 | try(PhotonTransaction transaction = photon.beginTransaction())
20 | {
21 | String sql = H2TestUtil.readResourceFile("setup-shape-multi-table.sql");
22 |
23 | for(String statement : sql.split(";"))
24 | {
25 | transaction.query(statement.trim()).executeUpdate();
26 | }
27 |
28 | transaction.commit();
29 | }
30 |
31 | return photon;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/All_Core_Unit_Tests.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/converters/date/InstantConverter.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.converters.date;
2 |
3 | import com.github.molcikas.photon.converters.ConverterException;
4 | import com.github.molcikas.photon.converters.Converter;
5 |
6 | import java.time.Instant;
7 | import java.util.Date;
8 |
9 | public class InstantConverter implements Converter
10 | {
11 | public Instant convert(Object val) throws ConverterException
12 | {
13 | if (val == null)
14 | {
15 | return null;
16 | }
17 |
18 | if(Instant.class.isAssignableFrom(val.getClass()))
19 | {
20 | return (Instant) val;
21 | }
22 |
23 | if(Date.class.isAssignableFrom(val.getClass()))
24 | {
25 | return Instant.ofEpochMilli(((Date) val).getTime());
26 | }
27 |
28 | if (val instanceof Number)
29 | {
30 | return Instant.ofEpochMilli(((Number) val).longValue());
31 | }
32 |
33 | throw new ConverterException("Cannot convert type " + val.getClass().toString() + " to java.util.Instant");
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/converters/IOUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.converters;
2 |
3 | import java.io.ByteArrayOutputStream;
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 | import java.io.Reader;
7 |
8 | public class IOUtils
9 | {
10 | private static final int EOF = -1;
11 | private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
12 |
13 | public static byte[] toByteArray(InputStream input) throws IOException {
14 | ByteArrayOutputStream output = new ByteArrayOutputStream();
15 | byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
16 | int n;
17 | while (EOF != (n = input.read(buffer))) {
18 | output.write(buffer, 0, n);
19 | }
20 | return output.toByteArray();
21 | }
22 |
23 | public static String toString(Reader input) throws IOException {
24 | StringBuilder output = new StringBuilder();
25 | char[] buffer = new char[DEFAULT_BUFFER_SIZE];
26 | int n;
27 | while (EOF != (n = input.read(buffer))) {
28 | output.append(buffer, 0, n);
29 | }
30 | return output.toString();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/entities/myonetomanytable/MyManyTable.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.entities.myonetomanytable;
2 |
3 | import java.util.List;
4 |
5 | public class MyManyTable
6 | {
7 | private Integer id;
8 |
9 | private Integer parent;
10 |
11 | private String myOtherValueWithDiffName;
12 |
13 | private List myThirdTables;
14 |
15 | public Integer getId()
16 | {
17 | return id;
18 | }
19 |
20 | public Integer getParent()
21 | {
22 | return parent;
23 | }
24 |
25 | public String getMyOtherValueWithDiffName()
26 | {
27 | return myOtherValueWithDiffName;
28 | }
29 |
30 | public List getMyThirdTables()
31 | {
32 | return myThirdTables;
33 | }
34 |
35 | private MyManyTable()
36 | {
37 | }
38 |
39 | public MyManyTable(Integer parent, String myOtherValueWithDiffName, List myThirdTables)
40 | {
41 | this.parent = parent;
42 | this.myOtherValueWithDiffName = myOtherValueWithDiffName;
43 | this.myThirdTables = myThirdTables;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/query/PhotonQueryResultRow.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.query;
2 |
3 | import java.util.Collections;
4 | import java.util.HashMap;
5 | import java.util.Map;
6 | import java.util.Set;
7 |
8 | public class PhotonQueryResultRow
9 | {
10 | private final Map values;
11 | private Object firstValue;
12 |
13 | public void addValue(String columnName, Object value)
14 | {
15 | if(values.isEmpty())
16 | {
17 | firstValue = value;
18 | }
19 | values.put(columnName, value);
20 | }
21 |
22 | public Object getValue(String columnName)
23 | {
24 | return values.get(columnName);
25 | }
26 |
27 | public Set> getValues()
28 | {
29 | return values.entrySet();
30 | }
31 |
32 | public Map getValuesMap()
33 | {
34 | return Collections.unmodifiableMap(values);
35 | }
36 |
37 | public Object getFirstValue()
38 | {
39 | return firstValue;
40 | }
41 |
42 | public PhotonQueryResultRow()
43 | {
44 | values = new HashMap<>();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/blueprints/TableValueTest.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.blueprints;
2 |
3 | import com.github.molcikas.photon.blueprints.table.TableBlueprintAndKey;
4 | import com.github.molcikas.photon.blueprints.table.TableValue;
5 | import com.github.molcikas.photon.converters.Convert;
6 | import org.junit.Test;
7 |
8 | import java.util.UUID;
9 |
10 | import static org.junit.Assert.assertTrue;
11 |
12 | public class TableValueTest
13 | {
14 | @Test
15 | public void equals_byteArray_areEqual()
16 | {
17 | UUID uuid = UUID.fromString("3e038307-a9b6-11e6-ab83-0a0027000010");
18 | byte[] bytes = (byte[]) Convert.getConverter(byte[].class).convert(uuid);
19 |
20 | TableValue value1 = new TableValue(bytes);
21 | TableValue value2 = new TableValue(bytes);
22 | assertTrue(value1.equals(value2));
23 |
24 | TableBlueprintAndKey tableBlueprintAndKey1 = new TableBlueprintAndKey(null, value1);
25 | TableBlueprintAndKey tableBlueprintAndKey2 = new TableBlueprintAndKey(null, value2);
26 | assertTrue(tableBlueprintAndKey1.equals(tableBlueprintAndKey2));
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/entities/mytable/MyTable.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.entities.mytable;
2 |
3 | public class MyTable
4 | {
5 | private Integer id;
6 | private String myvalue;
7 | private int version;
8 |
9 | private MyOtherTable myOtherTable;
10 |
11 | public Integer getId()
12 | {
13 | return id;
14 | }
15 |
16 | public String getMyvalue()
17 | {
18 | return myvalue;
19 | }
20 |
21 | public int getVersion()
22 | {
23 | return version;
24 | }
25 |
26 | public MyOtherTable getMyOtherTable()
27 | {
28 | return myOtherTable;
29 | }
30 |
31 | private MyTable()
32 | {
33 | }
34 |
35 | public MyTable(Integer id, String myvalue, MyOtherTable myOtherTable)
36 | {
37 | this.id = id;
38 | this.myvalue = myvalue;
39 | this.version = 1;
40 | this.myOtherTable = myOtherTable;
41 | }
42 |
43 | public void setMyvalue(String myvalue)
44 | {
45 | this.myvalue = myvalue;
46 | }
47 |
48 | public void setVersion(int version)
49 | {
50 | this.version = version;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/Integration_Tests.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/entities/shape/ShapeColorHistory.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.entities.shape;
2 |
3 | import java.time.ZonedDateTime;
4 |
5 | public class ShapeColorHistory
6 | {
7 | private int id;
8 |
9 | private int shapeId;
10 |
11 | private ZonedDateTime dateChanged;
12 |
13 | private String colorName;
14 |
15 | public int getId()
16 | {
17 | return id;
18 | }
19 |
20 | public int getShapeId()
21 | {
22 | return shapeId;
23 | }
24 |
25 | public ZonedDateTime getDateChanged()
26 | {
27 | return dateChanged;
28 | }
29 |
30 | public String getColorName()
31 | {
32 | return colorName;
33 | }
34 |
35 | private ShapeColorHistory()
36 | {
37 | }
38 |
39 | public ShapeColorHistory(int id, int shapeId, ZonedDateTime dateChanged, String colorName)
40 | {
41 | this.id = id;
42 | this.shapeId = shapeId;
43 | this.dateChanged = dateChanged;
44 | this.colorName = colorName;
45 | }
46 |
47 | public void setColorName(String colorName)
48 | {
49 | this.colorName = colorName;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/photon-perf-test/src/test/java/com/github/molcikas/photon/perf/hibernate/RecipeInstructionEntity.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.perf.hibernate;
2 |
3 | import javax.persistence.Column;
4 | import javax.persistence.Entity;
5 | import javax.persistence.Id;
6 | import javax.persistence.Table;
7 | import java.util.UUID;
8 |
9 | @Entity
10 | @Table(name = "recipeinstruction")
11 | public class RecipeInstructionEntity
12 | {
13 | @Id
14 | @Column(columnDefinition = "BINARY(16)", length = 16)
15 | public UUID recipeInstructionId;
16 |
17 | @Column(columnDefinition = "BINARY(16)", length = 16)
18 | public UUID recipeId;
19 |
20 | @Column
21 | public int stepNumber;
22 |
23 | @Column
24 | public String description;
25 |
26 | public void setDescription(String description)
27 | {
28 | this.description = description;
29 | }
30 |
31 | protected RecipeInstructionEntity()
32 | {
33 | }
34 |
35 | public RecipeInstructionEntity(UUID recipeInstructionId, UUID recipeId, int stepNumber, String description)
36 | {
37 | this.recipeInstructionId = recipeInstructionId;
38 | this.recipeId = recipeId;
39 | this.stepNumber = stepNumber;
40 | this.description = description;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/blueprints/table/ColumnDataType.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.blueprints.table;
2 |
3 | public enum ColumnDataType
4 | {
5 | BIT(-7),
6 | TINYINT(-6),
7 | SMALLINT(5),
8 | INTEGER(4),
9 | BIGINT(-5),
10 | FLOAT(6),
11 | REAL(7),
12 | DOUBLE(8),
13 | NUMERIC(2),
14 | DECIMAL(3),
15 | CHAR(1),
16 | VARCHAR(12),
17 | LONGVARCHAR(-1),
18 | DATE(91),
19 | TIME(92),
20 | TIMESTAMP(93),
21 | BINARY(-2),
22 | VARBINARY(-3),
23 | LONGVARBINARY(-4),
24 | NULL(0),
25 | OTHER(1111),
26 | JAVA_OBJECT(2000),
27 | DISTINCT(2001),
28 | STRUCT(2002),
29 | ARRAY(2003),
30 | BLOB(2004),
31 | CLOB(2005),
32 | REF(2006),
33 | DATALINK(70),
34 | BOOLEAN(16),
35 | ROWID(-8),
36 | NCHAR(-15),
37 | NVARCHAR(-9),
38 | LONGNVARCHAR(-16),
39 | NCLOB(2011),
40 | SQLXML(2009),
41 | REF_CURSOR(2012),
42 | TIME_WITH_TIMEZONE(2013),
43 | TIMESTAMP_WITH_TIMEZONE(2014);
44 |
45 | private final int jdbcType;
46 |
47 | public int getJdbcType()
48 | {
49 | return jdbcType;
50 | }
51 |
52 | ColumnDataType(int jdbcType)
53 | {
54 | this.jdbcType = jdbcType;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/converters/date/LocalDateConverter.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.converters.date;
2 |
3 | import com.github.molcikas.photon.converters.Converter;
4 | import com.github.molcikas.photon.converters.ConverterException;
5 |
6 | import java.time.LocalDate;
7 | import java.util.Date;
8 |
9 | public class LocalDateConverter implements Converter
10 | {
11 | private static final long MILLSECONDS_PER_DAY = 86400000;
12 |
13 | public LocalDate convert(Object val) throws ConverterException
14 | {
15 | if (val == null)
16 | {
17 | return null;
18 | }
19 |
20 | if(LocalDate.class.isAssignableFrom(val.getClass()))
21 | {
22 | return (LocalDate) val;
23 | }
24 |
25 | if(Date.class.isAssignableFrom(val.getClass()))
26 | {
27 | return LocalDate.ofEpochDay(((Date) val).getTime() / MILLSECONDS_PER_DAY);
28 | }
29 |
30 | if (val instanceof Number)
31 | {
32 | return LocalDate.ofEpochDay(((Number) val).longValue() / MILLSECONDS_PER_DAY);
33 | }
34 |
35 | throw new ConverterException("Cannot convert type " + val.getClass().toString() + " to java.util.LocalDate");
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/blueprints/entity/CompoundEntityFieldValueMapping.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.blueprints.entity;
2 |
3 | import java.util.Map;
4 |
5 | /**
6 | * Interface for custom mapping a group of database values to and from one or more entity fields.
7 | *
8 | * @param - The entity class type
9 | */
10 | public interface CompoundEntityFieldValueMapping
11 | {
12 | /**
13 | * Get the database values from a given entity instance.
14 | *
15 | * @param entityInstance - The entity instance
16 | * @return - The database values. The key is the column name and the value is the database value.
17 | */
18 | Map getDatabaseValues(E entityInstance);
19 |
20 | /**
21 | * Set a given set of database values on a given entity instance.
22 | *
23 | * @param entityInstance - The entity instance
24 | * @param databaseValues - The database values. The key is the column name and the value is the database value.
25 | * @return - The field values to set on the entity. The key is the field name and the value is the field value. If
26 | * the values were applied directly to the entity instance, then null or an empty map can be returned.
27 | */
28 | Map setFieldValues(E entityInstance, Map databaseValues);
29 | }
30 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/converters/date/ZonedDateTimeConverter.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.converters.date;
2 |
3 | import com.github.molcikas.photon.converters.Converter;
4 | import com.github.molcikas.photon.converters.ConverterException;
5 |
6 | import java.time.*;
7 | import java.util.Date;
8 |
9 | public class ZonedDateTimeConverter implements Converter
10 | {
11 | public ZonedDateTime convert(Object val) throws ConverterException
12 | {
13 | if (val == null)
14 | {
15 | return null;
16 | }
17 |
18 | if(ZonedDateTime.class.isAssignableFrom(val.getClass()))
19 | {
20 | return (ZonedDateTime) val;
21 | }
22 |
23 | if(Date.class.isAssignableFrom(val.getClass()))
24 | {
25 | Instant instant = Instant.ofEpochMilli(((Date) val).getTime());
26 | return ZonedDateTime.ofInstant(instant, ZoneId.systemDefault());
27 | }
28 |
29 | if (val instanceof Number)
30 | {
31 | Instant instant = Instant.ofEpochMilli(((Number) val).longValue());
32 | return ZonedDateTime.ofInstant(instant, ZoneId.systemDefault());
33 | }
34 |
35 | throw new ConverterException("Cannot convert type " + val.getClass().toString() + " to java.util.ZonedDateTime");
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/query/ParameterValue.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.query;
2 |
3 | import com.github.molcikas.photon.blueprints.table.ColumnBlueprint;
4 | import com.github.molcikas.photon.blueprints.table.ColumnDataType;
5 | import com.github.molcikas.photon.blueprints.table.TableValue;
6 | import com.github.molcikas.photon.converters.Converter;
7 | import lombok.EqualsAndHashCode;
8 | import lombok.Getter;
9 |
10 | @EqualsAndHashCode(doNotUseGetters = true)
11 | public class ParameterValue
12 | {
13 | private final TableValue value;
14 |
15 | @Getter
16 | private final ColumnDataType dataType;
17 |
18 | @Getter
19 | private final Converter customSerializer;
20 |
21 | public ParameterValue(Object value, ColumnDataType dataType, Converter customSerializer)
22 | {
23 | this.value = new TableValue(value);
24 | this.dataType = dataType;
25 | this.customSerializer = customSerializer;
26 | }
27 |
28 | public ParameterValue(Object value, ColumnBlueprint columnBlueprint)
29 | {
30 | this.value = new TableValue(value);
31 | this.dataType = columnBlueprint.getColumnDataType();
32 | this.customSerializer = columnBlueprint.getCustomSerializer();
33 | }
34 |
35 | public Object getRawValue()
36 | {
37 | return value.getValue();
38 | }
39 | }
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/converters/DefaultEnumConverterFactory.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.converters;
2 |
3 | /**
4 | * Default implementation of {@link EnumConverterFactory},
5 | * used by sql2o to convert a value from the database into an {@link Enum}.
6 | */
7 | public class DefaultEnumConverterFactory implements EnumConverterFactory {
8 | public Converter newConverter(final Class enumType) {
9 | return new Converter() {
10 | @SuppressWarnings("unchecked")
11 | public E convert(Object val) throws ConverterException {
12 | if (val == null) {
13 | return null;
14 | }
15 | try {
16 | if (val instanceof String){
17 | return (E)Enum.valueOf(enumType, val.toString());
18 | } else if (val instanceof Number){
19 | return enumType.getEnumConstants()[((Number)val).intValue()];
20 | }
21 | } catch (Throwable t) {
22 | throw new ConverterException("Error converting value '" + val.toString() + "' to " + enumType.getName(), t);
23 | }
24 | throw new ConverterException("Cannot convert type '" + val.getClass().getName() + "' to an Enum");
25 | }
26 | };
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/h2/product/ProductDbSetup.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.h2.product;
2 |
3 | import com.github.molcikas.photon.Photon;
4 | import com.github.molcikas.photon.PhotonTransaction;
5 | import com.github.molcikas.photon.tests.unit.h2.H2TestUtil;
6 |
7 | public class ProductDbSetup
8 | {
9 | public static Photon setupDatabase()
10 | {
11 | Photon photon = new Photon(H2TestUtil.h2Url, H2TestUtil.h2User, H2TestUtil.h2Password);
12 | return setupDatabase(photon);
13 | }
14 |
15 | public static Photon setupDatabase(Photon photon)
16 | {
17 | try(PhotonTransaction transaction = photon.beginTransaction())
18 | {
19 | transaction.query("DROP TABLE IF EXISTS `product`").executeUpdate();
20 | transaction.query("CREATE TABLE `product` (\n" +
21 | " `theProductId` int(11) NOT NULL AUTO_INCREMENT,\n" +
22 | " `numerator` int(11) NOT NULL,\n" +
23 | " `denominator` int(11) NOT NULL,\n" +
24 | " PRIMARY KEY (`theProductId`)\n" +
25 | ")").executeUpdate();
26 | transaction.query("insert into `product` (`theProductId`, `numerator`, `denominator`) values (1, 2, 3)").executeUpdate();
27 |
28 | transaction.commit();
29 | }
30 |
31 | return photon;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/converters/date/LocalDateTimeConverter.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.converters.date;
2 |
3 | import com.github.molcikas.photon.converters.Converter;
4 | import com.github.molcikas.photon.converters.ConverterException;
5 |
6 | import java.time.Instant;
7 | import java.time.LocalDateTime;
8 | import java.time.ZoneId;
9 | import java.util.Date;
10 |
11 | public class LocalDateTimeConverter implements Converter
12 | {
13 | public LocalDateTime convert(Object val) throws ConverterException
14 | {
15 | if (val == null)
16 | {
17 | return null;
18 | }
19 |
20 | if(LocalDateTime.class.isAssignableFrom(val.getClass()))
21 | {
22 | return (LocalDateTime) val;
23 | }
24 |
25 | if(Date.class.isAssignableFrom(val.getClass()))
26 | {
27 | Instant instant = Instant.ofEpochMilli(((Date) val).getTime());
28 | return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
29 | }
30 |
31 | if (val instanceof Number)
32 | {
33 | Instant instant = Instant.ofEpochMilli(((Number) val).longValue());
34 | return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
35 | }
36 |
37 | throw new ConverterException("Cannot convert type " + val.getClass().toString() + " to java.util.LocalDateTime");
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/converters/BooleanConverter.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.converters;
2 |
3 | /**
4 | * Created with IntelliJ IDEA.
5 | * User: lars
6 | * Date: 6/1/13
7 | * Time: 10:54 PM
8 | * To change this template use File | Settings | File Templates.
9 | */
10 | public class BooleanConverter implements Converter
11 | {
12 |
13 | public Boolean convert(Object val) throws ConverterException {
14 | if (val == null) return false;
15 |
16 | if (val instanceof Boolean) {
17 | return (Boolean) val;
18 | }
19 |
20 | if (val instanceof Number) {
21 | return ((Number)val).intValue() != 0;
22 | }
23 |
24 | if (val instanceof Character) {
25 | // cast to char is required to compile with java 8
26 | return (char)val =='Y'
27 | || (char)val =='T'
28 | || (char)val =='J';
29 | }
30 |
31 | if (val instanceof String) {
32 | String strVal = ((String)val).trim();
33 | return "Y".equalsIgnoreCase(strVal) || "YES".equalsIgnoreCase(strVal) || "TRUE".equalsIgnoreCase(strVal) ||
34 | "T".equalsIgnoreCase(strVal) || "J".equalsIgnoreCase(strVal);
35 | }
36 |
37 | throw new ConverterException("Don't know how to convert type " + val.getClass().getName() + " to " + Boolean.class.getName());
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/blueprints/entity/EntityFieldValueMapping.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.blueprints.entity;
2 |
3 | import java.util.Map;
4 |
5 | /**
6 | * Interface for custom mapping for getting and setting field values on an entity for a given database column.
7 | *
8 | * @param - The entity class type
9 | * @param - The field class type
10 | */
11 | public interface EntityFieldValueMapping
12 | {
13 | /**
14 | * Get the field value from the entity instance that maps to the database column.
15 | *
16 | * @param entityInstance - The entity instance
17 | * @return - The field value
18 | */
19 | F getFieldValue(E entityInstance);
20 |
21 | /**
22 | * Set the field value(s) on a given entity instance that maps to the database column. The value can be applied
23 | * directly to the entity instance, or the method can return a map of values that will be applied to the entity
24 | * instance (to avoid having to write reflection code directly in this method).
25 | *
26 | * @param entityInstance - The entity instance
27 | * @param fieldValue - The field value
28 | * @return - The field values to set on the entity. The key is the field name and the value is the field value. If
29 | * the value was applied directly to the entity instance, then null or an empty map can be returned.
30 | */
31 | Map setFieldValue(E entityInstance, F fieldValue);
32 | }
33 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/h2/someaggregate/SomeAggregateTests.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.h2.someaggregate;
2 |
3 | import com.github.molcikas.photon.Photon;
4 | import com.github.molcikas.photon.PhotonTransaction;
5 | import com.github.molcikas.photon.blueprints.table.ColumnDataType;
6 | import com.github.molcikas.photon.tests.unit.entities.someaggregate.SomeAggregate;
7 | import com.github.molcikas.photon.tests.unit.entities.someaggregate.SomeClass;
8 | import org.junit.Before;
9 | import org.junit.Test;
10 |
11 | public class SomeAggregateTests
12 | {
13 | private Photon photon;
14 |
15 | @Before
16 | public void setupDatabase()
17 | {
18 | photon = SomeAggregateDbSetup.setupDatabase();
19 | }
20 |
21 | @Test
22 | public void update_entityWithOnlyId_doesNothing()
23 | {
24 | registerAggregate();
25 |
26 | try(PhotonTransaction transaction = photon.beginTransaction())
27 | {
28 | SomeAggregate someAggregate = transaction
29 | .query(SomeAggregate.class)
30 | .fetchById(1);
31 |
32 | transaction.save(someAggregate);
33 | }
34 | }
35 |
36 | private void registerAggregate()
37 | {
38 | photon
39 | .registerAggregate(SomeAggregate.class)
40 | .withChild("fieldOne", SomeClass.class)
41 | .withForeignKeyToParent("someAggregateId", ColumnDataType.INTEGER)
42 | .addAsChild()
43 | .register();
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/entities/shape/Shape.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.entities.shape;
2 |
3 | import lombok.EqualsAndHashCode;
4 |
5 | import java.util.List;
6 |
7 | @EqualsAndHashCode
8 | public class Shape
9 | {
10 | private Integer id;
11 |
12 | private Integer drawingId;
13 |
14 | private String type;
15 |
16 | private String color;
17 |
18 | private List colorHistory;
19 |
20 | public Integer getId()
21 | {
22 | return id;
23 | }
24 |
25 | public String getType()
26 | {
27 | return type;
28 | }
29 |
30 | public String getColor()
31 | {
32 | return color;
33 | }
34 |
35 | public List getColorHistory()
36 | {
37 | return colorHistory;
38 | }
39 |
40 | protected Shape()
41 | {
42 | }
43 |
44 | public Shape(Integer id, String type, String color, Integer drawingId)
45 | {
46 | this.id = id;
47 | this.type = type;
48 | this.color = color;
49 | this.drawingId = drawingId;
50 | }
51 |
52 |
53 | public Shape(Integer id,
54 | String type,
55 | String color,
56 | Integer drawingId,
57 | List colorHistory)
58 | {
59 | this.id = id;
60 | this.type = type;
61 | this.color = color;
62 | this.drawingId = drawingId;
63 | this.colorHistory = colorHistory;
64 | }
65 |
66 | public void setColor(String color)
67 | {
68 | this.color = color;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/blueprints/entity/MappedClassBlueprint.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.blueprints.entity;
2 |
3 | import com.github.molcikas.photon.exceptions.PhotonException;
4 |
5 | import java.lang.reflect.Field;
6 | import java.util.ArrayList;
7 | import java.util.Arrays;
8 | import java.util.Collections;
9 | import java.util.List;
10 | import java.util.stream.Collectors;
11 |
12 | public class MappedClassBlueprint
13 | {
14 | private final Class mappedClass;
15 | private final boolean includeAllFields;
16 | private final List includedFields;
17 |
18 | public Class getMappedClass()
19 | {
20 | return mappedClass;
21 | }
22 |
23 | public MappedClassBlueprint(Class mappedClass, boolean includeAllFields, List includedFields)
24 | {
25 | if(mappedClass == null)
26 | {
27 | throw new PhotonException("Mapped class cannot be null.");
28 | }
29 |
30 | this.mappedClass = mappedClass;
31 | this.includeAllFields = includeAllFields;
32 | this.includedFields = new ArrayList<>(includedFields != null ? includedFields : Collections.emptyList());
33 | }
34 |
35 | public List getIncludedFields()
36 | {
37 | List fieldsToInclude = Arrays.asList(mappedClass.getDeclaredFields());
38 |
39 | if(includeAllFields)
40 | {
41 | return fieldsToInclude;
42 | }
43 |
44 | return fieldsToInclude
45 | .stream()
46 | .filter(f -> includedFields.contains(f.getName()))
47 | .collect(Collectors.toList());
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/query/PhotonSqlParameter.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.query;
2 |
3 | import com.github.molcikas.photon.blueprints.table.ColumnDataType;
4 | import com.github.molcikas.photon.blueprints.table.TableBlueprintBuilder;
5 | import com.github.molcikas.photon.options.PhotonOptions;
6 |
7 | import java.util.Collection;
8 |
9 | public class PhotonSqlParameter
10 | {
11 | private final String name;
12 | private Object value;
13 | private ColumnDataType dataType;
14 | private boolean isCollection;
15 |
16 | public String getName()
17 | {
18 | return name;
19 | }
20 |
21 | public Object getValue()
22 | {
23 | return value;
24 | }
25 |
26 | public ColumnDataType getDataType()
27 | {
28 | return dataType;
29 | }
30 |
31 | public boolean isCollection()
32 | {
33 | return isCollection;
34 | }
35 |
36 | public PhotonSqlParameter(String name)
37 | {
38 | this.name = name;
39 | }
40 |
41 | public void assignValue(Object value, PhotonOptions photonOptions)
42 | {
43 | this.value = value;
44 | this.dataType = value != null ? TableBlueprintBuilder.defaultColumnDataTypeForField(value.getClass(), photonOptions).dataType : null;
45 | this.isCollection = value != null && Collection.class.isAssignableFrom(value.getClass());
46 | }
47 |
48 | public void assignValue(Object value, ColumnDataType dataType)
49 | {
50 | this.value = value;
51 | this.dataType = dataType;
52 | this.isCollection = value != null && Collection.class.isAssignableFrom(value.getClass());
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/blueprints/MyTableBlueprintTests.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.blueprints;
2 |
3 | import org.junit.Assert;
4 | import org.junit.Test;
5 | import com.github.molcikas.photon.datasource.GenericDataSource;
6 | import com.github.molcikas.photon.Photon;
7 | import com.github.molcikas.photon.exceptions.PhotonException;
8 | import com.github.molcikas.photon.tests.unit.entities.mytable.MyOtherTable;
9 | import com.github.molcikas.photon.tests.unit.entities.mytable.MyTable;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | public class MyTableBlueprintTests
14 | {
15 | @Test
16 | public void registerEntity_childWithNoForeignKeyToParent_ThrowsException()
17 | {
18 | Photon photon = new Photon(new GenericDataSource("", "", ""));
19 |
20 | try
21 | {
22 | photon.registerAggregate(MyTable.class)
23 | .withId("id")
24 | .withChild("myOtherTable", MyOtherTable.class)
25 | .withId("id")
26 | .addAsChild()
27 | .register();
28 |
29 | Assert.fail("Failed to throw PhotonException.");
30 | }
31 | catch(PhotonException ex)
32 | {
33 | assertTrue(ex.getMessage().contains("foreign key"));
34 | }
35 | }
36 |
37 | // TODO: Aggregate root entity must have primary key.
38 |
39 | // TODO: Entity without a primary key field cannot have child entities.
40 |
41 | // TODO: Entity without a primary key field cannot have foreign key list field.
42 |
43 | // TODO: Cannot set withForeignKeyToParent to the primary key and also set primaryKeyAutoIncrement to true.
44 | }
45 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/entities/recipe/RecipeInstruction.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.entities.recipe;
2 |
3 | import java.util.Objects;
4 | import java.util.UUID;
5 |
6 | public class RecipeInstruction
7 | {
8 | private UUID recipeInstructionId;
9 |
10 | private int stepNumber;
11 |
12 | private String description;
13 |
14 | public UUID getRecipeInstructionId()
15 | {
16 | return recipeInstructionId;
17 | }
18 |
19 | public int getStepNumber()
20 | {
21 | return stepNumber;
22 | }
23 |
24 | public String getDescription()
25 | {
26 | return description;
27 | }
28 |
29 | private RecipeInstruction()
30 | {
31 | }
32 |
33 | public RecipeInstruction(UUID recipeInstructionId, int stepNumber, String description)
34 | {
35 | this.recipeInstructionId = recipeInstructionId;
36 | this.stepNumber = stepNumber;
37 | this.description = description;
38 | }
39 |
40 | public void setDescription(String description)
41 | {
42 | this.description = description;
43 | }
44 |
45 | @Override
46 | public boolean equals(Object o)
47 | {
48 | if (this == o) return true;
49 | if (o == null || getClass() != o.getClass()) return false;
50 | RecipeInstruction that = (RecipeInstruction) o;
51 | return stepNumber == that.stepNumber &&
52 | Objects.equals(recipeInstructionId, that.recipeInstructionId) &&
53 | Objects.equals(description, that.description);
54 | }
55 |
56 | @Override
57 | public int hashCode()
58 | {
59 | return Objects.hash(recipeInstructionId, stepNumber, description);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/photon-core/build.gradle:
--------------------------------------------------------------------------------
1 |
2 | description = "A micro ORM that gives developers control over the SQL executed while also providing an easy way to do basic CRUD operations on aggregates."
3 | archivesBaseName = "photon-core"
4 |
5 | dependencies {
6 | compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.4'
7 | compile group: 'org.apache.commons', name: 'commons-collections4', version: '4.1'
8 | compile group: 'org.projectlombok', name: 'lombok', version: '1.16.16'
9 |
10 | // Database
11 | testCompile group: 'com.h2database', name: 'h2', version: '1.4.193'
12 | testCompile group: 'mysql', name: 'mysql-connector-java', version: '5.1.40'
13 | testCompile group: 'com.microsoft.sqlserver', name: 'sqljdbc4', version: '4.0'
14 | testCompile group: 'org.postgresql', name: 'postgresql', version: '9.4.1212'
15 | testCompile files('src/test/libs/ojdbc6.jar')
16 |
17 | // Unit testing
18 | testCompile group: 'junit', name: 'junit', version: '4.12'
19 | testCompile group: 'org.mockito', name: 'mockito-all', version: '1.10.19'
20 | testCompile group: 'commons-io', name: 'commons-io', version: '2.5'
21 | }
22 |
23 | idea {
24 | module {
25 | iml.withXml { xmlFile ->
26 | def facetManager = xmlFile.asNode().component.find { it.@name == 'FacetManager' } as Node
27 | if (!facetManager) {
28 | facetManager = xmlFile.asNode().appendNode('component', [name: 'FacetManager'])
29 | }
30 | def builder = new NodeBuilder()
31 | def infinitestFacet = builder.facet(type: "Infinitest", name: 'Infinitest') {
32 | configuration {}
33 | }
34 | facetManager.append infinitestFacet
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/classes/test/photon-perf-test/META-INF/persistence.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 | com.github.molcikas.photon.perf.hibernate.RecipeEntity
8 | com.github.molcikas.photon.perf.hibernate.RecipeIngredientEntity
9 | com.github.molcikas.photon.perf.hibernate.RecipeInstructionEntity
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/photon-perf-test/src/test/resources/META-INF/persistence.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 | com.github.molcikas.photon.perf.hibernate.RecipeEntity
8 | com.github.molcikas.photon.perf.hibernate.RecipeIngredientEntity
9 | com.github.molcikas.photon.perf.hibernate.RecipeInstructionEntity
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/integration/PhotonTestTable.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.integration;
2 |
3 | import java.time.ZonedDateTime;
4 | import java.util.Objects;
5 | import java.util.UUID;
6 |
7 | public class PhotonTestTable
8 | {
9 | private Integer id;
10 | private UUID uuidColumn;
11 | private ZonedDateTime dateColumn;
12 | private String varcharColumn;
13 |
14 | public int getId()
15 | {
16 | return id;
17 | }
18 |
19 | public UUID getUuidColumn()
20 | {
21 | return uuidColumn;
22 | }
23 |
24 | public ZonedDateTime getDateColumn()
25 | {
26 | return dateColumn;
27 | }
28 |
29 | public String getVarcharColumn()
30 | {
31 | return varcharColumn;
32 | }
33 |
34 | private PhotonTestTable()
35 | {
36 | }
37 |
38 | public PhotonTestTable(Integer id, UUID uuidColumn, ZonedDateTime dateColumn, String varcharColumn)
39 | {
40 | this.id = id;
41 | this.uuidColumn = uuidColumn;
42 | this.dateColumn = dateColumn;
43 | this.varcharColumn = varcharColumn;
44 | }
45 |
46 | @Override
47 | public boolean equals(Object o)
48 | {
49 | if (this == o) return true;
50 | if (o == null || getClass() != o.getClass()) return false;
51 | PhotonTestTable that = (PhotonTestTable) o;
52 | return Objects.equals(id, that.id) &&
53 | Objects.equals(uuidColumn, that.uuidColumn) &&
54 | Objects.equals(dateColumn, that.dateColumn) &&
55 | Objects.equals(varcharColumn, that.varcharColumn);
56 | }
57 |
58 | @Override
59 | public int hashCode()
60 | {
61 | return Objects.hash(id, uuidColumn, dateColumn, varcharColumn);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/photon-perf-test/src/test/java/com/github/molcikas/photon/perf/hibernate/RecipeIngredientEntity.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.perf.hibernate;
2 |
3 | import javax.persistence.Column;
4 | import javax.persistence.Entity;
5 | import javax.persistence.Id;
6 | import javax.persistence.Table;
7 | import java.util.UUID;
8 |
9 | @Entity
10 | @Table(name = "recipeingredient")
11 | public class RecipeIngredientEntity
12 | {
13 | @Id
14 | @Column(columnDefinition = "BINARY(16)", length = 16)
15 | public UUID recipeIngredientId;
16 |
17 | @Column(columnDefinition = "BINARY(16)", length = 16)
18 | public UUID recipeId;
19 |
20 | @Column
21 | public boolean isRequired;
22 |
23 | @Column
24 | public String quantity;
25 |
26 | @Column
27 | public String quantityUnit;
28 |
29 | @Column
30 | public String quantityDetail;
31 |
32 | @Column
33 | public String name;
34 |
35 | @Column
36 | public String preparation;
37 |
38 | @Column
39 | public int orderBy;
40 |
41 | public void setName(String name)
42 | {
43 | this.name = name;
44 | }
45 |
46 | protected RecipeIngredientEntity()
47 | {
48 | }
49 |
50 | public RecipeIngredientEntity(UUID recipeIngredientId, UUID recipeId, boolean isRequired, String quantity, String quantityUnit, String quantityDetail, String name, String preparation, int orderBy)
51 | {
52 | this.recipeIngredientId = recipeIngredientId;
53 | this.recipeId = recipeId;
54 | this.isRequired = isRequired;
55 | this.quantity = quantity;
56 | this.quantityUnit = quantityUnit;
57 | this.quantityDetail = quantityDetail;
58 | this.name = name;
59 | this.preparation = preparation;
60 | this.orderBy = orderBy;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/blueprints/table/TableValue.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.blueprints.table;
2 |
3 | import lombok.Getter;
4 |
5 | import javax.xml.bind.DatatypeConverter;
6 | import java.util.Arrays;
7 | import java.util.Objects;
8 |
9 | public class TableValue
10 | {
11 | @Getter
12 | private final Object value;
13 |
14 | public TableValue(Object value)
15 | {
16 | this.value = value;
17 | }
18 |
19 | @Override
20 | public boolean equals(Object o)
21 | {
22 | if (this == o) return true;
23 | if (o == null || getClass() != o.getClass()) return false;
24 | TableValue other = (TableValue) o;
25 |
26 | if (Objects.equals(value, other.value))
27 | {
28 | return true;
29 | }
30 |
31 | return value instanceof byte[] && other.value instanceof byte[] && Arrays.equals((byte[]) value, (byte[]) other.value);
32 | }
33 |
34 | @Override
35 | public int hashCode()
36 | {
37 | if (value instanceof byte[])
38 | {
39 | byte[] keyArray = (byte[]) value;
40 | int hash = keyArray.length;
41 | int position = 0;
42 | for (byte b : keyArray)
43 | {
44 | hash += b << (position % 4);
45 | position++;
46 | }
47 | return hash;
48 | }
49 |
50 | return Objects.hash(value);
51 | }
52 |
53 | @Override
54 | public String toString()
55 | {
56 | if(value == null)
57 | {
58 | return "(null)";
59 | }
60 |
61 | if(value instanceof byte[])
62 | {
63 | return "(" + DatatypeConverter.printHexBinary((byte[]) value) + ")";
64 | }
65 |
66 | return "(" + value + ")";
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/converters/date/DateConverter.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.converters.date;
2 |
3 | import com.github.molcikas.photon.converters.Converter;
4 | import com.github.molcikas.photon.converters.ConverterException;
5 |
6 | import java.time.*;
7 | import java.util.Date;
8 |
9 | public class DateConverter implements Converter
10 | {
11 | public Date convert(Object val) throws ConverterException
12 | {
13 | if (val == null)
14 | {
15 | return null;
16 | }
17 |
18 | if(Date.class.isAssignableFrom(val.getClass()))
19 | {
20 | return new Date(((Date)val).getTime());
21 | }
22 |
23 | if(ZonedDateTime.class.isAssignableFrom(val.getClass()))
24 | {
25 | return new Date(((ZonedDateTime)val).toEpochSecond() * 1000);
26 | }
27 |
28 | if(LocalDate.class.isAssignableFrom(val.getClass()))
29 | {
30 | LocalDate localDate = (LocalDate) val;
31 | return new Date(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toEpochSecond() * 1000);
32 | }
33 |
34 | if(LocalDateTime.class.isAssignableFrom(val.getClass()))
35 | {
36 | LocalDateTime localDateTime = (LocalDateTime) val;
37 | return new Date(localDateTime.atZone(ZoneId.systemDefault()).toEpochSecond() * 1000);
38 | }
39 |
40 | if(Instant.class.isAssignableFrom(val.getClass()))
41 | {
42 | return new Date(((Instant)val).getEpochSecond() * 1000);
43 | }
44 |
45 | if (val instanceof Number)
46 | {
47 | return new Date(((Number) val).longValue());
48 | }
49 |
50 | throw new ConverterException("Cannot convert type " + val.getClass().toString() + " to java.util.Date");
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/converters/StringConverter.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.converters;
2 |
3 | import java.io.IOException;
4 | import java.io.Reader;
5 | import java.sql.Clob;
6 | import java.sql.SQLException;
7 |
8 | /**
9 | * Used by sql2o to convert a value from the database into a {@link String}.
10 | */
11 | public class StringConverter implements Converter
12 | {
13 |
14 | public String convert(Object val) throws ConverterException {
15 | if (val == null){
16 | return null;
17 | }
18 |
19 | if (val instanceof Clob) {
20 | Clob clobVal = (Clob)val;
21 | try
22 | {
23 | try {
24 | return clobVal.getSubString(1, (int)clobVal.length());
25 | } catch (SQLException e) {
26 | throw new ConverterException("error converting clob to String", e);
27 | }
28 | } finally {
29 | try {
30 | clobVal.free();
31 | } catch (Throwable ignore) {
32 | //ignore
33 | }
34 | }
35 | }
36 |
37 | if(val instanceof Reader){
38 | Reader reader = (Reader) val;
39 | try {
40 | try {
41 | return IOUtils.toString(reader);
42 | } catch (IOException e) {
43 | throw new ConverterException("error converting reader to String", e);
44 | }
45 | } finally {
46 | try {
47 | reader.close();
48 | } catch (Throwable ignore) {
49 | // ignore
50 | }
51 | }
52 | }
53 |
54 | if(val instanceof Enum>) {
55 | return ((Enum) val).name();
56 | }
57 |
58 | return val.toString().trim();
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/converters/date/TimestampConverter.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.converters.date;
2 |
3 | import com.github.molcikas.photon.converters.Converter;
4 | import com.github.molcikas.photon.converters.ConverterException;
5 |
6 | import java.sql.Timestamp;
7 | import java.time.*;
8 | import java.util.Date;
9 |
10 | public class TimestampConverter implements Converter
11 | {
12 | public Timestamp convert(Object val) throws ConverterException
13 | {
14 | if (val == null)
15 | {
16 | return null;
17 | }
18 |
19 | if(Date.class.isAssignableFrom(val.getClass()))
20 | {
21 | return new Timestamp(((Date)val).getTime());
22 | }
23 |
24 | if(ZonedDateTime.class.isAssignableFrom(val.getClass()))
25 | {
26 | return new Timestamp(((ZonedDateTime)val).toEpochSecond() * 1000);
27 | }
28 |
29 | if(LocalDate.class.isAssignableFrom(val.getClass()))
30 | {
31 | LocalDate localDate = (LocalDate) val;
32 | return new Timestamp(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toEpochSecond() * 1000);
33 | }
34 |
35 | if(LocalDateTime.class.isAssignableFrom(val.getClass()))
36 | {
37 | LocalDateTime localDateTime = (LocalDateTime) val;
38 | return new Timestamp(localDateTime.atZone(ZoneId.systemDefault()).toEpochSecond() * 1000);
39 | }
40 |
41 | if(Instant.class.isAssignableFrom(val.getClass()))
42 | {
43 | return new Timestamp(((Instant)val).getEpochSecond() * 1000);
44 | }
45 |
46 | if (val instanceof Number)
47 | {
48 | return new Timestamp(((Number) val).longValue());
49 | }
50 |
51 | throw new ConverterException("Cannot convert type " + val.getClass().toString() + " to java.sql.Timestamp");
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/converters/number/NumberConverter.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.converters.number;
2 |
3 | import com.github.molcikas.photon.converters.Converter;
4 |
5 | /**
6 | * Base class for numeric converters.
7 | */
8 | public abstract class NumberConverter implements Converter
9 | {
10 | private boolean isPrimitive;
11 |
12 | public NumberConverter(boolean primitive) {
13 | isPrimitive = primitive;
14 | }
15 |
16 | public V convert(Object val) {
17 | if (val == null) {
18 | return isPrimitive ? convertNumberValue(0) : null;
19 | }
20 |
21 | // val.getClass().isPrimitive() is ALWAYS false
22 | // since boxing (i.e. Object val=(int)1;)
23 | // changes type from Integet.TYPE to Integer.class
24 | // learn 2 java :)
25 |
26 | else if (/*val.getClass().isPrimitive() || */val instanceof Number ) {
27 | return convertNumberValue((Number)val);
28 | }
29 | else if (val instanceof String) {
30 | String stringVal = ((String)val).trim();
31 | stringVal = stringVal.isEmpty() ? null : stringVal;
32 |
33 | if (stringVal == null) {
34 | return isPrimitive ? convertNumberValue(0) : null;
35 | }
36 |
37 | return convertStringValue(stringVal);
38 | }
39 | else if(val instanceof Enum>) {
40 | int enumVal = ((Enum) val).ordinal();
41 | return convertNumberValue(enumVal);
42 | }
43 | else {
44 | throw new IllegalArgumentException("Cannot convert type " + val.getClass().toString() + " to " + getTypeDescription());
45 | }
46 | }
47 |
48 | protected abstract V convertNumberValue(Number val);
49 |
50 | protected abstract V convertStringValue(String val);
51 |
52 | protected abstract String getTypeDescription();
53 | }
54 |
--------------------------------------------------------------------------------
/photon-perf-test/src/test/java/com/github/molcikas/photon/perf/photon/RecipeIngredient.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.perf.photon;
2 |
3 | import java.util.UUID;
4 |
5 | public class RecipeIngredient
6 | {
7 | private UUID recipeIngredientId;
8 |
9 | private boolean isRequired;
10 |
11 | private String quantity;
12 |
13 | private String quantityUnit;
14 |
15 | private String quantityDetail;
16 |
17 | private String name;
18 |
19 | private String preparation;
20 |
21 | private Integer orderBy;
22 |
23 | public UUID getRecipeIngredientId()
24 | {
25 | return recipeIngredientId;
26 | }
27 |
28 | public boolean isRequired()
29 | {
30 | return isRequired;
31 | }
32 |
33 | public String getQuantity()
34 | {
35 | return quantity;
36 | }
37 |
38 | public String getQuantityUnit()
39 | {
40 | return quantityUnit;
41 | }
42 |
43 | public String getQuantityDetail()
44 | {
45 | return quantityDetail;
46 | }
47 |
48 | public String getName()
49 | {
50 | return name;
51 | }
52 |
53 | public String getPreparation()
54 | {
55 | return preparation;
56 | }
57 |
58 | public Integer getOrderBy()
59 | {
60 | return orderBy;
61 | }
62 |
63 | public void setName(String name)
64 | {
65 | this.name = name;
66 | }
67 |
68 | private RecipeIngredient()
69 | {
70 | }
71 |
72 | public RecipeIngredient(UUID recipeIngredientId, boolean isRequired, String quantity, String quantityUnit, String quantityDetail, String name, String preparation, Integer orderBy)
73 | {
74 | this.recipeIngredientId = recipeIngredientId;
75 | this.isRequired = isRequired;
76 | this.quantity = quantity;
77 | this.quantityUnit = quantityUnit;
78 | this.quantityDetail = quantityDetail;
79 | this.name = name;
80 | this.preparation = preparation;
81 | this.orderBy = orderBy;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/sqlbuilders/SqlJoinClauseBuilderService.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.sqlbuilders;
2 |
3 | import com.github.molcikas.photon.blueprints.table.TableBlueprint;
4 |
5 | import java.util.*;
6 |
7 | public final class SqlJoinClauseBuilderService
8 | {
9 | public static void buildChildToParentJoinClauseSql(
10 | StringBuilder sqlBuilder,
11 | TableBlueprint tableBlueprint,
12 | boolean alwaysUseInnerJoins)
13 | {
14 | while(tableBlueprint.getParentTableBlueprint() != null)
15 | {
16 | sqlBuilder.append(String.format("%n%s [%s] ON [%s].[%s] = [%s].[%s]",
17 | alwaysUseInnerJoins ? "JOIN" : tableBlueprint.getJoinType().getJoinSql(),
18 | tableBlueprint.getParentTableBlueprint().getTableName(),
19 | tableBlueprint.getParentTableBlueprint().getTableName(),
20 | tableBlueprint.getParentTableBlueprint().getPrimaryKeyColumnName(),
21 | tableBlueprint.getTableName(),
22 | tableBlueprint.getForeignKeyToParentColumn().getColumnName()
23 | ));
24 | tableBlueprint = tableBlueprint.getParentTableBlueprint();
25 | }
26 | }
27 |
28 | public static void buildParentToEachChildJoinClauseSql(
29 | StringBuilder sqlBuilder,
30 | TableBlueprint parentTableBlueprint,
31 | List childTableBlueprints)
32 | {
33 | for(TableBlueprint childTableBlueprint : childTableBlueprints)
34 | {
35 | sqlBuilder.append(String.format("%n%s [%s] ON [%s].[%s] = [%s].[%s]",
36 | childTableBlueprint.getJoinType().getJoinSql(),
37 | childTableBlueprint.getTableName(),
38 | childTableBlueprint.getTableName(),
39 | childTableBlueprint.getForeignKeyToParentColumn().getColumnName(),
40 | parentTableBlueprint.getTableName(),
41 | parentTableBlueprint.getPrimaryKeyColumnName()
42 | ));
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/converters/ByteArrayConverter.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.converters;
2 |
3 | import java.io.IOException;
4 | import java.io.InputStream;
5 | import java.nio.ByteBuffer;
6 | import java.sql.Blob;
7 | import java.sql.SQLException;
8 | import java.util.UUID;
9 |
10 | public class ByteArrayConverter implements Converter
11 | {
12 |
13 | public byte[] convert(Object val) throws ConverterException {
14 | if (val == null) return null;
15 |
16 | if (val instanceof Blob) {
17 | Blob b = (Blob)val;
18 | InputStream stream=null;
19 | try {
20 | try {
21 | stream = b.getBinaryStream();
22 | return IOUtils.toByteArray(stream);
23 | } finally {
24 | if(stream!=null) {
25 | try {
26 | stream.close();
27 | } catch (Throwable ignore){
28 | // ignore stream.close errors
29 | }
30 | }
31 | try {
32 | b.free();
33 | } catch (Throwable ignore){
34 | // ignore blob.free errors
35 | }
36 | }
37 | } catch (SQLException e) {
38 | throw new ConverterException("Error converting Blob to byte[]", e);
39 | } catch (IOException e) {
40 | throw new ConverterException("Error converting Blob to byte[]", e);
41 | }
42 | }
43 |
44 | if (val instanceof byte[]){
45 | return (byte[])val;
46 | }
47 |
48 | if(val instanceof UUID)
49 | {
50 | UUID uuid = (UUID) val;
51 | ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
52 | bb.putLong(uuid.getMostSignificantBits());
53 | bb.putLong(uuid.getLeastSignificantBits());
54 | return bb.array();
55 | }
56 |
57 | throw new RuntimeException("could not convert " + val.getClass().getName() + " to byte[]");
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/photon-perf-test/src/test/java/com/github/molcikas/photon/perf/ReflectionTest.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.perf;
2 |
3 | import org.apache.commons.lang3.time.StopWatch;
4 | import org.junit.Test;
5 | import com.github.molcikas.photon.perf.photon.Recipe;
6 |
7 | import java.lang.reflect.Field;
8 |
9 | public class ReflectionTest
10 | {
11 | private final int TEST_RUNS = 10;
12 | private final int TEST_ITERATIONS = 10000000;
13 |
14 | @Test
15 | public void testReflectionNoCache()
16 | {
17 | try
18 | {
19 | for(int t = 0; t < TEST_RUNS; t++)
20 | {
21 | Recipe recipe = new Recipe();
22 |
23 | StopWatch stopWatch = new StopWatch();
24 | stopWatch.start();
25 | for (int i = 0; i < TEST_ITERATIONS; i++)
26 | {
27 | Field field = Recipe.class.getDeclaredField("name");
28 | field.setAccessible(true);
29 | field.set(recipe, "myname" + i);
30 | }
31 | long finishTime = stopWatch.getNanoTime();
32 | System.out.println(String.format("Finished after %s ms with avg set taking %s ns.", finishTime / 1000000, finishTime / TEST_ITERATIONS));
33 | }
34 | }
35 | catch(Exception ex)
36 | {
37 | throw new RuntimeException(ex);
38 | }
39 | }
40 |
41 | @Test
42 | public void testReflectionWithCache()
43 | {
44 | try
45 | {
46 | for(int t = 0; t < TEST_RUNS; t++)
47 | {
48 | Recipe recipe = new Recipe();
49 | Field field = Recipe.class.getDeclaredField("name");
50 | field.setAccessible(true);
51 |
52 | StopWatch stopWatch = new StopWatch();
53 | stopWatch.start();
54 | for (int i = 0; i < TEST_ITERATIONS; i++)
55 | {
56 | field.set(recipe, "myname" + i);
57 | }
58 | long finishTime = stopWatch.getNanoTime();
59 | System.out.println(String.format("Finished after %s ms with avg set taking %s ns.", finishTime / 1000000, finishTime / TEST_ITERATIONS));
60 | }
61 | }
62 | catch(Exception ex)
63 | {
64 | throw new RuntimeException(ex);
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/datasource/ExistingConnectionDataSource.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.datasource;
2 |
3 | import javax.sql.DataSource;
4 | import java.io.PrintWriter;
5 | import java.sql.Connection;
6 | import java.sql.DriverManager;
7 | import java.sql.SQLException;
8 | import java.sql.SQLFeatureNotSupportedException;
9 |
10 | /**
11 | * A DataSource that always returns an existing provided connection. This is useful if Photon is being used alongside
12 | * another ORM. If photon is strictly used for queries and not updates, wrap the connection with ReadOnlyConnection so
13 | * that Photon does not ever modify the state of the connection.
14 | */
15 | public class ExistingConnectionDataSource implements DataSource
16 | {
17 | private Connection connection;
18 |
19 | public ExistingConnectionDataSource()
20 | {
21 | }
22 |
23 | public ExistingConnectionDataSource(Connection connection)
24 | {
25 | this.connection = connection;
26 | }
27 |
28 | public Connection getConnection() throws SQLException
29 | {
30 | return connection;
31 | }
32 |
33 | public Connection getConnection(String username, String password) throws SQLException
34 | {
35 | return connection;
36 | }
37 |
38 | public PrintWriter getLogWriter() throws SQLException
39 | {
40 | return DriverManager.getLogWriter();
41 | }
42 |
43 | public void setLogWriter(PrintWriter printWriter) throws SQLException
44 | {
45 | DriverManager.setLogWriter(printWriter);
46 | }
47 |
48 | public void setLoginTimeout(int i) throws SQLException
49 | {
50 | DriverManager.setLoginTimeout(i);
51 | }
52 |
53 | public int getLoginTimeout() throws SQLException
54 | {
55 | return DriverManager.getLoginTimeout();
56 | }
57 |
58 | public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException
59 | {
60 | throw new SQLFeatureNotSupportedException();
61 | }
62 |
63 | public T unwrap(Class tClass) throws SQLException
64 | {
65 | throw new SQLFeatureNotSupportedException();
66 | }
67 |
68 | public boolean isWrapperFor(Class> aClass) throws SQLException
69 | {
70 | return false;
71 | }
72 |
73 | public void setConnection(Connection connection)
74 | {
75 | this.connection = connection;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/photon-perf-test/src/test/java/com/github/molcikas/photon/perf/hibernate/RecipeEntity.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.perf.hibernate;
2 |
3 | import javax.persistence.*;
4 | import java.util.Set;
5 | import java.util.UUID;
6 |
7 | @Entity
8 | @Table(name = "recipe")
9 | public class RecipeEntity
10 | {
11 | @Id
12 | @Column(columnDefinition = "BINARY(16)", length = 16)
13 | public UUID recipeId;
14 |
15 | @Column
16 | public String name;
17 |
18 | @Column
19 | public String description;
20 |
21 | @Column
22 | public int prepTime;
23 |
24 | @Column
25 | public int cookTime;
26 |
27 | @Column
28 | public int servings;
29 |
30 | @Column
31 | public boolean isVegetarian;
32 |
33 | @Column
34 | public boolean isVegan;
35 |
36 | @Column
37 | public boolean isPublished;
38 |
39 | @Column
40 | public String credit;
41 |
42 | @OneToMany(mappedBy = "recipeId", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
43 | @OrderBy("name ASC")
44 | public Set ingredients;
45 |
46 | @OneToMany(mappedBy = "recipeId", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
47 | @OrderBy("stepNumber ASC")
48 | public Set instructions;
49 |
50 | public Set getIngredients()
51 | {
52 | return ingredients;
53 | }
54 |
55 | public Set getInstructions()
56 | {
57 | return instructions;
58 | }
59 |
60 | public void setName(String name)
61 | {
62 | this.name = name;
63 | }
64 |
65 | protected RecipeEntity()
66 | {
67 | }
68 |
69 | public RecipeEntity(UUID recipeId, String name, String description, int prepTime, int cookTime, int servings, boolean isVegetarian, boolean isVegan, boolean isPublished, String credit, Set ingredients, Set instructions)
70 | {
71 | this.recipeId = recipeId;
72 | this.name = name;
73 | this.description = description;
74 | this.prepTime = prepTime;
75 | this.cookTime = cookTime;
76 | this.servings = servings;
77 | this.isVegetarian = isVegetarian;
78 | this.isVegan = isVegan;
79 | this.isPublished = isPublished;
80 | this.credit = credit;
81 | this.ingredients = ingredients;
82 | this.instructions = instructions;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/blueprints/table/JoinedTableBlueprintBuilder.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.blueprints.table;
2 |
3 | import com.github.molcikas.photon.blueprints.entity.EntityBlueprintBuilder;
4 | import com.github.molcikas.photon.blueprints.entity.FieldBlueprint;
5 | import com.github.molcikas.photon.exceptions.PhotonException;
6 | import com.github.molcikas.photon.options.PhotonOptions;
7 | import org.apache.commons.lang3.StringUtils;
8 |
9 | import java.util.List;
10 |
11 | public class JoinedTableBlueprintBuilder extends TableBlueprintBuilder
12 | {
13 | public JoinedTableBlueprintBuilder(
14 | Class entityClass,
15 | String tableName,
16 | JoinType joinType,
17 | EntityBlueprintBuilder entityBlueprintBuilder,
18 | PhotonOptions photonOptions)
19 | {
20 | super(entityClass, tableName, joinType, entityBlueprintBuilder, photonOptions);
21 | }
22 |
23 | @Override
24 | public TableBlueprintBuilder withParentTable(String parentTableName)
25 | {
26 | throw new PhotonException("Cannot call withParentTable() on a joined table builder.");
27 | }
28 |
29 | @Override
30 | public TableBlueprintBuilder withForeignKeyToParent(String foreignKeyToParent)
31 | {
32 | throw new PhotonException("Cannot call withForeignKeyToParent() on a joined table builder.");
33 | }
34 |
35 | @Override
36 | public TableBlueprintBuilder withForeignKeyToParent(String foreignKeyToParent, ColumnDataType columnDataType)
37 | {
38 | throw new PhotonException("Cannot call withForeignKeyToParent() on a joined table builder.");
39 | }
40 |
41 | @Override
42 | public TableBlueprint build(
43 | boolean isSimpleEntity,
44 | List fields,
45 | List parentEntityTables,
46 | TableBlueprint mainTableBlueprint,
47 | List joinedTableBuilders)
48 | {
49 | if(StringUtils.isBlank(idFieldName))
50 | {
51 | idFieldName = determineDefaultIdFieldName(fields);
52 | }
53 |
54 | this.parentTableName = parentEntityTables.get(0);
55 | this.foreignKeyToParent = idFieldName;
56 |
57 | if(isSimpleEntity)
58 | {
59 | throw new PhotonException("Simple entity with main table '%s' cannot have joined tables.", parentTableName);
60 | }
61 |
62 | return super.build(false, fields, parentEntityTables, mainTableBlueprint, joinedTableBuilders);
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/h2/fieldtest/FieldTestDbSetup.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.h2.fieldtest;
2 |
3 | import com.github.molcikas.photon.Photon;
4 | import com.github.molcikas.photon.PhotonTransaction;
5 | import com.github.molcikas.photon.blueprints.table.ColumnDataType;
6 | import com.github.molcikas.photon.tests.unit.entities.fieldtest.FieldTest;
7 | import com.github.molcikas.photon.tests.unit.h2.H2TestUtil;
8 |
9 | public class FieldTestDbSetup
10 | {
11 | public static Photon setupDatabase()
12 | {
13 | Photon photon = new Photon(H2TestUtil.h2Url, H2TestUtil.h2User, H2TestUtil.h2Password);
14 |
15 | try(PhotonTransaction transaction = photon.beginTransaction())
16 | {
17 | transaction.query("DROP TABLE IF EXISTS `fieldtest`").executeUpdate();
18 | transaction.query("CREATE TABLE `fieldtest` (\n" +
19 | " `id` int(11) NOT NULL AUTO_INCREMENT,\n" +
20 | " `date` DATETIME,\n" +
21 | " `zonedDateTime` DATETIME,\n" +
22 | " `localDate` DATETIME,\n" +
23 | " `localDateTime` DATETIME,\n" +
24 | " `instant` DATETIME,\n" +
25 | " `testEnumNumber` int(11),\n" +
26 | " `testEnumString` VARCHAR(255),\n" +
27 | " PRIMARY KEY (`id`)\n" +
28 | ")").executeUpdate();
29 | transaction.query("insert into `fieldtest` (`id`, `date`, `zonedDateTime`, `localDate`, `localDateTime`, `instant`, `testEnumNumber`, `testEnumString`) " +
30 | "values (1, PARSEDATETIME('2017-03-19 09-28-17', 'yyyy-MM-dd HH-mm-ss'), PARSEDATETIME('2017-03-19 09-28-18', 'yyyy-MM-dd HH-mm-ss'), PARSEDATETIME('2017-03-19 09-28-19', 'yyyy-MM-dd HH-mm-ss'), PARSEDATETIME('2017-03-19 09-28-20', 'yyyy-MM-dd HH-mm-ss'), PARSEDATETIME('2017-03-19 09-28-21', 'yyyy-MM-dd HH-mm-ss'), 0, 'VALUE_ONE')").executeUpdate();
31 | transaction.query("insert into `fieldtest` (`id`, `date`, `zonedDateTime`, `localDate`, `localDateTime`, `instant`, `testEnumNumber`, `testEnumString`) " +
32 | "values (2, NULL, NULL, NULL, NULL, NULL, NULL, NULL)").executeUpdate();
33 | transaction.commit();
34 | }
35 |
36 | return photon;
37 | }
38 |
39 | public static void registerAggregate(Photon photon)
40 | {
41 | photon.registerAggregate(FieldTest.class)
42 | .withId("id")
43 | .withDatabaseColumn("testEnumString", ColumnDataType.VARCHAR)
44 | .register();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/entities/recipe/RecipeIngredient.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.entities.recipe;
2 |
3 | import org.apache.commons.lang3.math.Fraction;
4 |
5 | import java.util.Objects;
6 |
7 | public class RecipeIngredient
8 | {
9 | private boolean isRequired;
10 |
11 | private Fraction quantity;
12 |
13 | private String quantityUnit;
14 |
15 | private String quantityDetail;
16 |
17 | private String name;
18 |
19 | private String preparation;
20 |
21 | private Integer orderBy;
22 |
23 | public boolean isRequired()
24 | {
25 | return isRequired;
26 | }
27 |
28 | public Fraction getQuantity()
29 | {
30 | return quantity;
31 | }
32 |
33 | public String getQuantityUnit()
34 | {
35 | return quantityUnit;
36 | }
37 |
38 | public String getQuantityDetail()
39 | {
40 | return quantityDetail;
41 | }
42 |
43 | public String getName()
44 | {
45 | return name;
46 | }
47 |
48 | public String getPreparation()
49 | {
50 | return preparation;
51 | }
52 |
53 | public Integer getOrderBy()
54 | {
55 | return orderBy;
56 | }
57 |
58 | private RecipeIngredient()
59 | {
60 | }
61 |
62 | public RecipeIngredient(boolean isRequired, Fraction quantity, String quantityUnit, String quantityDetail, String name, String preparation, Integer orderBy)
63 | {
64 | this.isRequired = isRequired;
65 | this.quantity = quantity;
66 | this.quantityUnit = quantityUnit;
67 | this.quantityDetail = quantityDetail;
68 | this.name = name;
69 | this.preparation = preparation;
70 | this.orderBy = orderBy;
71 | }
72 |
73 | @Override
74 | public boolean equals(Object o)
75 | {
76 | if (this == o) return true;
77 | if (o == null || getClass() != o.getClass()) return false;
78 | RecipeIngredient that = (RecipeIngredient) o;
79 | return isRequired == that.isRequired &&
80 | Objects.equals(quantity, that.quantity) &&
81 | Objects.equals(quantityUnit, that.quantityUnit) &&
82 | Objects.equals(quantityDetail, that.quantityDetail) &&
83 | Objects.equals(name, that.name) &&
84 | Objects.equals(preparation, that.preparation) &&
85 | Objects.equals(orderBy, that.orderBy);
86 | }
87 |
88 | @Override
89 | public int hashCode()
90 | {
91 | return Objects.hash(isRequired, quantity, quantityUnit, quantityDetail, name, preparation, orderBy);
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/h2/recipe/RecipeDeleteTests.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.h2.recipe;
2 |
3 | import com.github.molcikas.photon.blueprints.table.ColumnDataType;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 | import com.github.molcikas.photon.Photon;
7 | import com.github.molcikas.photon.PhotonTransaction;
8 | import com.github.molcikas.photon.tests.unit.entities.recipe.Recipe;
9 | import com.github.molcikas.photon.tests.unit.entities.recipe.RecipeIngredient;
10 | import com.github.molcikas.photon.tests.unit.entities.recipe.RecipeInstruction;
11 |
12 | import java.util.UUID;
13 |
14 | import static org.junit.Assert.assertNull;
15 |
16 | public class RecipeDeleteTests
17 | {
18 | private Photon photon;
19 |
20 | @Before
21 | public void setupDatabase()
22 | {
23 | photon = RecipeDbSetup.setupDatabase();
24 | }
25 |
26 | @Test
27 | public void aggregate_delete_existingRecipe_deletesRecipe()
28 | {
29 | registerRecipeAggregate();
30 |
31 | try (PhotonTransaction transaction = photon.beginTransaction())
32 | {
33 | Recipe recipe1 = transaction.query(Recipe.class).fetchById(UUID.fromString("3E04169A-A9B6-11E6-AB83-0A0027000010"));
34 |
35 | transaction.delete(recipe1);
36 | transaction.commit();
37 | }
38 |
39 | try (PhotonTransaction transaction = photon.beginTransaction())
40 | {
41 | Recipe recipe1 = transaction.query(Recipe.class).fetchById(UUID.fromString("3E04169A-A9B6-11E6-AB83-0A0027000010"));
42 |
43 | assertNull(recipe1);
44 | transaction.commit();
45 | }
46 | }
47 |
48 | private void registerRecipeAggregate()
49 | {
50 | registerRecipeAggregate("recipeingredient.orderBy");
51 | }
52 |
53 | private void registerRecipeAggregate(String orderBySql)
54 | {
55 | photon.registerAggregate(Recipe.class)
56 | .withId("recipeId")
57 | .withChild("instructions", RecipeInstruction.class)
58 | .withId("recipeInstructionId", ColumnDataType.BINARY)
59 | .withForeignKeyToParent("recipeId")
60 | .withDatabaseColumn("recipeId", ColumnDataType.BINARY)
61 | .withOrderBySql("stepNumber")
62 | .addAsChild()
63 | .withChild("ingredients", RecipeIngredient.class)
64 | .withId("recipeIngredientId")
65 | .withForeignKeyToParent("recipeId")
66 | .withDatabaseColumn("recipeIngredientId", ColumnDataType.BINARY)
67 | .withDatabaseColumn("recipeId", ColumnDataType.BINARY)
68 | .withOrderBySql(orderBySql)
69 | .addAsChild()
70 | .register();
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/h2/mytable/MyTableDbSetup.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.h2.mytable;
2 |
3 | import com.github.molcikas.photon.Photon;
4 | import com.github.molcikas.photon.PhotonTransaction;
5 | import com.github.molcikas.photon.tests.unit.h2.H2TestUtil;
6 |
7 | public class MyTableDbSetup
8 | {
9 | public static Photon setupDatabase()
10 | {
11 | Photon photon = new Photon(H2TestUtil.h2Url, H2TestUtil.h2User, H2TestUtil.h2Password);
12 | return setupDatabase(photon);
13 | }
14 |
15 | public static Photon setupDatabase(Photon photon)
16 | {
17 | try(PhotonTransaction transaction = photon.beginTransaction())
18 | {
19 | transaction.query("DROP TABLE IF EXISTS `mytable`").executeUpdate();
20 | transaction.query("CREATE TABLE `mytable` (\n" +
21 | " `id` int(11) NOT NULL AUTO_INCREMENT,\n" +
22 | " `myvalue` varchar(255) DEFAULT 'oops',\n" +
23 | " `version` int(11) NOT NULL DEFAULT 1,\n" +
24 | " PRIMARY KEY (`id`)\n" +
25 | ")").executeUpdate();
26 | transaction.query("insert into `mytable` (`id`, `myvalue`) values (1, 'my1dbvalue')").executeUpdate();
27 | transaction.query("insert into `mytable` (`id`, `myvalue`) values (2, 'my2dbvalue')").executeUpdate();
28 | transaction.query("insert into `mytable` (`id`, `myvalue`) values (3, 'my3dbvalue')").executeUpdate();
29 | transaction.query("insert into `mytable` (`id`, `myvalue`) values (4, 'my4dbvalue')").executeUpdate();
30 | transaction.query("insert into `mytable` (`id`, `myvalue`) values (5, 'my5dbvalue')").executeUpdate();
31 | transaction.query("insert into `mytable` (`id`, `myvalue`) values (6, NULL)").executeUpdate();
32 |
33 | transaction.query("DROP TABLE IF EXISTS `myothertable`").executeUpdate();
34 | transaction.query("CREATE TABLE `myothertable` (\n" +
35 | " `id` int(11) NOT NULL,\n" +
36 | " `myothervalue` varchar(255) DEFAULT 'oops',\n" +
37 | " PRIMARY KEY (`id`),\n" +
38 | " CONSTRAINT `MyOtherTable_MyTable` FOREIGN KEY (`id`) REFERENCES `mytable` (`id`)\n" +
39 | ")").executeUpdate();
40 | transaction.query("insert into `myothertable` (`id`, `myothervalue`) values (3, 'my3otherdbvalue')").executeUpdate();
41 | transaction.query("insert into `myothertable` (`id`, `myothervalue`) values (4, 'my4otherdbvalue')").executeUpdate();
42 | transaction.query("insert into `myothertable` (`id`, `myothervalue`) values (5, 'my5otherdbvalue')").executeUpdate();
43 |
44 | transaction.commit();
45 | }
46 |
47 | return photon;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/datasource/GenericDataSource.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.datasource;
2 |
3 | import javax.sql.DataSource;
4 | import java.io.PrintWriter;
5 | import java.sql.Connection;
6 | import java.sql.DriverManager;
7 | import java.sql.SQLException;
8 | import java.sql.SQLFeatureNotSupportedException;
9 | import java.util.Properties;
10 |
11 |
12 | public class GenericDataSource implements DataSource {
13 |
14 | private final String url;
15 | private final Properties properties;
16 |
17 | public GenericDataSource(String url, String user, String password) {
18 |
19 | if (!url.startsWith("jdbc")){
20 | url = "jdbc:" + url;
21 | }
22 |
23 | this.url = url;
24 | this.properties = new Properties();
25 | set(properties,user,password);
26 | }
27 |
28 | private void set(Properties info, String user, String password) {
29 | if (user != null) {
30 | info.put("user", user);
31 | }
32 | if (password != null) {
33 | info.put("password", password);
34 | }
35 | }
36 |
37 | public GenericDataSource(String url, Properties properties) {
38 |
39 | if (!url.startsWith("jdbc")){
40 | url = "jdbc:" + url;
41 | }
42 |
43 | this.url = url;
44 | this.properties = properties;
45 | }
46 |
47 | public String getUrl() {
48 | return url;
49 | }
50 |
51 | public String getUser() {
52 | return properties.getProperty("user");
53 | }
54 |
55 | public String getPassword() {
56 | return properties.getProperty("password");
57 | }
58 |
59 | public Connection getConnection() throws SQLException {
60 | return DriverManager.getConnection(this.getUrl(), properties);
61 | }
62 |
63 | public Connection getConnection(String username, String password) throws SQLException {
64 | Properties info = new Properties(this.properties);
65 | set(info,username,password);
66 | return DriverManager.getConnection(this.getUrl(), info);
67 | }
68 |
69 | public PrintWriter getLogWriter() throws SQLException {
70 | return DriverManager.getLogWriter();
71 | }
72 |
73 | public void setLogWriter(PrintWriter printWriter) throws SQLException {
74 | DriverManager.setLogWriter(printWriter);
75 | }
76 |
77 | public void setLoginTimeout(int i) throws SQLException {
78 | DriverManager.setLoginTimeout(i);
79 | }
80 |
81 | public int getLoginTimeout() throws SQLException {
82 | return DriverManager.getLoginTimeout();
83 | }
84 |
85 | public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException {
86 | throw new SQLFeatureNotSupportedException();
87 | }
88 |
89 | public T unwrap(Class tClass) throws SQLException {
90 | throw new SQLFeatureNotSupportedException();
91 | }
92 |
93 | public boolean isWrapperFor(Class> aClass) throws SQLException {
94 | return false;
95 | }
96 | }
97 |
98 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/query/PhotonAggregateFilterQuery.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.query;
2 |
3 | import com.github.molcikas.photon.Photon;
4 | import com.github.molcikas.photon.sqlbuilders.SqlBuilderApplyOptionsService;
5 | import org.apache.commons.lang3.StringUtils;
6 | import com.github.molcikas.photon.blueprints.AggregateBlueprint;
7 | import com.github.molcikas.photon.exceptions.PhotonException;
8 |
9 | import java.sql.Connection;
10 | import java.util.List;
11 |
12 | public class PhotonAggregateFilterQuery
13 | {
14 | private final PhotonAggregateQuery photonAggregateQuery;
15 | private final PhotonQuery photonQuery;
16 | private final boolean isQueryIdsOnly;
17 |
18 | PhotonAggregateFilterQuery(
19 | AggregateBlueprint aggregateBlueprint,
20 | String selectSql,
21 | boolean isWhereClauseOnly,
22 | Connection connection,
23 | Photon photon,
24 | PhotonAggregateQuery photonAggregateQuery)
25 | {
26 | if(StringUtils.isBlank(selectSql))
27 | {
28 | throw new PhotonException("Photon aggregate SELECT SQL cannot be blank.");
29 | }
30 |
31 | this.photonAggregateQuery = photonAggregateQuery;
32 | this.isQueryIdsOnly = !isWhereClauseOnly;
33 |
34 | if(isWhereClauseOnly)
35 | {
36 | selectSql = String.format(aggregateBlueprint.getAggregateRootEntityBlueprint().getTableBlueprint().getSelectWhereSql(), selectSql);
37 | selectSql = SqlBuilderApplyOptionsService.applyPhotonOptionsToSql(selectSql, photon.getOptions());
38 | }
39 |
40 | this.photonQuery = new PhotonQuery(selectSql, false, connection, photon);
41 | }
42 |
43 | /**
44 | * Adds a parameter to the current query.
45 | *
46 | * @param parameter - The name of the parameter. Must match the name used in the SQL text for this query.
47 | * @param value - The parameter value
48 | * @return - The photon query (for chaining)
49 | */
50 | public PhotonAggregateFilterQuery addParameter(String parameter, Object value)
51 | {
52 | photonQuery.addParameter(parameter, value);
53 | return this;
54 | }
55 |
56 | /**
57 | * Execute the query and use the first id in the result set to query for the aggregate.
58 | *
59 | * @return - The aggregate instance
60 | */
61 | public T fetch()
62 | {
63 | if(isQueryIdsOnly)
64 | {
65 | return photonAggregateQuery.fetchByIdsQuery(photonQuery);
66 | }
67 | else
68 | {
69 | return photonAggregateQuery.fetchByQuery(photonQuery);
70 | }
71 | }
72 |
73 | /**
74 | * Execute the query and use the ids in the result set to query for aggregates.
75 | *
76 | * @return - The aggregate instances
77 | */
78 | public List fetchList()
79 | {
80 | if(isQueryIdsOnly)
81 | {
82 | return photonAggregateQuery.fetchListByIdsQuery(photonQuery);
83 | }
84 | else
85 | {
86 | return photonAggregateQuery.fetchListByQuery(photonQuery);
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/docs/AdvancedLoadingAndSaving.md:
--------------------------------------------------------------------------------
1 | # Advanced Loading and Saving
2 |
3 | ## Change Tracking
4 |
5 | By default, photon tracks the state of each entity in each aggregate and only saves changes when `save()` is called. There is no concept of "flushing" changes. All queries (including inserts and updates) are always executed immediately.
6 |
7 | If a queried aggregate will not be updated during a transaction, you can disable change tracking to reduce memory usage and improve performance.
8 |
9 | ```java
10 | MyTable myTable = transaction
11 | .query(MyTable.class)
12 | .noTracking()
13 | .fetchById(2);
14 | ```
15 |
16 | If you have a long-running transaction with aggregates falling out of scope and being garbage collected, you can clear the tracked state to reduce memory usage.
17 |
18 | ```java
19 | try(PhotonTransaction transaction = photon.beginTransaction())
20 | {
21 | Recipe recipe = transaction
22 | .query(Recipe.class)
23 | .fetchById(UUID.fromString("3e038307-a9b6-11e6-ab83-0a0027000010"));
24 |
25 | // ... later, when the recipe entity falls out of scope ...
26 |
27 | transaction.untrack(recipe);
28 | }
29 | ```
30 |
31 | Aggregates do not need to be tracked by Photon in order for them to save correctly. If an untracked aggregate is saved, the entire aggregate will be re-saved (as if the entire aggregate had changed).
32 |
33 | ## Lazy Loading
34 |
35 | Aggregates are loaded as whole units. Photon does not support "[lazy loading](http://www.mehdi-khalili.com/orm-anti-patterns-part-3-lazy-loading)" because an aggregate should not be used to control the loading of other aggregates. All entities in an aggregate are eager loaded. Therefore, it is important to keep your aggregates small. See [Effective Aggregate Design](https://vaughnvernon.co/?p=838) for more information on these design concepts.
36 |
37 | ## Partial Aggregate Loading and Saving
38 |
39 | While not recommended for most circumstances, Photon does support loading and saving partial aggregates. This can be useful if a simple update is needed and the overhead of loading and re-saving unmodified child entities would cause performance issues.
40 |
41 | ```java
42 | try(PhotonTransaction transaction = photon.beginTransaction())
43 | {
44 | Recipe recipe = transaction
45 | .query(Recipe.class)
46 | .exclude("ingredients", "instructions") // Do not load the ingredient and instruction lists
47 | .fetchById(2);
48 |
49 | recipe.renameTo("Spaghetti and Meatballs");
50 |
51 | // Do not save the ingredient and instruction lists since we did not load them, otherwise this
52 | // recipe would lose all of its ingredients and instructions.
53 | transaction.saveWithExcludedFields(recipe, "ingredients", "instructions");
54 |
55 | transaction.commit();
56 | }
57 | ```
58 |
59 | It can also be useful for creating queries that only retrieve portions of an aggregate (although creating view models is preferred).
60 |
61 | ```java
62 | try(PhotonTransaction transaction = photon.beginTransaction())
63 | {
64 | Recipe recipe = transaction
65 | .query(Recipe.class)
66 | .exclude("ingredients") // Do not load the ingredient list
67 | .fetchById(2);
68 |
69 | return recipe;
70 | ```
71 |
--------------------------------------------------------------------------------
/photon-perf-test/src/test/java/com/github/molcikas/photon/perf/photon/Recipe.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.perf.photon;
2 |
3 | import java.util.List;
4 | import java.util.UUID;
5 |
6 | public class Recipe
7 | {
8 | private UUID recipeId;
9 |
10 | private String name;
11 |
12 | private String description;
13 |
14 | private int prepTime;
15 |
16 | private int cookTime;
17 |
18 | private int servings;
19 |
20 | private boolean isVegetarian;
21 |
22 | private boolean isVegan;
23 |
24 | private boolean isPublished;
25 |
26 | private String credit;
27 |
28 | private List ingredients;
29 |
30 | private List instructions;
31 |
32 | public UUID getRecipeId()
33 | {
34 | return recipeId;
35 | }
36 |
37 | public String getName()
38 | {
39 | return name;
40 | }
41 |
42 | public String getDescription()
43 | {
44 | return description;
45 | }
46 |
47 | public int getPrepTime()
48 | {
49 | return prepTime;
50 | }
51 |
52 | public int getCookTime()
53 | {
54 | return cookTime;
55 | }
56 |
57 | public int getServings()
58 | {
59 | return servings;
60 | }
61 |
62 | public boolean isVegetarian()
63 | {
64 | return isVegetarian;
65 | }
66 |
67 | public boolean isVegan()
68 | {
69 | return isVegan;
70 | }
71 |
72 | public boolean isPublished()
73 | {
74 | return isPublished;
75 | }
76 |
77 | public String getCredit()
78 | {
79 | return credit;
80 | }
81 |
82 | public List getIngredients()
83 | {
84 | return ingredients;
85 | }
86 |
87 | public List getInstructions()
88 | {
89 | return instructions;
90 | }
91 |
92 | // Anemic setters are strongly discouraged in aggregates, but we need these for testing.
93 |
94 | public void setInstructions(List instructions)
95 | {
96 | this.instructions = instructions;
97 | }
98 |
99 | public void setName(String name)
100 | {
101 | this.name = name;
102 | }
103 |
104 | public void setPrepTime(int prepTime)
105 | {
106 | this.prepTime = prepTime;
107 | }
108 |
109 | public Recipe()
110 | {
111 | }
112 |
113 | public Recipe(UUID recipeId, String name, String description, int prepTime, int cookTime, int servings, boolean isVegetarian, boolean isVegan, boolean isPublished, String credit, List ingredients, List instructions)
114 | {
115 | this.recipeId = recipeId;
116 | this.name = name;
117 | this.description = description;
118 | this.prepTime = prepTime;
119 | this.cookTime = cookTime;
120 | this.servings = servings;
121 | this.isVegetarian = isVegetarian;
122 | this.isVegan = isVegan;
123 | this.isPublished = isPublished;
124 | this.credit = credit;
125 | this.ingredients = ingredients;
126 | this.instructions = instructions;
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/entities/fieldtest/FieldTest.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.entities.fieldtest;
2 |
3 | import java.time.Instant;
4 | import java.time.LocalDate;
5 | import java.time.LocalDateTime;
6 | import java.time.ZonedDateTime;
7 | import java.util.Date;
8 |
9 | public class FieldTest
10 | {
11 | private int id;
12 | private Date date;
13 | private ZonedDateTime zonedDateTime;
14 | private LocalDate localDate;
15 | private LocalDateTime localDateTime;
16 | private Instant instant;
17 | private TestEnum testEnumNumber;
18 | private TestEnum testEnumString;
19 |
20 | public int getId()
21 | {
22 | return id;
23 | }
24 |
25 | public void setId(int id)
26 | {
27 | this.id = id;
28 | }
29 |
30 | public Date getDate()
31 | {
32 | return date;
33 | }
34 |
35 | public void setDate(Date date)
36 | {
37 | this.date = date;
38 | }
39 |
40 | public ZonedDateTime getZonedDateTime()
41 | {
42 | return zonedDateTime;
43 | }
44 |
45 | public void setZonedDateTime(ZonedDateTime zonedDateTime)
46 | {
47 | this.zonedDateTime = zonedDateTime;
48 | }
49 |
50 | public LocalDate getLocalDate()
51 | {
52 | return localDate;
53 | }
54 |
55 | public void setLocalDate(LocalDate localDate)
56 | {
57 | this.localDate = localDate;
58 | }
59 |
60 | public TestEnum getTestEnumNumber()
61 | {
62 | return testEnumNumber;
63 | }
64 |
65 | public void setTestEnumNumber(TestEnum testEnumNumber)
66 | {
67 | this.testEnumNumber = testEnumNumber;
68 | }
69 |
70 | public TestEnum getTestEnumString()
71 | {
72 | return testEnumString;
73 | }
74 |
75 | public void setTestEnumString(TestEnum testEnumString)
76 | {
77 | this.testEnumString = testEnumString;
78 | }
79 |
80 | public LocalDateTime getLocalDateTime()
81 | {
82 | return localDateTime;
83 | }
84 |
85 | public void setLocalDateTime(LocalDateTime localDateTime)
86 | {
87 | this.localDateTime = localDateTime;
88 | }
89 |
90 | public Instant getInstant()
91 | {
92 | return instant;
93 | }
94 |
95 | public void setInstant(Instant instant)
96 | {
97 | this.instant = instant;
98 | }
99 |
100 | public FieldTest()
101 | {
102 | }
103 |
104 | public FieldTest(int id,
105 | Date date,
106 | ZonedDateTime zonedDateTime,
107 | LocalDate localDate,
108 | LocalDateTime localDateTime,
109 | Instant instant,
110 | TestEnum testEnumNumber,
111 | TestEnum testEnumString)
112 | {
113 | this.id = id;
114 | this.date = date;
115 | this.zonedDateTime = zonedDateTime;
116 | this.localDate = localDate;
117 | this.localDateTime = localDateTime;
118 | this.instant = instant;
119 | this.testEnumNumber = testEnumNumber;
120 | this.testEnumString = testEnumString;
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/blueprints/entity/FlattenedCollectionBlueprint.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.blueprints.entity;
2 |
3 | import com.github.molcikas.photon.blueprints.table.ColumnDataType;
4 | import lombok.Getter;
5 | import org.apache.commons.lang3.StringUtils;
6 | import com.github.molcikas.photon.exceptions.PhotonException;
7 |
8 | import java.util.Arrays;
9 | import java.util.List;
10 |
11 | public class FlattenedCollectionBlueprint
12 | {
13 | @Getter
14 | private final Class fieldClass;
15 |
16 | @Getter
17 | private final String tableName;
18 |
19 | @Getter
20 | private final String foreignKeyToParent;
21 |
22 | @Getter
23 | private final String foreignKeyToParentLowerCase;
24 |
25 | @Getter
26 | private final String columnName;
27 |
28 | @Getter
29 | private final String columnNameLowerCase;
30 |
31 | @Getter
32 | private final ColumnDataType columnDataType;
33 |
34 | @Getter
35 | private String selectSql;
36 |
37 | @Getter
38 | private String insertSql;
39 |
40 | @Getter
41 | private String deleteSql;
42 |
43 | @Getter
44 | private String deleteForeignKeysSql;
45 |
46 | public FlattenedCollectionBlueprint(
47 | Class fieldClass,
48 | String tableName,
49 | String foreignKeyToParent,
50 | String columnName,
51 | ColumnDataType columnDataType)
52 | {
53 | this.fieldClass = fieldClass;
54 | this.tableName = tableName;
55 | this.foreignKeyToParent = foreignKeyToParent;
56 | this.foreignKeyToParentLowerCase = foreignKeyToParent.toLowerCase();
57 | this.columnName = columnName;
58 | this.columnNameLowerCase = columnName.toLowerCase();
59 | this.columnDataType = columnDataType;
60 | }
61 |
62 | public List getSelectColumnNames()
63 | {
64 | return Arrays.asList(columnName, foreignKeyToParent);
65 | }
66 |
67 | public List getSelectColumnNamesLowerCase()
68 | {
69 | return Arrays.asList(columnNameLowerCase, foreignKeyToParentLowerCase);
70 | }
71 |
72 | public void setSelectSql(String selectSql)
73 | {
74 | if(StringUtils.isBlank(selectSql))
75 | {
76 | throw new PhotonException("Select SQL cannot be blank.");
77 | }
78 | this.selectSql = selectSql;
79 | }
80 |
81 | public void setInsertSql(String insertSql)
82 | {
83 | if(StringUtils.isBlank(insertSql))
84 | {
85 | throw new PhotonException("Insert SQL cannot be blank.");
86 | }
87 | this.insertSql = insertSql;
88 | }
89 |
90 | public void setDeleteSql(String deleteSql)
91 | {
92 | if(StringUtils.isBlank(deleteSql))
93 | {
94 | throw new PhotonException("Delete SQL cannot be blank.");
95 | }
96 | this.deleteSql = deleteSql;
97 | }
98 |
99 | public void setDeleteForeignKeysSql(String deleteForeignKeysSql)
100 | {
101 | if(StringUtils.isBlank(deleteForeignKeysSql))
102 | {
103 | throw new PhotonException("Delete Foreign Keys SQL cannot be blank.");
104 | }
105 | this.deleteForeignKeysSql = deleteForeignKeysSql;
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/photon-core/src/test/resources/setup-shape-multi-table.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS `drawing`;
2 | DROP TABLE IF EXISTS `shape`;
3 | DROP TABLE IF EXISTS `shapecolorhistory`;
4 | DROP TABLE IF EXISTS `circle`;
5 | DROP TABLE IF EXISTS `rectangle`;
6 | DROP TABLE IF EXISTS `cornercoordinates`;
7 |
8 | CREATE TABLE `drawing` (
9 | `id` int(11) NOT NULL AUTO_INCREMENT,
10 | `name` varchar(255) NULL,
11 | PRIMARY KEY (`id`)
12 | );
13 |
14 | CREATE TABLE `shape` (
15 | `id` int(11) NOT NULL AUTO_INCREMENT,
16 | `type` varchar(255) NOT NULL,
17 | `color` varchar(255) NOT NULL,
18 | `drawingId` int(11) DEFAULT NULL,
19 | CONSTRAINT `shape_drawing` FOREIGN KEY (`drawingId`) REFERENCES `drawing` (`id`),
20 | PRIMARY KEY (`id`)
21 | );
22 |
23 | CREATE TABLE `shapecolorhistory` (
24 | `id` int(11) NOT NULL AUTO_INCREMENT,
25 | `shapeId` int(11) NOT NULL,
26 | `dateChanged` datetime NOT NULL,
27 | `colorName` varchar(255) NOT NULL,
28 | PRIMARY KEY (`id`),
29 | CONSTRAINT `shapecolorhistory_shape` FOREIGN KEY (`shapeId`) REFERENCES `shape` (`id`)
30 | );
31 |
32 | CREATE TABLE `circle` (
33 | `id` int(11) NOT NULL AUTO_INCREMENT,
34 | `radius` int(11) NULL,
35 | PRIMARY KEY (`id`),
36 | CONSTRAINT `circle_shape` FOREIGN KEY (`id`) REFERENCES `shape` (`id`)
37 | );
38 |
39 | CREATE TABLE `rectangle` (
40 | `id` int(11) NOT NULL AUTO_INCREMENT,
41 | `width` int(11) NULL,
42 | `height` int(11) NULL,
43 | PRIMARY KEY (`id`),
44 | CONSTRAINT `rectangle_shape` FOREIGN KEY (`id`) REFERENCES `shape` (`id`)
45 | );
46 |
47 | CREATE TABLE `cornercoordinates` (
48 | `id` int(11) NOT NULL AUTO_INCREMENT,
49 | `shapeId` int(11) NOT NULL,
50 | `x` int(11) NULL,
51 | `y` int(11) NULL,
52 | PRIMARY KEY (`id`),
53 | CONSTRAINT `cornerCoordinates_shape` FOREIGN KEY (`shapeId`) REFERENCES `shape` (`id`)
54 | );
55 |
56 | insert into `drawing` (`id`) values (1);
57 |
58 | insert into `shape` (`id`, `type`, `color`, `drawingId`) values (1, 'circle', 'red', 1);
59 | insert into `circle` (`id`, `radius`) values (1, 3);
60 |
61 | insert into `shape` (`id`, `type`, `color`, `drawingId`) values (2, 'rectangle', 'blue', 1);
62 | insert into `rectangle` (`id`, `width`, `height`) values (2, 7, 8);
63 |
64 | insert into `shape` (`id`, `type`, `color`, `drawingId`) values (3, 'circle', 'orange', 1);
65 | insert into `circle` (`id`, `radius`) values (3, 4);
66 | insert into `shapecolorhistory` (`id`, `shapeId`, `dateChanged`, `colorName`) values (1, 3, PARSEDATETIME('2017-03-19 09-28-17', 'yyyy-MM-dd HH-mm-ss'), 'creamsicle');
67 | insert into `shapecolorhistory` (`id`, `shapeId`, `dateChanged`, `colorName`) values (2, 3, PARSEDATETIME('2017-04-20 10-29-18', 'yyyy-MM-dd HH-mm-ss'), 'yellow');
68 |
69 | insert into `shape` (`id`, `type`, `color`, `drawingId`) values (4, 'rectangle', 'white', 1);
70 | insert into `rectangle` (`id`, `width`, `height`) values (4, 11, 9);
71 | insert into `shapecolorhistory` (`id`, `shapeId`, `dateChanged`, `colorName`) values (3, 4, PARSEDATETIME('2017-04-11 11-28-17', 'yyyy-MM-dd HH-mm-ss'), 'beige');
72 | insert into `shapecolorhistory` (`id`, `shapeId`, `dateChanged`, `colorName`) values (4, 4, PARSEDATETIME('2017-04-22 12-29-18', 'yyyy-MM-dd HH-mm-ss'), 'gray');
73 | insert into `cornercoordinates` (`id`, `shapeId`, `x`, `y`) values (1, 4, 0, 0);
74 | insert into `cornercoordinates` (`id`, `shapeId`, `x`, `y`) values (2, 4, 7, 0);
75 | insert into `cornercoordinates` (`id`, `shapeId`, `x`, `y`) values (3, 4, 0, 8);
76 | insert into `cornercoordinates` (`id`, `shapeId`, `x`, `y`) values (4, 4, 7, 8);
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/h2/shape/ShapeDbSetup.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.h2.shape;
2 |
3 | import com.github.molcikas.photon.Photon;
4 | import com.github.molcikas.photon.PhotonTransaction;
5 | import com.github.molcikas.photon.tests.unit.h2.H2TestUtil;
6 |
7 | public class ShapeDbSetup
8 | {
9 | public static Photon setupDatabase()
10 | {
11 | Photon photon = new Photon(H2TestUtil.h2Url, H2TestUtil.h2User, H2TestUtil.h2Password);
12 | return setupDatabase(photon);
13 | }
14 |
15 | public static Photon setupDatabase(Photon photon)
16 | {
17 | try(PhotonTransaction transaction = photon.beginTransaction())
18 | {
19 | transaction.query("DROP TABLE IF EXISTS `drawing`").executeUpdate();
20 | transaction.query("CREATE TABLE `drawing` (\n" +
21 | "`id` int(11) NOT NULL AUTO_INCREMENT,\n" +
22 | "`name` varchar(255) NULL,\n" +
23 | "PRIMARY KEY (`id`)\n" +
24 | ")").executeUpdate();
25 |
26 | transaction.query("insert into `drawing` (`id`) values (1)").executeUpdate();
27 |
28 | transaction.query("DROP TABLE IF EXISTS `shape`").executeUpdate();
29 | transaction.query("CREATE TABLE `shape` (\n" +
30 | " `id` int(11) NOT NULL AUTO_INCREMENT,\n" +
31 | " `type` varchar(255) NOT NULL,\n" +
32 | " `color` varchar(255) NOT NULL,\n" +
33 | " `radius` int(11) NULL,\n" +
34 | " `width` int(11) NULL,\n" +
35 | " `height` int(11) NULL,\n" +
36 | " `drawingId` int(11) DEFAULT NULL,\n" +
37 | " CONSTRAINT `shape_drawing` FOREIGN KEY (`drawingId`) REFERENCES `drawing` (`id`),\n" +
38 | " PRIMARY KEY (`id`)\n" +
39 | ")").executeUpdate();
40 |
41 | transaction.query("insert into `shape` (`id`, `type`, `color`, `radius`, `width`, `height`) values (1, 'circle', 'red', 3, NULL, NULL)").executeUpdate();
42 | transaction.query("insert into `shape` (`id`, `type`, `color`, `radius`, `width`, `height`) values (2, 'rectangle', 'blue', NULL, 7, 8)").executeUpdate();
43 |
44 | transaction.query("DROP TABLE IF EXISTS `cornercoordinates`").executeUpdate();
45 | transaction.query("CREATE TABLE `cornercoordinates` (\n" +
46 | " `id` int(11) NOT NULL AUTO_INCREMENT,\n" +
47 | " `shapeId` int(11) NOT NULL,\n" +
48 | " `x` int(11) NULL,\n" +
49 | " `y` int(11) NULL,\n" +
50 | " PRIMARY KEY (`id`),\n" +
51 | " CONSTRAINT `CornerCoordinates_Shape` FOREIGN KEY (`shapeId`) REFERENCES `shape` (`id`)\n" +
52 | ")").executeUpdate();
53 |
54 | transaction.query("insert into `cornercoordinates` (`id`, `shapeId`, `x`, `y`) values (1, 2, 0, 0)").executeUpdate();
55 | transaction.query("insert into `cornercoordinates` (`id`, `shapeId`, `x`, `y`) values (2, 2, 7, 0)").executeUpdate();
56 | transaction.query("insert into `cornercoordinates` (`id`, `shapeId`, `x`, `y`) values (3, 2, 0, 8)").executeUpdate();
57 | transaction.query("insert into `cornercoordinates` (`id`, `shapeId`, `x`, `y`) values (4, 2, 7, 8)").executeUpdate();
58 |
59 | transaction.commit();
60 | }
61 |
62 | return photon;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/blueprints/table/ColumnBlueprint.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.blueprints.table;
2 |
3 | import com.github.molcikas.photon.blueprints.entity.FieldBlueprint;
4 | import lombok.EqualsAndHashCode;
5 | import org.apache.commons.lang3.StringUtils;
6 | import com.github.molcikas.photon.converters.Converter;
7 | import com.github.molcikas.photon.exceptions.PhotonException;
8 |
9 | @EqualsAndHashCode
10 | public class ColumnBlueprint
11 | {
12 | private final String columnNameQualified;
13 | private final String columnName;
14 | private final ColumnDataType columnDataType;
15 | private final boolean isPrimaryKeyColumn;
16 | private final boolean isAutoIncrementColumn;
17 | private final boolean isForeignKeyToParentColumn;
18 | private final Converter customSerializer;
19 |
20 | // Reference to the entity field that this database column is mapped to. This can (but does not have to)
21 | // be null if this column is an unmapped primary key or a foreign key to the parent.
22 | private final FieldBlueprint mappedFieldBlueprint;
23 |
24 | private int columnIndex;
25 |
26 | public String getColumnNameQualified()
27 | {
28 | return columnNameQualified;
29 | }
30 |
31 | public String getColumnName()
32 | {
33 | return columnName;
34 | }
35 |
36 | public ColumnDataType getColumnDataType()
37 | {
38 | return columnDataType;
39 | }
40 |
41 | public boolean isPrimaryKeyColumn()
42 | {
43 | return isPrimaryKeyColumn;
44 | }
45 |
46 | public boolean isAutoIncrementColumn()
47 | {
48 | return isAutoIncrementColumn;
49 | }
50 |
51 | public boolean isForeignKeyToParentColumn()
52 | {
53 | return isForeignKeyToParentColumn;
54 | }
55 |
56 | public Converter getCustomSerializer()
57 | {
58 | return customSerializer;
59 | }
60 |
61 | public FieldBlueprint getMappedFieldBlueprint()
62 | {
63 | return mappedFieldBlueprint;
64 | }
65 |
66 | public int getColumnIndex()
67 | {
68 | return columnIndex;
69 | }
70 |
71 | public ColumnBlueprint(
72 | String tableName,
73 | String columnName,
74 | ColumnDataType columnDataType,
75 | boolean isPrimaryKeyColumn,
76 | boolean isAutoIncrementColumn,
77 | boolean isForeignKeyToParentColumn,
78 | Converter customSerializer,
79 | FieldBlueprint mappedFieldBlueprint,
80 | int columnIndex)
81 | {
82 | if(StringUtils.isBlank(tableName))
83 | {
84 | throw new PhotonException("Table name cannot be blank.");
85 | }
86 | if(StringUtils.isBlank(columnName))
87 | {
88 | throw new PhotonException("Column name cannot be blank.");
89 | }
90 | if(isAutoIncrementColumn && !isPrimaryKeyColumn)
91 | {
92 | throw new PhotonException("The column '%s' cannot be auto-increment because it is not the primary key.", columnName);
93 | }
94 | this.columnNameQualified = tableName + "_" + columnName;
95 | this.columnName = columnName;
96 | this.columnDataType = columnDataType;
97 | this.isPrimaryKeyColumn = isPrimaryKeyColumn;
98 | this.isAutoIncrementColumn = isAutoIncrementColumn;
99 | this.isForeignKeyToParentColumn = isForeignKeyToParentColumn;
100 | this.customSerializer = customSerializer;
101 | this.mappedFieldBlueprint = mappedFieldBlueprint;
102 | this.columnIndex = columnIndex;
103 | }
104 |
105 | void moveColumnToIndex(int newColumnIndex)
106 | {
107 | this.columnIndex = newColumnIndex;
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/photon-perf-test/src/test/java/com/github/molcikas/photon/perf/RecipeDbSetup.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.perf;
2 |
3 | import com.zaxxer.hikari.HikariConfig;
4 | import com.zaxxer.hikari.HikariDataSource;
5 | import com.github.molcikas.photon.Photon;
6 | import com.github.molcikas.photon.PhotonTransaction;
7 |
8 | import javax.sql.DataSource;
9 |
10 | public class RecipeDbSetup
11 | {
12 | private static final String h2Url = "jdbc:h2:mem:test;MODE=MySQL;DB_CLOSE_DELAY=-1";
13 | private static final String h2User = "sa";
14 | private static final String h2Password = "";
15 |
16 | public static DataSource createDataSource()
17 | {
18 | HikariConfig hikariConfig = new HikariConfig();
19 | hikariConfig.setJdbcUrl(h2Url);
20 | hikariConfig.setUsername(h2User);
21 | hikariConfig.setPassword(h2Password);
22 | hikariConfig.addDataSourceProperty("cachePrepStmts", "true");
23 | hikariConfig.addDataSourceProperty("prepStmtCacheSize", "250");
24 | hikariConfig.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
25 |
26 | return new HikariDataSource(hikariConfig);
27 | }
28 |
29 | public static void setupDatabase()
30 | {
31 | Photon photon = new Photon(h2Url, h2User, h2Password);
32 |
33 | try(PhotonTransaction transaction = photon.beginTransaction())
34 | {
35 | transaction.query("DROP TABLE IF EXISTS recipe").executeUpdate();
36 | transaction.query("CREATE TABLE recipe (\n" +
37 | " recipeId binary(16) NOT NULL,\n" +
38 | " name varchar(50) NOT NULL,\n" +
39 | " description text NOT NULL,\n" +
40 | " prepTime int(10) unsigned NOT NULL,\n" +
41 | " cookTime int(10) unsigned NOT NULL,\n" +
42 | " servings int(10) unsigned NOT NULL,\n" +
43 | " isVegetarian tinyint(1) NOT NULL,\n" +
44 | " isVegan tinyint(1) NOT NULL,\n" +
45 | " isPublished tinyint(1) NOT NULL,\n" +
46 | " credit varchar(512) DEFAULT NULL,\n" +
47 | " PRIMARY KEY (recipeId)\n" +
48 | ")").executeUpdate();
49 |
50 | transaction.query("DROP TABLE IF EXISTS recipeingredient").executeUpdate();
51 | transaction.query("CREATE TABLE recipeingredient (\n" +
52 | " recipeIngredientId binary(16) NOT NULL,\n" +
53 | " recipeId binary(16) NOT NULL,\n" +
54 | " isRequired tinyint(1) NOT NULL,\n" +
55 | " quantity varchar(64) DEFAULT NULL,\n" +
56 | " quantityUnit varchar(64) DEFAULT NULL,\n" +
57 | " quantityDetail varchar(64) DEFAULT NULL,\n" +
58 | " name varchar(128) NOT NULL,\n" +
59 | " preparation varchar(128) DEFAULT NULL,\n" +
60 | " orderBy int(11) NOT NULL,\n" +
61 | " PRIMARY KEY (recipeIngredientId),\n" +
62 | " CONSTRAINT RecipeIngredient_Recipe FOREIGN KEY (recipeId) REFERENCES recipe (recipeId) ON DELETE NO ACTION ON UPDATE CASCADE\n" +
63 | ")").executeUpdate();
64 |
65 | transaction.query("DROP TABLE IF EXISTS recipeinstruction").executeUpdate();
66 | transaction.query("CREATE TABLE recipeinstruction (\n" +
67 | " recipeInstructionId binary(16) NOT NULL,\n" +
68 | " recipeId binary(16) NOT NULL,\n" +
69 | " stepNumber int(10) unsigned NOT NULL,\n" +
70 | " description text NOT NULL,\n" +
71 | " PRIMARY KEY (recipeInstructionId),\n" +
72 | " CONSTRAINT RecipeInstruction_Recipe FOREIGN KEY (recipeId) REFERENCES recipe (recipeId) ON DELETE NO ACTION ON UPDATE CASCADE\n" +
73 | ")").executeUpdate();
74 |
75 | transaction.commit();
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/h2/shape/ShapeChildEntityTests.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.h2.shape;
2 |
3 | import com.github.molcikas.photon.Photon;
4 | import com.github.molcikas.photon.PhotonTransaction;
5 | import com.github.molcikas.photon.blueprints.table.ColumnDataType;
6 | import com.github.molcikas.photon.tests.unit.entities.shape.Circle;
7 | import com.github.molcikas.photon.tests.unit.entities.shape.CornerCoordinates;
8 | import com.github.molcikas.photon.tests.unit.entities.shape.Rectangle;
9 | import com.github.molcikas.photon.tests.unit.entities.shape.Shape;
10 | import org.junit.Before;
11 | import org.junit.Test;
12 |
13 | import static org.junit.Assert.*;
14 |
15 | public class ShapeChildEntityTests
16 | {
17 | private Photon photon;
18 |
19 | @Before
20 | public void setupDatabase()
21 | {
22 | photon = ShapeDbSetup.setupDatabase();
23 | }
24 |
25 | @Test
26 | public void withMappedClass_selectExistingRows_createsAggregate()
27 | {
28 | registerAggregate();
29 |
30 | try (PhotonTransaction transaction = photon.beginTransaction())
31 | {
32 | Rectangle rectangle = transaction
33 | .query(Rectangle.class)
34 | .fetchById(2);
35 |
36 | assertNotNull(rectangle);
37 | assertEquals(Integer.valueOf(2), rectangle.getId());
38 | assertEquals("blue", rectangle.getColor());
39 |
40 | assertEquals(4, rectangle.getCorners().size());
41 | assertEquals(Integer.valueOf(0), rectangle.getCorners().get(0).getX());
42 | assertEquals(Integer.valueOf(7), rectangle.getCorners().get(1).getX());
43 | assertEquals(Integer.valueOf(8), rectangle.getCorners().get(3).getY());
44 | }
45 | }
46 |
47 | @Test
48 | public void withMappedClass_updateExistingRows_updatesAggregate()
49 | {
50 | registerAggregate();
51 |
52 | try (PhotonTransaction transaction = photon.beginTransaction())
53 | {
54 | Rectangle rectangle = transaction
55 | .query(Rectangle.class)
56 | .fetchById(2);
57 |
58 | rectangle.getCorners().remove(1);
59 | rectangle.getCorners().get(2).setX(3);
60 | transaction.save(rectangle);
61 | transaction.commit();
62 | }
63 |
64 | try (PhotonTransaction transaction = photon.beginTransaction())
65 | {
66 | Rectangle rectangle = transaction
67 | .query(Rectangle.class)
68 | .fetchById(2);
69 |
70 | assertEquals(3, rectangle.getCorners().size());
71 | assertEquals(Integer.valueOf(0), rectangle.getCorners().get(0).getX());
72 | assertEquals(Integer.valueOf(8), rectangle.getCorners().get(1).getY());
73 | assertEquals(Integer.valueOf(3), rectangle.getCorners().get(2).getX());
74 | }
75 | }
76 |
77 | private void registerAggregate()
78 | {
79 | photon.registerAggregate(Shape.class)
80 | .withMappedClass(Circle.class)
81 | .withMappedClass(Rectangle.class)
82 | .withClassDiscriminator(valuesMap ->
83 | {
84 | String type = (String) valuesMap.get("Shape_type");
85 | switch (type)
86 | {
87 | case "circle":
88 | return Circle.class;
89 | case "rectangle":
90 | return Rectangle.class;
91 | default:
92 | return null;
93 | }
94 | })
95 | .withChild("corners", CornerCoordinates.class)
96 | .withForeignKeyToParent("shapeId", ColumnDataType.INTEGER)
97 | .addAsChild()
98 | .register();
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/h2/shape/ShapeQueryTests.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.h2.shape;
2 |
3 | import com.github.molcikas.photon.Photon;
4 | import com.github.molcikas.photon.PhotonTransaction;
5 | import com.github.molcikas.photon.tests.unit.entities.shape.Circle;
6 | import com.github.molcikas.photon.tests.unit.entities.shape.Rectangle;
7 | import com.github.molcikas.photon.tests.unit.entities.shape.Shape;
8 | import org.junit.Before;
9 | import org.junit.Test;
10 |
11 | import java.util.List;
12 |
13 | import static junit.framework.TestCase.assertNotNull;
14 | import static org.junit.Assert.assertEquals;
15 |
16 | public class ShapeQueryTests
17 | {
18 | private Photon photon;
19 |
20 | @Before
21 | public void setupDatabase()
22 | {
23 | photon = ShapeDbSetup.setupDatabase();
24 | }
25 |
26 | @Test
27 | public void withMappedClass_selectExistingRow_fetchesObject()
28 | {
29 | registerAggregate();
30 |
31 | try (PhotonTransaction transaction = photon.beginTransaction())
32 | {
33 | Circle circle = transaction
34 | .query("SELECT * FROM shape WHERE id = 1")
35 | .withMappedClass(Shape.class)
36 | .fetch(Circle.class);
37 |
38 | assertNotNull(circle);
39 | assertEquals(Integer.valueOf(1), circle.getId());
40 | assertEquals("red", circle.getColor());
41 | assertEquals(3, circle.getRadius());
42 | }
43 | }
44 |
45 | @Test
46 | public void withMappedClass_selectExistingRows_fetchesList()
47 | {
48 | registerAggregate();
49 |
50 | try (PhotonTransaction transaction = photon.beginTransaction())
51 | {
52 | List shapes = transaction
53 | .query("SELECT * FROM shape WHERE id IN (1, 2)")
54 | .withMappedClass(Circle.class)
55 | .withMappedClass(Rectangle.class)
56 | .withClassDiscriminator(valuesMap ->
57 | {
58 | String type = (String) valuesMap.get("type");
59 | switch (type)
60 | {
61 | case "circle":
62 | return Circle.class;
63 | case "rectangle":
64 | return Rectangle.class;
65 | default:
66 | return null;
67 | }
68 | })
69 | .fetchList(Shape.class);
70 |
71 | assertEquals(2, shapes.size());
72 |
73 | Circle circle = (Circle) shapes.get(0);
74 | assertEquals(Integer.valueOf(1), circle.getId());
75 | assertEquals("red", circle.getColor());
76 | assertEquals(3, circle.getRadius());
77 |
78 | Rectangle rectangle = (Rectangle) shapes.get(1);
79 | assertEquals(Integer.valueOf(2), rectangle.getId());
80 | assertEquals("blue", rectangle.getColor());
81 | assertEquals(7, rectangle.getWidth());
82 | assertEquals(8, rectangle.getHeight());
83 | }
84 | }
85 |
86 | private void registerAggregate()
87 | {
88 | photon.registerAggregate(Shape.class)
89 | .withMappedClass(Circle.class)
90 | .withMappedClass(Rectangle.class)
91 | .withClassDiscriminator(valuesMap ->
92 | {
93 | String type = (String) valuesMap.get("Shape_type");
94 | switch (type)
95 | {
96 | case "circle":
97 | return Circle.class;
98 | case "rectangle":
99 | return Rectangle.class;
100 | default:
101 | return null;
102 | }
103 | })
104 | .register();
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/h2/twoaggregates/TwoAggregatesDbSetup.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.h2.twoaggregates;
2 |
3 | import com.github.molcikas.photon.Photon;
4 | import com.github.molcikas.photon.PhotonTransaction;
5 | import com.github.molcikas.photon.tests.unit.h2.H2TestUtil;
6 |
7 | public class TwoAggregatesDbSetup
8 | {
9 | public static Photon setupDatabase()
10 | {
11 | Photon photon = new Photon(H2TestUtil.h2Url, H2TestUtil.h2User, H2TestUtil.h2Password);
12 |
13 | try(PhotonTransaction transaction = photon.beginTransaction())
14 | {
15 | transaction.query("DROP TABLE IF EXISTS `aggregateone`").executeUpdate();
16 | transaction.query("CREATE TABLE `aggregateone` (\n" +
17 | " `aggregateOneId` binary(16) NOT NULL,\n" +
18 | " `myValue` varchar(255) DEFAULT 'oops',\n" +
19 | " PRIMARY KEY (`aggregateOneId`)\n" +
20 | ")").executeUpdate();
21 | transaction.query("insert into `aggregateone` (`aggregateOneId`, `myValue`) values (X'3DFFC3B3A9B611E6AB830A0027000010', 'agg1val0')").executeUpdate();
22 | transaction.query("insert into `aggregateone` (`aggregateOneId`, `myValue`) values (X'3DFFC3B3A9B611E6AB830A0027000011', 'agg1val1')").executeUpdate();
23 | transaction.query("insert into `aggregateone` (`aggregateOneId`, `myValue`) values (X'3DFFC3B3A9B611E6AB830A0027000012', 'agg1val2')").executeUpdate();
24 |
25 | transaction.query("DROP TABLE IF EXISTS `aggregatetwo`").executeUpdate();
26 | transaction.query("CREATE TABLE `aggregatetwo` (\n" +
27 | " `aggregateOneId` binary(16) NOT NULL,\n" +
28 | " `aggregateTwoId` binary(16) NOT NULL,\n" +
29 | " `myValue` varchar(255) DEFAULT 'oops',\n" +
30 | " PRIMARY KEY (`aggregateTwoId`)\n" +
31 | ")").executeUpdate();
32 | transaction.query("insert into `aggregatetwo` (`aggregateTwoId`, `myValue`) values (X'3DFFC3B3A9B611E6AB830A0027000020', 'agg2val0')").executeUpdate();
33 | transaction.query("insert into `aggregatetwo` (`aggregateTwoId`, `myValue`) values (X'3DFFC3B3A9B611E6AB830A0027000021', 'agg2val1')").executeUpdate();
34 | transaction.query("insert into `aggregatetwo` (`aggregateTwoId`, `myValue`) values (X'3DFFC3B3A9B611E6AB830A0027000022', 'agg2val2')").executeUpdate();
35 |
36 | transaction.query("DROP TABLE IF EXISTS `aggregatemapping`").executeUpdate();
37 | transaction.query("CREATE TABLE `aggregatemapping` (\n" +
38 | " `aggregateOneId` binary(16) NOT NULL,\n" +
39 | " `aggregateTwoId` binary(16) NOT NULL,\n" +
40 | " PRIMARY KEY (`aggregateOneId`, `aggregateTwoId`),\n" +
41 | " CONSTRAINT `AggregateMapping_AggregateOne` FOREIGN KEY (`aggregateOneId`) REFERENCES `aggregateone` (`aggregateOneId`),\n" +
42 | " CONSTRAINT `AggregateMapping_AggregateTwo` FOREIGN KEY (`aggregateTwoId`) REFERENCES `aggregatetwo` (`aggregateTwoId`)\n" +
43 | ")").executeUpdate();
44 | transaction.query("insert into `aggregatemapping` (`aggregateOneId`, `aggregateTwoId`) values (X'3DFFC3B3A9B611E6AB830A0027000011', X'3DFFC3B3A9B611E6AB830A0027000021')").executeUpdate();
45 | transaction.query("insert into `aggregatemapping` (`aggregateOneId`, `aggregateTwoId`) values (X'3DFFC3B3A9B611E6AB830A0027000012', X'3DFFC3B3A9B611E6AB830A0027000020')").executeUpdate();
46 | transaction.query("insert into `aggregatemapping` (`aggregateOneId`, `aggregateTwoId`) values (X'3DFFC3B3A9B611E6AB830A0027000012', X'3DFFC3B3A9B611E6AB830A0027000021')").executeUpdate();
47 | transaction.query("insert into `aggregatemapping` (`aggregateOneId`, `aggregateTwoId`) values (X'3DFFC3B3A9B611E6AB830A0027000012', X'3DFFC3B3A9B611E6AB830A0027000022')").executeUpdate();
48 |
49 | transaction.commit();
50 | }
51 |
52 | return photon;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/docs/ValueObjects.md:
--------------------------------------------------------------------------------
1 | # Working with Value Objects
2 |
3 | ORMs typically very little support for . They expect you to only use primitive values likes strings and integers in your entities. Photon provides several mechanisms to make it easier for you to use value objects.
4 |
5 | If you have a value object in an entity, you can customize how that value object is hydrated from the database or serialized into the database.
6 |
7 | ```java
8 | photon.registerAggregate(RecipeIngredient.class)
9 | // The quantity is a VARCHAR in the database but is converted to a Fraction value object when hydrated.
10 | .withDatabaseColumnSerializer("quantity", fraction -> fraction.getNumerator() + "/" + fraction.getDenominator())
11 | .withFieldHydrater("quantity", fractionString -> Fraction.getFraction(fractionString))
12 | .register();
13 | ```
14 |
15 | If you have a common value object that is used in multiple entities in your application, and the value object maps to a single database column value using its `toString()` method, you can register a global custom converter for hydrating it.
16 |
17 | ```java
18 | Photon.registerConverter(Fraction.class, fraction -> Fraction.getFraction(fraction));
19 | ```
20 |
21 | If you have a value object that does not neatly map between an entity field and a database column, you can create a custom field value mapper.
22 |
23 | ```java
24 | photon.registerAggregate(Product.class)
25 | .withDatabaseColumn("quantity", ColumnDataType.INTEGER, new EntityFieldValueMapping()
26 | {
27 | @Override
28 | public String getFieldValue(MyTable entityInstance)
29 | {
30 | return entityInstance.getQuantityInfo().getQuantity();
31 | }
32 |
33 | @Override
34 | public Map setFieldValue(MyTable entityInstance, Integer value)
35 | {
36 | entityInstance.getQuantityInfo().updateQuantity(value);
37 | return null;
38 | }
39 | }
40 | )
41 | .register();
42 | ```
43 |
44 | If you have a value object that maps to multiple database columns, you can create a custom mapper for the value object.
45 |
46 | ```java
47 | photon.registerAggregate(Product.class)
48 | .withDatabaseColumns(
49 | Arrays.asList(
50 | new DatabaseColumnDefinition("numerator", ColumnDataType.INTEGER),
51 | new DatabaseColumnDefinition("denominator", ColumnDataType.INTEGER)
52 | ),
53 | new CompoundEntityFieldValueMapping()
54 | {
55 | @Override
56 | public Map getDatabaseValues(Product entityInstance)
57 | {
58 | Map values = new HashMap<>();
59 | values.put("numerator", entityInstance.getQuantity().getNumerator());
60 | values.put("denominator", entityInstance.getQuantity().getDenominator());
61 | return values;
62 | }
63 |
64 | @Override
65 | public Map setFieldValues(Product entityInstance, Map values)
66 | {
67 | Map valuesToSet = new HashMap<>();
68 | valuesToSet.put(
69 | "quantity",
70 | Fraction.getFraction((int) values.get("numerator"), (int) values.get("denominator"))
71 | );
72 | return valuesToSet;
73 | }
74 | }
75 | )
76 | .register();
77 | ```
78 |
79 | If you have a value object that is a list of items, you can add the list as a child of the entity and optionally omit the id (since value objects are not supposed to have an id) and the foreign key to the parent. However, if you omit the id, Photon won't be able to link each object in the list to its database row, so the entire set of rows will be deleted and re-inserted whenever the aggregate is saved.
80 |
81 | ```java
82 | photon.registerAggregate(Recipe.class)
83 | .withChild("ingredients", RecipeIngredient.class)
84 | // The id and foreign key to parent must be specified here, but do not need to be in
85 | // the RecipeIngredient class.
86 | .withId("recipeIngredientId")
87 | .withForeignKeyToParent("recipeId")
88 | .addAsChild()
89 | .register();
90 | ```
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/options/PhotonOptions.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.options;
2 |
3 | import com.github.molcikas.photon.blueprints.table.ColumnDataType;
4 | import lombok.Builder;
5 | import lombok.Getter;
6 |
7 | /**
8 | * The options for photon.
9 | */
10 | @Getter
11 | public class PhotonOptions
12 | {
13 | public static final ColumnDataType DEFAULT_UUID_DATA_TYPE = ColumnDataType.BINARY;
14 |
15 | private final String delimitIdentifierStart;
16 | private final String delimitIdentifierEnd;
17 | private final DefaultTableName defaultTableName;
18 | private final boolean enableJdbcGetGeneratedKeys;
19 | private final ColumnDataType defaultUuidDataType;
20 |
21 | /**
22 | * Constructor. Defaults the UUID data type to PhotonOptions.DEFAULT_UUID_DATA_TYPE.
23 | *
24 | * @param delimitIdentifierStart - The delimiter identifier start (e.g. "[" for SQL Server or "`" for MySQL)
25 | * @param delimitIdentifierEnd - The delimiter identifier end (e.g. "]" for SQL Server or "`" for MySQL)
26 | * @param defaultTableName - The strategy for determining the default table name for an entity
27 | * @param enableJdbcGetGeneratedKeys - Whether to use Statement.RETURN_GENERATED_KEYS when creating prepared
28 | * statements. Set this to false for Oracle databases.
29 | * @param defaultUuidDataType - Default java.sql.Types value for UUID fields. Set this to null for Postgres
30 | * databases.
31 | */
32 | @Builder
33 | public PhotonOptions(
34 | String delimitIdentifierStart,
35 | String delimitIdentifierEnd,
36 | DefaultTableName defaultTableName,
37 | Boolean enableJdbcGetGeneratedKeys,
38 | ColumnDataType defaultUuidDataType)
39 | {
40 | this.delimitIdentifierStart = delimitIdentifierStart != null ? delimitIdentifierStart : "";
41 | this.delimitIdentifierEnd = delimitIdentifierEnd != null ? delimitIdentifierEnd : "";
42 | this.defaultTableName = defaultTableName != null ? defaultTableName : DefaultTableName.ClassName;
43 | this.enableJdbcGetGeneratedKeys = enableJdbcGetGeneratedKeys != null ? enableJdbcGetGeneratedKeys : true;
44 | this.defaultUuidDataType = defaultUuidDataType;
45 | }
46 |
47 | /**
48 | * Creates a PhotonOptions object with the default options.
49 | *
50 | * @return - the photon options
51 | */
52 | public static PhotonOptions defaultOptions()
53 | {
54 | return new PhotonOptions(null, null, null, null, DEFAULT_UUID_DATA_TYPE);
55 | }
56 |
57 | /**
58 | * Recommended options for MySQL databases.
59 | *
60 | * @return - the photon options
61 | */
62 | public static PhotonOptionsBuilder mysqlOptions()
63 | {
64 | return PhotonOptions
65 | .builder()
66 | .delimitIdentifierStart("`")
67 | .delimitIdentifierEnd("`")
68 | .defaultUuidDataType(ColumnDataType.BINARY);
69 | }
70 |
71 | /**
72 | * Recommended options for Oracle databases.
73 | *
74 | * @return - the photon options
75 | */
76 | public static PhotonOptionsBuilder oracleOptions()
77 | {
78 | return PhotonOptions
79 | .builder()
80 | .enableJdbcGetGeneratedKeys(false)
81 | .defaultUuidDataType(ColumnDataType.BINARY);
82 | }
83 |
84 | /**
85 | * Recommended options for Postgres databases.
86 | *
87 | * @return - the photon options
88 | */
89 | public static PhotonOptionsBuilder postgresOptions()
90 | {
91 | return PhotonOptions
92 | .builder()
93 | .delimitIdentifierStart("\"")
94 | .delimitIdentifierEnd("\"")
95 | .defaultUuidDataType(null);
96 | }
97 |
98 | /**
99 | * Recommended options for SQL Server databases.
100 | *
101 | * @return - the photon options
102 | */
103 | public static PhotonOptionsBuilder sqlServerOptions()
104 | {
105 | return PhotonOptions
106 | .builder()
107 | .delimitIdentifierStart("[")
108 | .delimitIdentifierEnd("]")
109 | .defaultUuidDataType(ColumnDataType.BINARY);
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/entities/recipe/Recipe.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.entities.recipe;
2 |
3 | import java.util.List;
4 | import java.util.Objects;
5 | import java.util.UUID;
6 |
7 | public class Recipe
8 | {
9 | private UUID recipeId;
10 |
11 | private String name;
12 |
13 | private String description;
14 |
15 | private int prepTime;
16 |
17 | private int cookTime;
18 |
19 | private int servings;
20 |
21 | private boolean isVegetarian;
22 |
23 | private boolean isVegan;
24 |
25 | private boolean isPublished;
26 |
27 | private String credit;
28 |
29 | private List ingredients;
30 |
31 | private List instructions;
32 |
33 | public UUID getRecipeId()
34 | {
35 | return recipeId;
36 | }
37 |
38 | public String getName()
39 | {
40 | return name;
41 | }
42 |
43 | public String getDescription()
44 | {
45 | return description;
46 | }
47 |
48 | public int getPrepTime()
49 | {
50 | return prepTime;
51 | }
52 |
53 | public int getCookTime()
54 | {
55 | return cookTime;
56 | }
57 |
58 | public int getServings()
59 | {
60 | return servings;
61 | }
62 |
63 | public boolean isVegetarian()
64 | {
65 | return isVegetarian;
66 | }
67 |
68 | public boolean isVegan()
69 | {
70 | return isVegan;
71 | }
72 |
73 | public boolean isPublished()
74 | {
75 | return isPublished;
76 | }
77 |
78 | public String getCredit()
79 | {
80 | return credit;
81 | }
82 |
83 | public List getIngredients()
84 | {
85 | return ingredients;
86 | }
87 |
88 | public List getInstructions()
89 | {
90 | return instructions;
91 | }
92 |
93 | // Anemic setters are strongly discouraged in aggregates, but we need these for testing.
94 |
95 | public void setInstructions(List instructions)
96 | {
97 | this.instructions = instructions;
98 | }
99 |
100 | public void setName(String name)
101 | {
102 | this.name = name;
103 | }
104 |
105 | public void setPrepTime(int prepTime)
106 | {
107 | this.prepTime = prepTime;
108 | }
109 |
110 | private Recipe()
111 | {
112 | }
113 |
114 | public Recipe(UUID recipeId, String name, String description, int prepTime, int cookTime, int servings, boolean isVegetarian, boolean isVegan, boolean isPublished, String credit, List ingredients, List instructions)
115 | {
116 | this.recipeId = recipeId;
117 | this.name = name;
118 | this.description = description;
119 | this.prepTime = prepTime;
120 | this.cookTime = cookTime;
121 | this.servings = servings;
122 | this.isVegetarian = isVegetarian;
123 | this.isVegan = isVegan;
124 | this.isPublished = isPublished;
125 | this.credit = credit;
126 | this.ingredients = ingredients;
127 | this.instructions = instructions;
128 | }
129 |
130 | @Override
131 | public boolean equals(Object o)
132 | {
133 | if (this == o) return true;
134 | if (o == null || getClass() != o.getClass()) return false;
135 | Recipe recipe = (Recipe) o;
136 | return prepTime == recipe.prepTime &&
137 | cookTime == recipe.cookTime &&
138 | servings == recipe.servings &&
139 | isVegetarian == recipe.isVegetarian &&
140 | isVegan == recipe.isVegan &&
141 | isPublished == recipe.isPublished &&
142 | Objects.equals(recipeId, recipe.recipeId) &&
143 | Objects.equals(name, recipe.name) &&
144 | Objects.equals(description, recipe.description) &&
145 | Objects.equals(credit, recipe.credit) &&
146 | Objects.equals(ingredients, recipe.ingredients) &&
147 | Objects.equals(instructions, recipe.instructions);
148 | }
149 |
150 | @Override
151 | public int hashCode()
152 | {
153 | return Objects.hash(recipeId, name, description, prepTime, cookTime, servings, isVegetarian, isVegan, isPublished, credit, ingredients, instructions);
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/docs/ViewModels.md:
--------------------------------------------------------------------------------
1 | # Constructing View Models using SQL
2 |
3 | User interfaces often need to display data from many different tables. Or, they need to show summaries and aggregations of data, such as averages, counts, or sums. One method for getting this data is to select all the entities that contain the necessary data and construct the view model in code. But there are several problems with this approach. First, you end up selecting more data than you need from the database, which creates unnecessary latency in your query and load on your database and application servers. Second, your entity object graphs become too large because they have to support these queries. A better approach is to have separate models that are specifically for displaying and reporting.
4 |
5 | Photon makes it easy to construct custom view models using plain SQL.
6 |
7 | ```java
8 | try(PhotonTransaction transaction = photon.beginTransaction())
9 | {
10 | String sql =
11 | "SELECT Product.id, Product.name, Order.orderDate " +
12 | "FROM Product " +
13 | "JOIN ProductOrders ON ProductOrders.productId = Product.id " +
14 | "JOIN Orders ON Orders.id = ProductOrder.orderId " +
15 | "WHERE Orders.orderDate > :orderDate " +
16 | "ORDER Orders.orderDate DESC ";
17 |
18 | List productOrdersAfterDate = transaction
19 | .query(sql)
20 | .addParameter("orderDate", new Date("2017/06/15"))
21 | .fetchList(ProductOrderDto.class);
22 |
23 | return productOrdersAfterDate;
24 | }
25 | ```
26 |
27 | Query results are automatically mapped to the view model fields by name. Unlike aggregates, view models do not need to be pre-registered with Photon.
28 |
29 | It's also possible to fetch scalar values and lists using plain SQL:
30 |
31 | ```java
32 | try(PhotonTransaction transaction = photon.beginTransaction())
33 | {
34 | String sql =
35 | "SELECT DISTINCT name " +
36 | "FROM Product " +
37 | "WHERE CHAR_LENGTH(name) > :nameLength ";
38 |
39 | List longProductNames = transaction
40 | .query(sql)
41 | .addParameter("nameLength", 8)
42 | .fetchScalarList(String.class);
43 |
44 | return longProductNames;
45 | }
46 | ```
47 |
48 | If you want to construct a view model that consists of root objects each containing a list of child objects, you could write multiple `SELECT` statements and aggregate the data yourself into a single DTO, but this quickly becomes tedious and error prone. Photon offers support for constructing aggregate view models so that you only have to write one `SELECT` statement. Aggregate view models use the same builder as regular aggregates.
49 |
50 | ```java
51 | public class ProductOrdersDto
52 | {
53 | public long productId;
54 |
55 | public String productName;
56 |
57 | public List productOrders;
58 | }
59 |
60 | public class ProductOrderDto
61 | {
62 | public long orderId;
63 |
64 | public Date orderDate;
65 | }
66 | ```
67 |
68 | ```java
69 | // Register the view model aggregate. You can re-use the same DTO classes in multiple view model aggregates (e.g. if you want different
70 | // view models with different sort orders), just give each aggregate view model a unique name.
71 |
72 | photon
73 | .registerViewModelAggregate(ProductOrdersDto.class, "ProductOrdersMostRecentFirst")
74 | .withId("productId")
75 | .withChild("productOrders", ProductOrderDto.class)
76 | .withForeignKeyToParent("productOrderId")
77 | .withOrderBySql("Order.orderDate DESC")
78 | .addAsChild()
79 | .register();
80 |
81 | // Create a query similar to querying for a regular aggregate.
82 |
83 | try(PhotonTransaction transaction = photon.beginTransaction())
84 | {
85 | String sql =
86 | "SELECT Product.id " +
87 | "FROM Product " +
88 | "JOIN ProductOrders ON ProductOrders.productId = Product.id " +
89 | "JOIN Orders ON Orders.id = ProductOrder.orderId " +
90 | "GROUP BY Product.id " +
91 | "HAVING COUNT(DISTINCT Orders.id) >= :minOrderCount ";
92 |
93 | List productOrders = transaction
94 | .query(ProductOrdersDto.class, "ProductOrdersMostRecentFirst")
95 | .whereIdIn(sql)
96 | .addParameter("minOrderCount", 3)
97 | .fetchList();
98 |
99 | return productOrders;
100 | }
101 | ```
102 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/integration/MySqlIntegrationTest.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.integration;
2 |
3 | import com.github.molcikas.photon.Photon;
4 | import com.github.molcikas.photon.PhotonTransaction;
5 | import com.github.molcikas.photon.options.PhotonOptions;
6 | import org.junit.After;
7 | import org.junit.Before;
8 | import org.junit.Test;
9 |
10 | import java.time.Instant;
11 | import java.time.ZoneId;
12 | import java.time.ZonedDateTime;
13 | import java.util.TimeZone;
14 | import java.util.UUID;
15 |
16 | import static org.junit.Assert.assertEquals;
17 | import static org.junit.Assert.assertNotNull;
18 |
19 | public class MySqlIntegrationTest
20 | {
21 | private Photon photon;
22 | private TimeZone timeZone;
23 |
24 | @Before
25 | public void setup()
26 | {
27 | timeZone = TimeZone.getDefault();
28 | TimeZone.setDefault(TimeZone.getTimeZone("America/Chicago"));
29 |
30 | String url = "jdbc:mysql://localhost:13306";
31 | photon = new Photon(url, "root", "bears", PhotonOptions.mysqlOptions().build());
32 |
33 | try(PhotonTransaction transaction = photon.beginTransaction())
34 | {
35 | transaction.query(
36 | "CREATE DATABASE IF NOT EXISTS photon_test_db"
37 | ).executeInsert();
38 | transaction.commit();
39 | }
40 |
41 | url = "jdbc:mysql://localhost:13306/photon_test_db";
42 | photon = new Photon(url, "root", "bears", PhotonOptions.mysqlOptions().build());
43 |
44 | photon
45 | .registerAggregate(PhotonTestTable.class)
46 | .withId("id")
47 | .withPrimaryKeyAutoIncrement()
48 | .register();
49 |
50 | try(PhotonTransaction transaction = photon.beginTransaction())
51 | {
52 | transaction.query(
53 | "DROP TABLE IF EXISTS PhotonTestTable"
54 | ).executeInsert();
55 |
56 | transaction.query(
57 | "CREATE TABLE PhotonTestTable( " +
58 | "`id` int NOT NULL AUTO_INCREMENT, " +
59 | "`uuidColumn` binary(16) NOT NULL, " +
60 | "`dateColumn` datetime NOT NULL, " +
61 | "`varcharColumn` varchar(50) NOT NULL, " +
62 | "PRIMARY KEY (`id`) " +
63 | ") "
64 | ).executeInsert();
65 |
66 | transaction.query("INSERT INTO PhotonTestTable VALUES (DEFAULT, UNHEX('8ED1E1BD253E4469B4CB71E1217825B7'), FROM_UNIXTIME(1489915698), 'Test String')").executeInsert();
67 |
68 | transaction.commit();
69 | }
70 | }
71 |
72 | @After
73 | public void cleanUp()
74 | {
75 | TimeZone.setDefault(timeZone);
76 | }
77 |
78 | @Test
79 | public void fetchExistingAggregateById_populatesValues()
80 | {
81 | try(PhotonTransaction transaction = photon.beginTransaction())
82 | {
83 | PhotonTestTable photonTestTable = transaction.query(PhotonTestTable.class).fetchById(1);
84 |
85 | assertNotNull(photonTestTable);
86 | assertEquals(1, photonTestTable.getId());
87 | assertEquals(UUID.fromString("8ED1E1BD-253E-4469-B4CB-71E1217825B7"), photonTestTable.getUuidColumn());
88 | assertEquals(ZonedDateTime.ofInstant(Instant.ofEpochMilli(1489915698000L), ZoneId.systemDefault()), photonTestTable.getDateColumn());
89 | assertEquals("Test String", photonTestTable.getVarcharColumn());
90 | }
91 | }
92 |
93 | @Test
94 | public void insertAggregateAndFetch_insertsAggregateAndPopulatesValues()
95 | {
96 | PhotonTestTable photonTestTable = new PhotonTestTable(
97 | null,
98 | UUID.fromString("11111111-2222-3333-4444-555555555555"),
99 | ZonedDateTime.ofInstant(Instant.ofEpochSecond(1493493022), ZoneId.systemDefault()),
100 | "My Test String"
101 | );
102 |
103 | try(PhotonTransaction transaction = photon.beginTransaction())
104 | {
105 | transaction.insert(photonTestTable);
106 | transaction.commit();
107 | }
108 |
109 | try(PhotonTransaction transaction = photon.beginTransaction())
110 | {
111 | PhotonTestTable photonTestTableFetched = transaction.query(PhotonTestTable.class).fetchById(2);
112 |
113 | assertEquals(photonTestTable, photonTestTableFetched);
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/photon-core/src/main/java/com/github/molcikas/photon/blueprints/AggregateBlueprint.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.blueprints;
2 |
3 | import com.github.molcikas.photon.blueprints.entity.EntityBlueprint;
4 | import com.github.molcikas.photon.blueprints.entity.FieldBlueprint;
5 | import com.github.molcikas.photon.exceptions.PhotonException;
6 | import com.github.molcikas.photon.options.PhotonOptions;
7 | import com.github.molcikas.photon.sqlbuilders.DeleteSqlBuilderService;
8 | import com.github.molcikas.photon.sqlbuilders.InsertSqlBuilderService;
9 | import com.github.molcikas.photon.sqlbuilders.SelectSqlBuilderService;
10 | import com.github.molcikas.photon.sqlbuilders.UpdateSqlBuilderService;
11 | import lombok.AllArgsConstructor;
12 | import lombok.EqualsAndHashCode;
13 | import lombok.Getter;
14 | import org.apache.commons.lang3.StringUtils;
15 |
16 | import java.util.*;
17 | import java.util.stream.Collectors;
18 |
19 | public class AggregateBlueprint
20 | {
21 | @AllArgsConstructor
22 | @Getter
23 | @EqualsAndHashCode
24 | private class EntityBlueprintKey
25 | {
26 | private final String fieldPath;
27 | private final int order;
28 | }
29 |
30 | private final EntityBlueprint aggregateRootEntityBlueprint;
31 | private final Map entityBlueprints;
32 |
33 | public EntityBlueprint getAggregateRootEntityBlueprint()
34 | {
35 | return aggregateRootEntityBlueprint;
36 | }
37 |
38 | public Class getAggregateRootClass()
39 | {
40 | return aggregateRootEntityBlueprint.getEntityClass();
41 | }
42 |
43 | public AggregateBlueprint(
44 | EntityBlueprint aggregateRootEntityBlueprint,
45 | PhotonOptions photonOptions)
46 | {
47 | if(aggregateRootEntityBlueprint == null)
48 | {
49 | throw new PhotonException("Aggregate root entity cannot be null.");
50 | }
51 |
52 | this.entityBlueprints = new HashMap<>();
53 | findAllEntityBlueprintsRecursive(aggregateRootEntityBlueprint, "", new Integer[] {0});
54 |
55 | this.aggregateRootEntityBlueprint = aggregateRootEntityBlueprint;
56 | SelectSqlBuilderService.buildSelectSqlTemplates(aggregateRootEntityBlueprint, photonOptions);
57 | UpdateSqlBuilderService.buildUpdateSqlTemplates(aggregateRootEntityBlueprint, photonOptions);
58 | InsertSqlBuilderService.buildInsertSqlTemplates(aggregateRootEntityBlueprint, photonOptions);
59 | DeleteSqlBuilderService.buildDeleteSqlTemplates(aggregateRootEntityBlueprint, photonOptions);
60 | }
61 |
62 | public List getEntityBlueprints(List excludedFieldPaths)
63 | {
64 | List fieldPaths = entityBlueprints
65 | .keySet()
66 | .stream()
67 | .map(EntityBlueprintKey::getFieldPath)
68 | .collect(Collectors.toList());
69 |
70 | Optional missingPath = excludedFieldPaths
71 | .stream()
72 | .filter(f -> !fieldPaths.contains(f))
73 | .findFirst();
74 | if(missingPath.isPresent())
75 | {
76 | throw new PhotonException(
77 | "The field path '%s' does not exist for '%s'.",
78 | missingPath.get(),
79 | aggregateRootEntityBlueprint.getEntityClassName()
80 | );
81 | }
82 |
83 | return entityBlueprints
84 | .entrySet()
85 | .stream()
86 | .filter(e -> !excludedFieldPaths.contains(e.getKey().getFieldPath()))
87 | .sorted(Comparator.comparingInt(e -> e.getKey().getOrder()))
88 | .map(Map.Entry::getValue)
89 | .collect(Collectors.toList());
90 | }
91 |
92 | /**
93 | * Pass the order inside an array so that it gets passed by reference instead of by value.
94 | *
95 | * @param entityBlueprint
96 | * @param fieldPath
97 | * @param order
98 | */
99 | private void findAllEntityBlueprintsRecursive(EntityBlueprint entityBlueprint, String fieldPath, Integer[] order)
100 | {
101 | entityBlueprints.put(new EntityBlueprintKey(fieldPath, order[0]), entityBlueprint);
102 | order[0]++;
103 | for(FieldBlueprint fieldBlueprint : entityBlueprint.getFieldsWithChildEntities())
104 | {
105 | String childFieldPath = fieldPath + (StringUtils.isEmpty(fieldPath) ? "" : ".") + fieldBlueprint.getFieldName();
106 | findAllEntityBlueprintsRecursive(fieldBlueprint.getChildEntityBlueprint(), childFieldPath, order);
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/unit/h2/myonetomanytable/MyOneToManyTableDbSetup.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.unit.h2.myonetomanytable;
2 |
3 | import com.github.molcikas.photon.Photon;
4 | import com.github.molcikas.photon.PhotonTransaction;
5 | import com.github.molcikas.photon.tests.unit.h2.H2TestUtil;
6 |
7 | public class MyOneToManyTableDbSetup
8 | {
9 | public static Photon setupDatabase()
10 | {
11 | Photon photon = new Photon(H2TestUtil.h2Url, H2TestUtil.h2User, H2TestUtil.h2Password);
12 |
13 | try(PhotonTransaction transaction = photon.beginTransaction())
14 | {
15 | transaction.query("DROP TABLE IF EXISTS `myonetomanytable`").executeUpdate();
16 | transaction.query("CREATE TABLE `myonetomanytable` (\n" +
17 | " `id` int(11) NOT NULL AUTO_INCREMENT,\n" +
18 | " `myvalue` varchar(255) DEFAULT 'oops',\n" +
19 | " PRIMARY KEY (`id`)\n" +
20 | ")").executeUpdate();
21 | transaction.query("insert into `myonetomanytable` (`id`, `myvalue`) values (1, 'my1dbvalue')").executeUpdate();
22 | transaction.query("insert into `myonetomanytable` (`id`, `myvalue`) values (2, 'my2dbvalue')").executeUpdate();
23 | transaction.query("insert into `myonetomanytable` (`id`, `myvalue`) values (3, 'my3dbvalue')").executeUpdate();
24 | transaction.query("insert into `myonetomanytable` (`id`, `myvalue`) values (4, 'my4dbvalue')").executeUpdate();
25 | transaction.query("insert into `myonetomanytable` (`id`, `myvalue`) values (5, 'my5dbvalue')").executeUpdate();
26 | transaction.query("insert into `myonetomanytable` (`id`, `myvalue`) values (6, 'my6dbvalue')").executeUpdate();
27 |
28 | transaction.query("DROP TABLE IF EXISTS `mymanytable`").executeUpdate();
29 | transaction.query("CREATE TABLE `mymanytable` (\n" +
30 | " `id` int(11) NOT NULL AUTO_INCREMENT,\n" +
31 | " `parent` int(11) NOT NULL,\n" +
32 | " `myothervalue` varchar(255) DEFAULT 'oops',\n" +
33 | " PRIMARY KEY (`id`),\n" +
34 | " CONSTRAINT `MyOneToManyTable_MyManyTable` FOREIGN KEY (`parent`) REFERENCES `myonetomanytable` (`id`)\n" +
35 | ")").executeUpdate();
36 | transaction.query("insert into `mymanytable` (`id`, `parent`, `myothervalue`) values (1, 3, 'my31otherdbvalue')").executeUpdate();
37 | transaction.query("insert into `mymanytable` (`id`, `parent`, `myothervalue`) values (2, 4, 'my41otherdbvalue')").executeUpdate();
38 | transaction.query("insert into `mymanytable` (`id`, `parent`, `myothervalue`) values (3, 4, 'my42otherdbvalue')").executeUpdate();
39 | transaction.query("insert into `mymanytable` (`id`, `parent`, `myothervalue`) values (4, 5, 'my51otherdbvalue')").executeUpdate();
40 | transaction.query("insert into `mymanytable` (`id`, `parent`, `myothervalue`) values (5, 5, 'my52otherdbvalue')").executeUpdate();
41 | transaction.query("insert into `mymanytable` (`id`, `parent`, `myothervalue`) values (6, 5, 'my53otherdbvalue')").executeUpdate();
42 | transaction.query("insert into `mymanytable` (`id`, `parent`, `myothervalue`) values (7, 6, 'my61otherdbvalue')").executeUpdate();
43 | transaction.query("insert into `mymanytable` (`id`, `parent`, `myothervalue`) values (8, 6, 'my62otherdbvalue')").executeUpdate();
44 | transaction.query("insert into `mymanytable` (`id`, `parent`, `myothervalue`) values (9, 6, 'my63otherdbvalue')").executeUpdate();
45 |
46 | transaction.query("DROP TABLE IF EXISTS `mythirdtable`").executeUpdate();
47 | transaction.query("CREATE TABLE `mythirdtable` (\n" +
48 | " `id` int(11) NOT NULL AUTO_INCREMENT,\n" +
49 | " `parent` int(11) NOT NULL,\n" +
50 | " `val` varchar(255) DEFAULT 'oops',\n" +
51 | " PRIMARY KEY (`id`),\n" +
52 | " CONSTRAINT `MyManyTable_MyThirdTable` FOREIGN KEY (`parent`) REFERENCES `mymanytable` (`id`)\n" +
53 | ")").executeUpdate();
54 | transaction.query("insert into `mythirdtable` (`id`, `parent`, `val`) values (1, 7, 'thirdtableval1')").executeUpdate();
55 | transaction.query("insert into `mythirdtable` (`id`, `parent`, `val`) values (2, 8, 'thirdtableval2')").executeUpdate();
56 | transaction.query("insert into `mythirdtable` (`id`, `parent`, `val`) values (3, 9, 'thirdtableval3')").executeUpdate();
57 | transaction.query("insert into `mythirdtable` (`id`, `parent`, `val`) values (4, 9, 'thirdtableval4')").executeUpdate();
58 |
59 | transaction.commit();
60 | }
61 |
62 | return photon;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/photon-core/src/test/java/com/github/molcikas/photon/tests/integration/OracleIntegrationTest.java:
--------------------------------------------------------------------------------
1 | package com.github.molcikas.photon.tests.integration;
2 |
3 | import com.github.molcikas.photon.Photon;
4 | import com.github.molcikas.photon.PhotonTransaction;
5 | import com.github.molcikas.photon.options.PhotonOptions;
6 | import org.junit.Before;
7 | import org.junit.Ignore;
8 | import org.junit.Test;
9 |
10 | import java.time.Instant;
11 | import java.time.ZoneId;
12 | import java.time.ZonedDateTime;
13 | import java.util.Date;
14 | import java.util.TimeZone;
15 | import java.util.UUID;
16 |
17 | import static org.junit.Assert.assertEquals;
18 | import static org.junit.Assert.assertNotNull;
19 |
20 | /**
21 | * For running these tests, you will need to download and install the Oracle JDBC driver (ojdbc6.jar) to src/test/libs.
22 | */
23 | @Ignore("The oracle docker container takes a very long time to start, so only run this test manually.")
24 | public class OracleIntegrationTest
25 | {
26 | private Photon photon;
27 |
28 | @Before
29 | public void setup()
30 | {
31 | String url = "jdbc:oracle:thin:@localhost:11521:xe";
32 | photon = new Photon(url, "system", "oracle", PhotonOptions.oracleOptions().build());
33 |
34 | photon
35 | .registerAggregate(PhotonTestTable.class)
36 | .withId("id")
37 | .withPrimaryKeyAutoIncrement()
38 | .register();
39 |
40 | try(PhotonTransaction transaction = photon.beginTransaction())
41 | {
42 | transaction.query(
43 | "BEGIN\n" +
44 | " EXECUTE IMMEDIATE 'DROP TABLE PHOTONTESTTABLE';\n" +
45 | "EXCEPTION\n" +
46 | " WHEN OTHERS THEN\n" +
47 | " IF SQLCODE != -942 THEN\n" +
48 | " RAISE;\n" +
49 | " END IF;\n" +
50 | "END;"
51 | ).executeInsert();
52 |
53 | transaction.query(
54 | "CREATE TABLE PHOTONTESTTABLE( " +
55 | "ID NUMBER GENERATED AS IDENTITY, " +
56 | "UUIDCOLUMN RAW(16) NOT NULL, " +
57 | "DATECOLUMN DATE NOT NULL, " +
58 | "VARCHARCOLUMN VARCHAR2(50) NOT NULL, " +
59 | "CONSTRAINT PHOTONTESTTABLE_PK PRIMARY KEY (ID) " +
60 | ")"
61 | ).executeInsert();
62 |
63 | transaction.query("INSERT INTO PhotonTestTable VALUES (DEFAULT, '8ED1E1BD253E4469B4CB71E1217825B7', DATE '1970-01-01' + 1489915698/24/60/60, 'Test String')").executeInsert();
64 |
65 | transaction.commit();
66 | }
67 | }
68 |
69 | // TODO: This test fails if run during standard time instead of DST. The time is off by one hour.
70 | @Test
71 | public void fetchExistingAggregateById_populatesValues()
72 | {
73 | try(PhotonTransaction transaction = photon.beginTransaction())
74 | {
75 | PhotonTestTable photonTestTable = transaction.query(PhotonTestTable.class).fetchById(1);
76 |
77 | // The database does not store a time zone, so we assume the date is in the system's time zone. But to make these tests
78 | // compare epoch times but still work with any system time zone, we have to offset the epoch to the system's time zone.
79 | int currentUtcOffset = TimeZone.getDefault().getOffset(new Date().getTime());
80 |
81 | assertNotNull(photonTestTable);
82 | assertEquals(1, photonTestTable.getId());
83 | assertEquals(UUID.fromString("8ED1E1BD-253E-4469-B4CB-71E1217825B7"), photonTestTable.getUuidColumn());
84 | assertEquals(ZonedDateTime.ofInstant(Instant.ofEpochMilli(1489915698000L - currentUtcOffset), ZoneId.systemDefault()), photonTestTable.getDateColumn());
85 | assertEquals("Test String", photonTestTable.getVarcharColumn());
86 | }
87 | }
88 |
89 | @Test
90 | public void insertAggregateAndFetch_insertsAggregateAndPopulatesValues()
91 | {
92 | PhotonTestTable photonTestTable = new PhotonTestTable(
93 | null,
94 | UUID.fromString("11111111-2222-3333-4444-555555555555"),
95 | ZonedDateTime.ofInstant(Instant.ofEpochSecond(1493493022), ZoneId.systemDefault()),
96 | "My Test String"
97 | );
98 |
99 | try(PhotonTransaction transaction = photon.beginTransaction())
100 | {
101 | transaction.insert(photonTestTable);
102 | transaction.commit();
103 | }
104 |
105 | try(PhotonTransaction transaction = photon.beginTransaction())
106 | {
107 | PhotonTestTable photonTestTableFetched = transaction.query(PhotonTestTable.class).fetchById(2);
108 |
109 | assertEquals(photonTestTable, photonTestTableFetched);
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------