CAY-2116 Split schema synchronization code in a separate module
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/2f7b1d53 Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/2f7b1d53 Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/2f7b1d53 Branch: refs/heads/master Commit: 2f7b1d53388883a2e341e9156bddd8d694f7ef88 Parents: 8c7bec0 Author: Andrus Adamchik <and...@objectstyle.com> Authored: Thu Sep 29 20:33:54 2016 +0300 Committer: Andrus Adamchik <and...@objectstyle.com> Committed: Thu Sep 29 20:38:29 2016 +0300 ---------------------------------------------------------------------- assembly/pom.xml | 6 + .../resources/assemblies/assembly-generic.xml | 1 + .../main/resources/assemblies/assembly-mac.xml | 1 + .../resources/assemblies/assembly-windows.xml | 1 + cayenne-dbsync/pom.xml | 117 +++ .../cayenne/dbsync/CayenneDbSyncModule.java | 82 ++ .../cayenne/dbsync/merge/AbstractToDbToken.java | 126 +++ .../dbsync/merge/AbstractToModelToken.java | 122 +++ .../cayenne/dbsync/merge/AddColumnToDb.java | 65 ++ .../cayenne/dbsync/merge/AddColumnToModel.java | 55 ++ .../dbsync/merge/AddRelationshipToDb.java | 87 ++ .../dbsync/merge/AddRelationshipToModel.java | 95 +++ .../cayenne/dbsync/merge/CreateTableToDb.java | 66 ++ .../dbsync/merge/CreateTableToModel.java | 102 +++ .../apache/cayenne/dbsync/merge/DbMerger.java | 405 +++++++++ .../cayenne/dbsync/merge/DbMergerConfig.java | 63 ++ .../dbsync/merge/DefaultModelMergeDelegate.java | 89 ++ .../merge/DefaultValueForNullProvider.java | 62 ++ .../cayenne/dbsync/merge/DropColumnToDb.java | 52 ++ .../cayenne/dbsync/merge/DropColumnToModel.java | 75 ++ .../dbsync/merge/DropRelationshipToDb.java | 72 ++ .../dbsync/merge/DropRelationshipToModel.java | 51 ++ .../cayenne/dbsync/merge/DropTableToDb.java | 50 ++ .../cayenne/dbsync/merge/DropTableToModel.java | 49 ++ .../cayenne/dbsync/merge/DummyReverseToken.java | 60 ++ .../dbsync/merge/EmptyValueForNullProvider.java | 40 + .../dbsync/merge/EntityMergeSupport.java | 522 ++++++++++++ .../cayenne/dbsync/merge/MergeDirection.java | 70 ++ .../cayenne/dbsync/merge/MergerContext.java | 118 +++ .../cayenne/dbsync/merge/MergerToken.java | 53 ++ .../dbsync/merge/ModelMergeDelegate.java | 62 ++ .../dbsync/merge/ProxyModelMergeDelegate.java | 108 +++ .../cayenne/dbsync/merge/SetAllowNullToDb.java | 57 ++ .../dbsync/merge/SetAllowNullToModel.java | 43 + .../cayenne/dbsync/merge/SetColumnTypeToDb.java | 119 +++ .../dbsync/merge/SetColumnTypeToModel.java | 102 +++ .../cayenne/dbsync/merge/SetNotNullToDb.java | 51 ++ .../cayenne/dbsync/merge/SetNotNullToModel.java | 43 + .../cayenne/dbsync/merge/SetPrimaryKeyToDb.java | 86 ++ .../dbsync/merge/SetPrimaryKeyToModel.java | 78 ++ .../dbsync/merge/SetValueForNullToDb.java | 47 ++ .../dbsync/merge/ValueForNullProvider.java | 42 + .../merge/factory/DB2MergerTokenFactory.java | 47 ++ .../factory/DefaultMergerTokenFactory.java | 181 ++++ .../merge/factory/DerbyMergerTokenFactory.java | 85 ++ .../factory/FirebirdMergerTokenFactory.java | 91 ++ .../merge/factory/H2MergerTokenFactory.java | 82 ++ .../merge/factory/HSQLMergerTokenFactory.java | 82 ++ .../merge/factory/IngresMergerTokenFactory.java | 224 +++++ .../merge/factory/MergerTokenFactory.java | 88 ++ .../factory/MergerTokenFactoryProvider.java | 36 + .../merge/factory/MySQLMergerTokenFactory.java | 156 ++++ .../factory/OpenBaseMergerTokenFactory.java | 143 ++++ .../merge/factory/OracleMergerTokenFactory.java | 111 +++ .../factory/PostgresMergerTokenFactory.java | 49 ++ .../factory/SQLServerMergerTokenFactory.java | 112 +++ .../merge/factory/SybaseMergerTokenFactory.java | 166 ++++ .../dbsync/reverse/DbAttributesBaseLoader.java | 107 +++ .../dbsync/reverse/DbAttributesLoader.java | 43 + .../reverse/DbAttributesPerSchemaLoader.java | 130 +++ .../apache/cayenne/dbsync/reverse/DbLoader.java | 829 ++++++++++++++++++ .../dbsync/reverse/DbLoaderConfiguration.java | 150 ++++ .../dbsync/reverse/DbLoaderDelegate.java | 58 ++ .../cayenne/dbsync/reverse/DbTableLoader.java | 195 +++++ .../dbsync/reverse/DefaultDbLoaderDelegate.java | 59 ++ .../dbsync/reverse/FiltersConfigBuilder.java | 393 +++++++++ .../dbsync/reverse/LoggingDbLoaderDelegate.java | 76 ++ .../reverse/ManyToManyCandidateEntity.java | 142 ++++ .../cayenne/dbsync/reverse/NameFilter.java | 27 + .../dbsync/reverse/NamePatternMatcher.java | 225 +++++ .../dbsync/reverse/filters/CatalogFilter.java | 62 ++ .../dbsync/reverse/filters/FiltersConfig.java | 81 ++ .../reverse/filters/IncludeTableFilter.java | 71 ++ .../filters/LegacyFilterConfigBridge.java | 150 ++++ .../dbsync/reverse/filters/PatternFilter.java | 169 ++++ .../dbsync/reverse/filters/SchemaFilter.java | 44 + .../dbsync/reverse/filters/TableFilter.java | 133 +++ .../cayenne/dbsync/reverse/mapper/DbType.java | 194 +++++ .../mapper/DefaultJdbc2JavaTypeMapper.java | 282 +++++++ .../reverse/mapper/Jdbc2JavaTypeMapper.java | 33 + .../dbsync/merge/AddColumnToModelIT.java | 98 +++ .../dbsync/merge/CreateTableToModelIT.java | 97 +++ .../cayenne/dbsync/merge/DbMergerTest.java | 261 ++++++ .../dbsync/merge/DropColumnToModelIT.java | 235 ++++++ .../dbsync/merge/DropRelationshipToModelIT.java | 188 +++++ .../dbsync/merge/DropTableToModelIT.java | 94 +++ .../dbsync/merge/EntityMergeSupportIT.java | 102 +++ .../apache/cayenne/dbsync/merge/MergeCase.java | 209 +++++ .../cayenne/dbsync/merge/MergerFactoryIT.java | 310 +++++++ .../dbsync/merge/SetAllowNullToDbIT.java | 66 ++ .../cayenne/dbsync/merge/SetNotNullToDbIT.java | 62 ++ .../dbsync/merge/SetPrimaryKeyToDbIT.java | 58 ++ .../cayenne/dbsync/merge/TokensReversTest.java | 89 ++ .../merge/TokensToModelExecutionTest.java | 80 ++ .../cayenne/dbsync/merge/ValueForNullIT.java | 127 +++ .../cayenne/dbsync/merge/builders/Builder.java | 38 + .../dbsync/merge/builders/DataMapBuilder.java | 128 +++ .../merge/builders/DbAttributeBuilder.java | 115 +++ .../dbsync/merge/builders/DbEntityBuilder.java | 90 ++ .../merge/builders/DbRelationshipBuilder.java | 85 ++ .../dbsync/merge/builders/DefaultBuilder.java | 57 ++ .../merge/builders/ObjAttributeBuilder.java | 67 ++ .../dbsync/merge/builders/ObjEntityBuilder.java | 98 +++ .../dbsync/merge/builders/ObjectMother.java | 70 ++ .../cayenne/dbsync/reverse/DbLoaderIT.java | 430 ++++++++++ .../dbsync/reverse/DbLoaderPartialIT.java | 116 +++ .../reverse/FiltersConfigBuilderTest.java | 391 +++++++++ .../reverse/ManyToManyCandidateEntityTest.java | 113 +++ .../reverse/filters/FiltersConfigTest.java | 93 +++ .../reverse/filters/IncludeFilterTest.java | 34 + .../reverse/filters/PatternFilterTest.java | 78 ++ .../dbsync/reverse/filters/TableFilterTest.java | 91 ++ .../dbsync/reverse/mapper/DbTypeTest.java | 87 ++ .../apache/cayenne/dbsync/unit/DbSyncCase.java | 57 ++ .../cayenne/dbsync/unit/DbSyncCaseModule.java | 38 + .../unit/DbSyncServerRuntimeProvider.java | 50 ++ .../cayenne-relationship-optimisation.xml | 4 + .../reverse/relationship-optimisation.map.xml | 43 + .../org/apache/cayenne/access/DbLoader.java | 832 ------------------- .../apache/cayenne/access/DbLoaderDelegate.java | 58 -- .../access/loader/DbAttributesBaseLoader.java | 107 --- .../access/loader/DbAttributesLoader.java | 43 - .../loader/DbAttributesPerSchemaLoader.java | 130 --- .../access/loader/DbLoaderConfiguration.java | 150 ---- .../cayenne/access/loader/DbTableLoader.java | 196 ----- .../access/loader/DefaultDbLoaderDelegate.java | 60 -- .../access/loader/LoggingDbLoaderDelegate.java | 76 -- .../loader/ManyToManyCandidateEntity.java | 142 ---- .../cayenne/access/loader/NameFilter.java | 27 - .../access/loader/NamePatternMatcher.java | 225 ----- .../access/loader/filters/CatalogFilter.java | 62 -- .../access/loader/filters/FiltersConfig.java | 81 -- .../loader/filters/IncludeTableFilter.java | 71 -- .../filters/LegacyFilterConfigBridge.java | 150 ---- .../access/loader/filters/PatternFilter.java | 169 ---- .../access/loader/filters/SchemaFilter.java | 44 - .../access/loader/filters/TableFilter.java | 133 --- .../cayenne/access/loader/mapper/DbType.java | 194 ----- .../mapper/DefaultJdbc2JavaTypeMapper.java | 282 ------- .../loader/mapper/Jdbc2JavaTypeMapper.java | 33 - .../org/apache/cayenne/dba/AutoAdapter.java | 6 - .../java/org/apache/cayenne/dba/DbAdapter.java | 6 - .../org/apache/cayenne/dba/JdbcAdapter.java | 14 +- .../apache/cayenne/dba/PerAdapterProvider.java | 46 + .../org/apache/cayenne/dba/db2/DB2Adapter.java | 13 +- .../cayenne/dba/db2/DB2MergerFactory.java | 49 -- .../apache/cayenne/dba/derby/DerbyAdapter.java | 36 +- .../cayenne/dba/derby/DerbyMergerFactory.java | 86 -- .../cayenne/dba/firebird/FirebirdAdapter.java | 6 +- .../dba/firebird/FirebirdMergerFactory.java | 91 -- .../org/apache/cayenne/dba/h2/H2Adapter.java | 6 - .../apache/cayenne/dba/h2/H2MergerFactory.java | 83 -- .../cayenne/dba/hsqldb/HSQLDBAdapter.java | 7 - .../cayenne/dba/hsqldb/HSQLMergerFactory.java | 83 -- .../cayenne/dba/ingres/IngresAdapter.java | 6 - .../cayenne/dba/ingres/IngresMergerFactory.java | 226 ----- .../apache/cayenne/dba/mysql/MySQLAdapter.java | 26 +- .../cayenne/dba/mysql/MySQLMergerFactory.java | 157 ---- .../cayenne/dba/openbase/OpenBaseAdapter.java | 469 +++++------ .../dba/openbase/OpenBaseMergerFactory.java | 144 ---- .../cayenne/dba/oracle/OracleAdapter.java | 25 +- .../cayenne/dba/oracle/OracleMergerFactory.java | 112 --- .../cayenne/dba/postgres/PostgresAdapter.java | 6 - .../dba/postgres/PostgresMergerFactory.java | 50 -- .../cayenne/dba/sqlserver/SQLServerAdapter.java | 6 - .../dba/sqlserver/SQLServerMergerFactory.java | 114 --- .../cayenne/dba/sybase/SybaseAdapter.java | 14 +- .../cayenne/dba/sybase/SybaseMergerFactory.java | 167 ---- .../cayenne/dbimport/FiltersConfigBuilder.java | 383 --------- .../apache/cayenne/merge/AbstractToDbToken.java | 126 --- .../cayenne/merge/AbstractToModelToken.java | 122 --- .../org/apache/cayenne/merge/AddColumnToDb.java | 64 -- .../apache/cayenne/merge/AddColumnToModel.java | 55 -- .../cayenne/merge/AddRelationshipToDb.java | 86 -- .../cayenne/merge/AddRelationshipToModel.java | 95 --- .../apache/cayenne/merge/CreateTableToDb.java | 65 -- .../cayenne/merge/CreateTableToModel.java | 102 --- .../java/org/apache/cayenne/merge/DbMerger.java | 405 --------- .../apache/cayenne/merge/DbMergerConfig.java | 63 -- .../merge/DefaultModelMergeDelegate.java | 89 -- .../merge/DefaultValueForNullProvider.java | 62 -- .../apache/cayenne/merge/DropColumnToDb.java | 51 -- .../apache/cayenne/merge/DropColumnToModel.java | 74 -- .../cayenne/merge/DropRelationshipToDb.java | 71 -- .../cayenne/merge/DropRelationshipToModel.java | 50 -- .../org/apache/cayenne/merge/DropTableToDb.java | 49 -- .../apache/cayenne/merge/DropTableToModel.java | 48 -- .../apache/cayenne/merge/DummyReverseToken.java | 58 -- .../merge/EmptyValueForNullProvider.java | 40 - .../apache/cayenne/merge/MergeDirection.java | 70 -- .../org/apache/cayenne/merge/MergerContext.java | 118 --- .../org/apache/cayenne/merge/MergerFactory.java | 142 ---- .../org/apache/cayenne/merge/MergerToken.java | 51 -- .../cayenne/merge/ModelMergeDelegate.java | 65 -- .../cayenne/merge/ProxyModelMergeDelegate.java | 108 --- .../apache/cayenne/merge/SetAllowNullToDb.java | 56 -- .../cayenne/merge/SetAllowNullToModel.java | 42 - .../apache/cayenne/merge/SetColumnTypeToDb.java | 119 --- .../cayenne/merge/SetColumnTypeToModel.java | 101 --- .../apache/cayenne/merge/SetNotNullToDb.java | 50 -- .../apache/cayenne/merge/SetNotNullToModel.java | 42 - .../apache/cayenne/merge/SetPrimaryKeyToDb.java | 85 -- .../cayenne/merge/SetPrimaryKeyToModel.java | 77 -- .../cayenne/merge/SetValueForNullToDb.java | 46 - .../cayenne/merge/ValueForNullProvider.java | 42 - .../apache/cayenne/util/EntityMergeSupport.java | 520 ------------ .../org/apache/cayenne/access/DbLoaderIT.java | 431 ---------- .../cayenne/access/DbLoaderPartialIT.java | 118 --- .../loader/ManyToManyCandidateEntityTest.java | 113 --- .../loader/filters/FiltersConfigTest.java | 93 --- .../loader/filters/IncludeFilterTest.java | 34 - .../loader/filters/PatternFilterTest.java | 78 -- .../access/loader/filters/TableFilterTest.java | 91 -- .../access/loader/mapper/DbTypeTest.java | 87 -- .../cayenne/dba/PerAdapterProviderTest.java | 83 ++ .../dbimport/FiltersConfigBuilderTest.java | 382 --------- .../cayenne/merge/AddColumnToModelIT.java | 98 --- .../cayenne/merge/CreateTableToModelIT.java | 97 --- .../org/apache/cayenne/merge/DbMergerTest.java | 261 ------ .../cayenne/merge/DropColumnToModelIT.java | 235 ------ .../merge/DropRelationshipToModelIT.java | 188 ----- .../cayenne/merge/DropTableToModelIT.java | 94 --- .../org/apache/cayenne/merge/MergeCase.java | 215 ----- .../apache/cayenne/merge/MergerFactoryIT.java | 310 ------- .../cayenne/merge/SetAllowNullToDbIT.java | 66 -- .../apache/cayenne/merge/SetNotNullToDbIT.java | 62 -- .../cayenne/merge/SetPrimaryKeyToDbIT.java | 58 -- .../apache/cayenne/merge/TokensReversTest.java | 88 -- .../cayenne/merge/TokensToModelExecution.java | 83 -- .../apache/cayenne/merge/ValueForNullIT.java | 128 --- .../apache/cayenne/merge/builders/Builder.java | 38 - .../cayenne/merge/builders/DataMapBuilder.java | 128 --- .../merge/builders/DbAttributeBuilder.java | 115 --- .../cayenne/merge/builders/DbEntityBuilder.java | 90 -- .../merge/builders/DbRelationshipBuilder.java | 85 -- .../cayenne/merge/builders/DefaultBuilder.java | 57 -- .../merge/builders/ObjAttributeBuilder.java | 67 -- .../merge/builders/ObjEntityBuilder.java | 98 --- .../cayenne/merge/builders/ObjectMother.java | 70 -- .../unit/di/server/ServerRuntimeProvider.java | 13 +- .../cayenne/util/EntityMergeSupportIT.java | 103 --- .../cayenne-relationship-optimisation.xml | 4 - .../loader/relationship-optimisation.map.xml | 43 - cayenne-tools/pom.xml | 14 + .../cayenne/gen/ClassGenerationAction.java | 22 +- .../cayenne/tools/AntDataPortDelegate.java | 10 +- .../CayenneGeneratorEntityFilterAction.java | 10 +- .../cayenne/tools/CayenneGeneratorTask.java | 2 +- .../apache/cayenne/tools/DbGeneratorTask.java | 9 +- .../apache/cayenne/tools/DbImporterTask.java | 9 +- .../tools/dbimport/DbImportConfiguration.java | 27 +- .../dbimport/DbImportDbLoaderDelegate.java | 2 +- .../cayenne/tools/dbimport/DbImportModule.java | 4 +- .../tools/dbimport/DefaultDbImportAction.java | 34 +- .../config/DefaultTypeMapperBuilder.java | 6 +- .../cayenne/tools/NamePatternMatcherTest.java | 8 +- .../tools/dbimport/DbImportModuleTest.java | 3 +- .../dbimport/DefaultDbImportActionTest.java | 40 +- docs/doc/src/main/resources/RELEASE-NOTES.txt | 1 + .../java/org/apache/cayenne/modeler/Main.java | 17 +- .../modeler/action/CreateObjEntityAction.java | 6 +- .../modeler/action/DbEntitySyncAction.java | 8 +- .../cayenne/modeler/action/MigrateAction.java | 72 +- .../modeler/action/ObjEntitySyncAction.java | 13 +- .../modeler/dialog/db/DbLoaderHelper.java | 16 +- .../modeler/dialog/db/MergerOptions.java | 67 +- .../db/MergerTokenSelectorController.java | 40 +- .../dialog/db/MergerTokenTableModel.java | 6 +- .../dialog/db/ModelerDbImportAction.java | 6 +- .../dialog/db/ReverseEngineeringController.java | 17 +- .../dialog/objentity/EntitySyncController.java | 15 +- .../cayenne/tools/CayenneGeneratorMojo.java | 2 +- .../apache/cayenne/tools/DbGeneratorMojo.java | 9 +- .../apache/cayenne/tools/DbImporterMojo.java | 21 +- .../tools/DbImporterMojoConfigurationTest.java | 8 +- pom.xml | 1 + 276 files changed, 14069 insertions(+), 13421 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/assembly/pom.xml ---------------------------------------------------------------------- diff --git a/assembly/pom.xml b/assembly/pom.xml index 6b7f423..45c042e 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -50,6 +50,12 @@ <dependency> <groupId>org.apache.cayenne</groupId> + <artifactId>cayenne-dbsync</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.apache.cayenne</groupId> <artifactId>cayenne-tools</artifactId> <version>${project.version}</version> </dependency> http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/assembly/src/main/resources/assemblies/assembly-generic.xml ---------------------------------------------------------------------- diff --git a/assembly/src/main/resources/assemblies/assembly-generic.xml b/assembly/src/main/resources/assemblies/assembly-generic.xml index 4414219..5eb4a62 100644 --- a/assembly/src/main/resources/assemblies/assembly-generic.xml +++ b/assembly/src/main/resources/assemblies/assembly-generic.xml @@ -80,6 +80,7 @@ <include>org.apache.cayenne:cayenne-lifecycle</include> <include>org.apache.cayenne:cayenne-project</include> <include>org.apache.cayenne:cayenne-server</include> + <include>org.apache.cayenne:cayenne-dbsync</include> <include>org.apache.cayenne:cayenne-tools</include> <include>org.apache.cayenne:cayenne-dbcp2</include> <include>org.apache.cayenne:cayenne-java8</include> http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/assembly/src/main/resources/assemblies/assembly-mac.xml ---------------------------------------------------------------------- diff --git a/assembly/src/main/resources/assemblies/assembly-mac.xml b/assembly/src/main/resources/assemblies/assembly-mac.xml index 05a4d1f..11e3546 100644 --- a/assembly/src/main/resources/assemblies/assembly-mac.xml +++ b/assembly/src/main/resources/assemblies/assembly-mac.xml @@ -80,6 +80,7 @@ <include>org.apache.cayenne:cayenne-lifecycle</include> <include>org.apache.cayenne:cayenne-project</include> <include>org.apache.cayenne:cayenne-server</include> + <include>org.apache.cayenne:cayenne-dbsync</include> <include>org.apache.cayenne:cayenne-tools</include> <include>org.apache.cayenne:cayenne-dbcp2</include> <include>org.apache.cayenne:cayenne-java8</include> http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/assembly/src/main/resources/assemblies/assembly-windows.xml ---------------------------------------------------------------------- diff --git a/assembly/src/main/resources/assemblies/assembly-windows.xml b/assembly/src/main/resources/assemblies/assembly-windows.xml index 7401a42..efa451d 100644 --- a/assembly/src/main/resources/assemblies/assembly-windows.xml +++ b/assembly/src/main/resources/assemblies/assembly-windows.xml @@ -80,6 +80,7 @@ <include>org.apache.cayenne:cayenne-lifecycle</include> <include>org.apache.cayenne:cayenne-project</include> <include>org.apache.cayenne:cayenne-server</include> + <include>org.apache.cayenne:cayenne-dbsync</include> <include>org.apache.cayenne:cayenne-tools</include> <include>org.apache.cayenne:cayenne-dbcp2</include> <include>org.apache.cayenne:cayenne-java8</include> http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/pom.xml ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/pom.xml b/cayenne-dbsync/pom.xml new file mode 100644 index 0000000..5cc1a63 --- /dev/null +++ b/cayenne-dbsync/pom.xml @@ -0,0 +1,117 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + + <parent> + <artifactId>cayenne-parent</artifactId> + <groupId>org.apache.cayenne</groupId> + <version>4.0.M4-SNAPSHOT</version> + </parent> + + <modelVersion>4.0.0</modelVersion> + + <artifactId>cayenne-dbsync</artifactId> + <packaging>jar</packaging> + <name>Cayenne Database Synchronization Tools</name> + <dependencies> + <dependency> + <groupId>org.apache.cayenne</groupId> + <artifactId>cayenne-server</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-all</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>xmlunit</groupId> + <artifactId>xmlunit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.cayenne</groupId> + <artifactId>cayenne-server</artifactId> + <version>${project.version}</version> + <scope>test</scope> + <type>test-jar</type> + </dependency> + + <dependency> + <groupId>org.apache.cayenne.build-tools</groupId> + <artifactId>cayenne-test-utilities</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <!-- This ensures LICESNE and NOTICE inclusion in all jars --> + <plugin> + <artifactId>maven-remote-resources-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>process</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-jar-plugin</artifactId> + <!-- include OSGi stuff --> + <configuration> + <archive> + <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile> + </archive> + </configuration> + <!-- share tests with downstream modules --> + <executions> + <execution> + <goals> + <goal>test-jar</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <executions> + <execution> + <id>bundle-manifest</id> + <phase>process-classes</phase> + <goals> + <goal>manifest</goal> + </goals> + <!-- TODO: export package filters. --> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/CayenneDbSyncModule.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/CayenneDbSyncModule.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/CayenneDbSyncModule.java new file mode 100644 index 0000000..4fa43b2 --- /dev/null +++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/CayenneDbSyncModule.java @@ -0,0 +1,82 @@ +package org.apache.cayenne.dbsync; + +import org.apache.cayenne.dba.db2.DB2Adapter; +import org.apache.cayenne.dba.derby.DerbyAdapter; +import org.apache.cayenne.dba.firebird.FirebirdAdapter; +import org.apache.cayenne.dba.h2.H2Adapter; +import org.apache.cayenne.dba.hsqldb.HSQLDBAdapter; +import org.apache.cayenne.dba.ingres.IngresAdapter; +import org.apache.cayenne.dba.mysql.MySQLAdapter; +import org.apache.cayenne.dba.openbase.OpenBaseAdapter; +import org.apache.cayenne.dba.oracle.Oracle8Adapter; +import org.apache.cayenne.dba.oracle.OracleAdapter; +import org.apache.cayenne.dba.postgres.PostgresAdapter; +import org.apache.cayenne.dba.sqlserver.SQLServerAdapter; +import org.apache.cayenne.dba.sybase.SybaseAdapter; +import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactoryProvider; +import org.apache.cayenne.dbsync.merge.factory.DB2MergerTokenFactory; +import org.apache.cayenne.dbsync.merge.factory.DefaultMergerTokenFactory; +import org.apache.cayenne.dbsync.merge.factory.DerbyMergerTokenFactory; +import org.apache.cayenne.dbsync.merge.factory.FirebirdMergerTokenFactory; +import org.apache.cayenne.dbsync.merge.factory.H2MergerTokenFactory; +import org.apache.cayenne.dbsync.merge.factory.HSQLMergerTokenFactory; +import org.apache.cayenne.dbsync.merge.factory.IngresMergerTokenFactory; +import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory; +import org.apache.cayenne.dbsync.merge.factory.MySQLMergerTokenFactory; +import org.apache.cayenne.dbsync.merge.factory.OpenBaseMergerTokenFactory; +import org.apache.cayenne.dbsync.merge.factory.OracleMergerTokenFactory; +import org.apache.cayenne.dbsync.merge.factory.PostgresMergerTokenFactory; +import org.apache.cayenne.dbsync.merge.factory.SQLServerMergerTokenFactory; +import org.apache.cayenne.dbsync.merge.factory.SybaseMergerTokenFactory; +import org.apache.cayenne.di.Binder; +import org.apache.cayenne.di.Module; + +/** + * @since 4.0 + */ +public class CayenneDbSyncModule implements Module { + + /** + * A DI container key for the Map<String, String> storing properties + * used by built-in Cayenne service. + */ + public static final String MERGER_FACTORIES_MAP = "cayenne.dbsync.mergerfactories"; + + @Override + public void configure(Binder binder) { + + // TODO: explicit binding before inserting into a map will be uneeded soon + binder.bind(DB2MergerTokenFactory.class).to(DB2MergerTokenFactory.class); + binder.bind(DerbyMergerTokenFactory.class).to(DerbyMergerTokenFactory.class); + binder.bind(FirebirdMergerTokenFactory.class).to(FirebirdMergerTokenFactory.class); + binder.bind(H2MergerTokenFactory.class).to(H2MergerTokenFactory.class); + binder.bind(HSQLMergerTokenFactory.class).to(HSQLMergerTokenFactory.class); + binder.bind(IngresMergerTokenFactory.class).to(IngresMergerTokenFactory.class); + binder.bind(MySQLMergerTokenFactory.class).to(MySQLMergerTokenFactory.class); + binder.bind(OpenBaseMergerTokenFactory.class).to(OpenBaseMergerTokenFactory.class); + binder.bind(OracleMergerTokenFactory.class).to(OracleMergerTokenFactory.class); + binder.bind(PostgresMergerTokenFactory.class).to(PostgresMergerTokenFactory.class); + binder.bind(SQLServerMergerTokenFactory.class).to(SQLServerMergerTokenFactory.class); + binder.bind(SybaseMergerTokenFactory.class).to(SybaseMergerTokenFactory.class); + + // default and per adapter merger factories... + binder.bind(MergerTokenFactory.class).to(DefaultMergerTokenFactory.class); + binder.bindMap(MERGER_FACTORIES_MAP) + .put(DB2Adapter.class.getName(), DB2MergerTokenFactory.class) + .put(DerbyAdapter.class.getName(), DerbyMergerTokenFactory.class) + .put(FirebirdAdapter.class.getName(), FirebirdMergerTokenFactory.class) + .put(H2Adapter.class.getName(), H2MergerTokenFactory.class) + .put(HSQLDBAdapter.class.getName(), HSQLMergerTokenFactory.class) + .put(IngresAdapter.class.getName(), IngresMergerTokenFactory.class) + .put(MySQLAdapter.class.getName(), MySQLMergerTokenFactory.class) + .put(OpenBaseAdapter.class.getName(), OpenBaseMergerTokenFactory.class) + .put(OracleAdapter.class.getName(), OracleMergerTokenFactory.class) + .put(Oracle8Adapter.class.getName(), OracleMergerTokenFactory.class) + .put(PostgresAdapter.class.getName(), PostgresMergerTokenFactory.class) + .put(SQLServerAdapter.class.getName(), SQLServerMergerTokenFactory.class) + .put(SybaseAdapter.class.getName(), SybaseMergerTokenFactory.class); + + binder.bind(MergerTokenFactoryProvider.class).to(MergerTokenFactoryProvider.class); + + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AbstractToDbToken.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AbstractToDbToken.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AbstractToDbToken.java new file mode 100644 index 0000000..1cc3092 --- /dev/null +++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AbstractToDbToken.java @@ -0,0 +1,126 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ +package org.apache.cayenne.dbsync.merge; + +import org.apache.cayenne.dba.DbAdapter; +import org.apache.cayenne.log.JdbcEventLogger; +import org.apache.cayenne.map.DbAttribute; +import org.apache.cayenne.map.DbEntity; +import org.apache.cayenne.validation.SimpleValidationFailure; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.List; + +/** + * Common abstract superclass for all {@link MergerToken}s going from the model + * to the database. + */ +public abstract class AbstractToDbToken implements MergerToken, Comparable<MergerToken> { + + private final String tokenName; + + protected AbstractToDbToken(String tokenName) { + this.tokenName = tokenName; + } + + @Override + public final String getTokenName() { + return tokenName; + } + + @Override + public final MergeDirection getDirection() { + return MergeDirection.TO_DB; + } + + @Override + public void execute(MergerContext mergerContext) { + for (String sql : createSql(mergerContext.getDataNode().getAdapter())) { + executeSql(mergerContext, sql); + } + } + + protected void executeSql(MergerContext mergerContext, String sql) { + JdbcEventLogger logger = mergerContext.getDataNode().getJdbcEventLogger(); + logger.log(sql); + + try (Connection conn = mergerContext.getDataNode().getDataSource().getConnection();) { + + try (Statement st = conn.createStatement();) { + st.execute(sql); + } + } catch (SQLException e) { + mergerContext.getValidationResult().addFailure(new SimpleValidationFailure(sql, e.getMessage())); + logger.logQueryError(e); + } + } + + @Override + public String toString() { + return getTokenName() + ' ' + getTokenValue() + ' ' + getDirection(); + } + + public abstract List<String> createSql(DbAdapter adapter); + + abstract static class Entity extends AbstractToDbToken { + + private DbEntity entity; + + public Entity(String tokenName, DbEntity entity) { + super(tokenName); + this.entity = entity; + } + + public DbEntity getEntity() { + return entity; + } + + public String getTokenValue() { + return getEntity().getName(); + } + + public int compareTo(MergerToken o) { + // default order as tokens are created + return 0; + } + + } + + abstract static class EntityAndColumn extends Entity { + + private DbAttribute column; + + public EntityAndColumn(String tokenName, DbEntity entity, DbAttribute column) { + super(tokenName, entity); + this.column = column; + } + + public DbAttribute getColumn() { + return column; + } + + @Override + public String getTokenValue() { + return getEntity().getName() + "." + getColumn().getName(); + } + + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AbstractToModelToken.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AbstractToModelToken.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AbstractToModelToken.java new file mode 100644 index 0000000..0c4b22e --- /dev/null +++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AbstractToModelToken.java @@ -0,0 +1,122 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ +package org.apache.cayenne.dbsync.merge; + +import org.apache.cayenne.map.DbAttribute; +import org.apache.cayenne.map.DbEntity; +import org.apache.cayenne.map.DbRelationship; +import org.apache.cayenne.map.ObjEntity; +import org.apache.cayenne.map.ObjRelationship; + +/** + * Common abstract superclass for all {@link MergerToken}s going from the database to the + * model. + * + */ +public abstract class AbstractToModelToken implements MergerToken { + + private final String tokenName; + + protected AbstractToModelToken(String tokenName) { + this.tokenName = tokenName; + } + + @Override + public final String getTokenName() { + return tokenName; + } + + public final MergeDirection getDirection() { + return MergeDirection.TO_MODEL; + } + + protected static void remove(ModelMergeDelegate mergerContext, DbRelationship rel, boolean reverse) { + if (rel == null) { + return; + } + if (reverse) { + remove(mergerContext, rel.getReverseRelationship(), false); + } + + DbEntity dbEntity = rel.getSourceEntity(); + for (ObjEntity objEntity : dbEntity.mappedObjEntities()) { + remove(mergerContext, objEntity.getRelationshipForDbRelationship(rel), true); + } + + rel.getSourceEntity().removeRelationship(rel.getName()); + mergerContext.dbRelationshipRemoved(rel); + } + + protected static void remove(ModelMergeDelegate mergerContext, ObjRelationship rel, boolean reverse) { + if (rel == null) { + return; + } + if (reverse) { + remove(mergerContext, rel.getReverseRelationship(), false); + } + rel.getSourceEntity().removeRelationship(rel.getName()); + mergerContext.objRelationshipRemoved(rel); + } + + @Override + public String toString() { + return getTokenName() + ' ' + getTokenValue() + ' ' + getDirection(); + } + + abstract static class Entity extends AbstractToModelToken { + + private final DbEntity entity; + + protected Entity(String tokenName, DbEntity entity) { + super(tokenName); + this.entity = entity; + } + + public DbEntity getEntity() { + return entity; + } + + public String getTokenValue() { + return getEntity().getName(); + } + + } + + abstract static class EntityAndColumn extends Entity { + + private final DbAttribute column; + + protected EntityAndColumn(String tokenName, DbEntity entity, DbAttribute column) { + super(tokenName, entity); + this.column = column; + } + + public DbAttribute getColumn() { + return column; + } + + @Override + public String getTokenValue() { + return getEntity().getName() + "." + getColumn().getName(); + } + + } + + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddColumnToDb.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddColumnToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddColumnToDb.java new file mode 100644 index 0000000..977128c --- /dev/null +++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddColumnToDb.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cayenne.dbsync.merge; + +import java.util.Collections; +import java.util.List; + +import org.apache.cayenne.dba.DbAdapter; +import org.apache.cayenne.dba.JdbcAdapter; +import org.apache.cayenne.dba.QuotingStrategy; +import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory; +import org.apache.cayenne.map.DbAttribute; +import org.apache.cayenne.map.DbEntity; + +public class AddColumnToDb extends AbstractToDbToken.EntityAndColumn { + + public AddColumnToDb(DbEntity entity, DbAttribute column) { + super("Add Column", entity, column); + } + + /** + * append the part of the token before the actual column data type + */ + protected void appendPrefix(StringBuffer sqlBuffer, QuotingStrategy context) { + + sqlBuffer.append("ALTER TABLE "); + sqlBuffer.append(context.quotedFullyQualifiedName(getEntity())); + sqlBuffer.append(" ADD COLUMN "); + sqlBuffer.append(context.quotedName(getColumn())); + sqlBuffer.append(" "); + } + + @Override + public List<String> createSql(DbAdapter adapter) { + StringBuffer sqlBuffer = new StringBuffer(); + QuotingStrategy context = adapter.getQuotingStrategy(); + appendPrefix(sqlBuffer, context); + + sqlBuffer.append(JdbcAdapter.getType(adapter, getColumn())); + sqlBuffer.append(JdbcAdapter.sizeAndPrecision(adapter, getColumn())); + + return Collections.singletonList(sqlBuffer.toString()); + } + + public MergerToken createReverse(MergerTokenFactory factory) { + return factory.createDropColumnToModel(getEntity(), getColumn()); + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddColumnToModel.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddColumnToModel.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddColumnToModel.java new file mode 100644 index 0000000..30390ca --- /dev/null +++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddColumnToModel.java @@ -0,0 +1,55 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ +package org.apache.cayenne.dbsync.merge; + +import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory; +import org.apache.cayenne.map.DbAttribute; +import org.apache.cayenne.map.DbEntity; +import org.apache.cayenne.map.ObjEntity; + +/** + * A {@link MergerToken} to add a {@link DbAttribute} to a {@link DbEntity}. The + * {@link EntityMergeSupport} will be used to update the mapped {@link ObjEntity} + * + */ +public class AddColumnToModel extends AbstractToModelToken.EntityAndColumn { + + public AddColumnToModel(DbEntity entity, DbAttribute column) { + super("Add Column", entity, column); + } + + public MergerToken createReverse(MergerTokenFactory factory) { + return factory.createDropColumnToDb(getEntity(), getColumn()); + } + + public void execute(MergerContext mergerContext) { + getEntity().addAttribute(getColumn()); + + // TODO: use EntityMergeSupport from DbImportConfiguration... otherwise we are ignoring a bunch of + // important settings + + EntityMergeSupport entityMergeSupport = new EntityMergeSupport(mergerContext.getDataMap()); + for(ObjEntity e : getEntity().mappedObjEntities()) { + entityMergeSupport.synchronizeOnDbAttributeAdded(e, getColumn()); + } + + mergerContext.getModelMergeDelegate().dbAttributeAdded(getColumn()); + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddRelationshipToDb.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddRelationshipToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddRelationshipToDb.java new file mode 100644 index 0000000..5c851a6 --- /dev/null +++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddRelationshipToDb.java @@ -0,0 +1,87 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ +package org.apache.cayenne.dbsync.merge; + +import java.util.Collections; +import java.util.List; + +import org.apache.cayenne.access.DbGenerator; +import org.apache.cayenne.dba.DbAdapter; +import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory; +import org.apache.cayenne.map.DbEntity; +import org.apache.cayenne.map.DbRelationship; + +public class AddRelationshipToDb extends AbstractToDbToken.Entity { + + private DbRelationship rel; + + public AddRelationshipToDb(DbEntity entity, DbRelationship rel) { + super("Add foreign key", entity); + this.rel = rel; + } + + /** + * @see DbGenerator#createConstraintsQueries(org.apache.cayenne.map.DbEntity) + */ + @Override + public List<String> createSql(DbAdapter adapter) { + // TODO: skip FK to a different DB + + if (this.shouldGenerateFkConstraint()) { + String fksql = adapter.createFkConstraint(rel); + if (fksql != null) { + return Collections.singletonList(fksql); + } + } + return Collections.emptyList(); + } + + public boolean shouldGenerateFkConstraint() { + return !rel.isToMany() + && rel.isToPK() // TODO it is not necessary primary key it can be unique index + && !rel.isToDependentPK(); + } + + public MergerToken createReverse(MergerTokenFactory factory) { + return factory.createDropRelationshipToModel(getEntity(), rel); + } + + @Override + public String getTokenValue() { + if (this.shouldGenerateFkConstraint()) { + return rel.getSourceEntity().getName() + "->" + rel.getTargetEntityName(); + } else { + return "Skip. No sql representation."; + } + } + + public DbRelationship getRelationship() { + return rel; + } + + @Override + public int compareTo(MergerToken o) { + // add all AddRelationshipToDb to the end. + if (o instanceof AddRelationshipToDb) { + return super.compareTo(o); + } + return 1; + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddRelationshipToModel.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddRelationshipToModel.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddRelationshipToModel.java new file mode 100644 index 0000000..f516a3e --- /dev/null +++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddRelationshipToModel.java @@ -0,0 +1,95 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ +package org.apache.cayenne.dbsync.merge; + +import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory; +import org.apache.cayenne.map.DbEntity; +import org.apache.cayenne.map.DbJoin; +import org.apache.cayenne.map.DbRelationship; +import org.apache.cayenne.map.ObjEntity; + +public class AddRelationshipToModel extends AbstractToModelToken.Entity { + + public static final String COMMA_SEPARATOR = ", "; + public static final int COMMA_SEPARATOR_LENGTH = COMMA_SEPARATOR.length(); + private DbRelationship rel; + + public AddRelationshipToModel(DbEntity entity, DbRelationship rel) { + super("Add Relationship", entity); + this.rel = rel; + } + + public MergerToken createReverse(MergerTokenFactory factory) { + return factory.createDropRelationshipToDb(getEntity(), rel); + } + + public void execute(MergerContext mergerContext) { + getEntity().addRelationship(rel); + // TODO: add reverse relationship as well if it does not exist + + // TODO: use EntityMergeSupport from DbImportConfiguration... otherwise we are ignoring a bunch of + // important settings + + EntityMergeSupport entityMergeSupport = new EntityMergeSupport(mergerContext.getDataMap()); + for(ObjEntity e : getEntity().mappedObjEntities()) { + entityMergeSupport.synchronizeOnDbRelationshipAdded(e, rel); + } + + mergerContext.getModelMergeDelegate().dbRelationshipAdded(rel); + } + + @Override + public String getTokenValue() { + String attributes = ""; + if (rel.getJoins().size() == 1) { + attributes = rel.getJoins().get(0).getTargetName(); + } else { + for (DbJoin dbJoin : rel.getJoins()) { + attributes += dbJoin.getTargetName() + COMMA_SEPARATOR; + } + + attributes = "{" + attributes.substring(0, attributes.length() - COMMA_SEPARATOR_LENGTH) + "}"; + } + + return rel.getName() + " " + rel.getSourceEntity().getName() + "->" + rel.getTargetEntityName() + "." + attributes; + } + + + public static String getTokenValue(DbRelationship rel) { + String attributes = ""; + if (rel.getJoins().size() == 1) { + attributes = rel.getJoins().get(0).getTargetName(); + } else { + for (DbJoin dbJoin : rel.getJoins()) { + attributes += dbJoin.getTargetName() + COMMA_SEPARATOR; + } + + attributes = "{" + attributes.substring(0, attributes.length() - COMMA_SEPARATOR_LENGTH) + "}"; + } + + return rel.getName() + " " + rel.getSourceEntity().getName() + "->" + rel.getTargetEntityName() + "." + attributes; + } + + public DbRelationship getRelationship() { + return rel; + } + + + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/CreateTableToDb.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/CreateTableToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/CreateTableToDb.java new file mode 100644 index 0000000..9584aaf --- /dev/null +++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/CreateTableToDb.java @@ -0,0 +1,66 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ +package org.apache.cayenne.dbsync.merge; + +import org.apache.cayenne.access.DataNode; +import org.apache.cayenne.dba.DbAdapter; +import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory; +import org.apache.cayenne.map.DbEntity; +import org.apache.cayenne.validation.SimpleValidationFailure; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class CreateTableToDb extends AbstractToDbToken.Entity { + + public CreateTableToDb(DbEntity entity) { + super("Create Table", entity); + } + + @Override + public List<String> createSql(DbAdapter adapter) { + List<String> sqls = new ArrayList<String>(); + sqls.addAll(adapter.getPkGenerator().createAutoPkStatements( + Collections.singletonList(getEntity()))); + sqls.add(adapter.createTable(getEntity())); + return sqls; + } + + @Override + public void execute(MergerContext mergerContext) { + try { + DataNode node = mergerContext.getDataNode(); + DbAdapter adapter = node.getAdapter(); + adapter.getPkGenerator().createAutoPk( + node, + Collections.singletonList(getEntity())); + executeSql(mergerContext, adapter.createTable(getEntity())); + } + catch (Exception e) { + mergerContext.getValidationResult().addFailure( + new SimpleValidationFailure(this, e.getMessage())); + } + } + + public MergerToken createReverse(MergerTokenFactory factory) { + return factory.createDropTableToModel(getEntity()); + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/CreateTableToModel.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/CreateTableToModel.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/CreateTableToModel.java new file mode 100644 index 0000000..69e18b3 --- /dev/null +++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/CreateTableToModel.java @@ -0,0 +1,102 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ +package org.apache.cayenne.dbsync.merge; + +import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory; +import org.apache.cayenne.map.DataMap; +import org.apache.cayenne.map.DbEntity; +import org.apache.cayenne.map.ObjEntity; +import org.apache.cayenne.map.naming.NameConverter; + +/** + * A {@link MergerToken} to add a {@link DbEntity} to a {@link DataMap} + * + */ +public class CreateTableToModel extends AbstractToModelToken.Entity { + + /** + * className if {@link ObjEntity} should be generated with a + * special class name. + * Setting this to <code>null</code>, because by default class name should be generated + */ + private String objEntityClassName = null; //CayenneDataObject.class.getName(); + + public CreateTableToModel(DbEntity entity) { + super("Create Table", entity); + } + + /** + * Set the {@link ObjEntity} className if {@link ObjEntity} should be generated with a + * special class name. Set to null if the {@link ObjEntity} should be created with a + * name based on {@link DataMap#getDefaultPackage()} and {@link ObjEntity#getName()} + * <p> + * The default value is <code>null</code> + */ + public void setObjEntityClassName(String n) { + objEntityClassName = n; + } + + public void execute(MergerContext mergerContext) { + DataMap map = mergerContext.getDataMap(); + map.addDbEntity(getEntity()); + + // create a ObjEntity + String objEntityName = NameConverter.underscoredToJava(getEntity().getName(), true); + // this loop will terminate even if no valid name is found + // to prevent loader from looping forever (though such case is very unlikely) + String baseName = objEntityName; + for (int i = 1; i < 1000 && map.getObjEntity(objEntityName) != null; i++) { + objEntityName = baseName + i; + } + + ObjEntity objEntity = new ObjEntity(objEntityName); + objEntity.setDbEntity(getEntity()); + + // try to find a class name for the ObjEntity + String className = objEntityClassName; + if (className == null) { + // we should generate a className based on the objEntityName + className = map.getNameWithDefaultPackage(objEntityName); + } + + objEntity.setClassName(className); + objEntity.setSuperClassName(map.getDefaultSuperclass()); + + if (map.isClientSupported()) { + objEntity.setClientClassName(map.getNameWithDefaultClientPackage(objEntity.getName())); + objEntity.setClientSuperClassName(map.getDefaultClientSuperclass()); + } + + map.addObjEntity(objEntity); + + // presumably there are no other ObjEntities pointing to this DbEntity, so syncing just this one... + + // TODO: use EntityMergeSupport from DbImportConfiguration... otherwise we are ignoring a bunch of + // important settings + new EntityMergeSupport(map).synchronizeWithDbEntity(objEntity); + + mergerContext.getModelMergeDelegate().dbEntityAdded(getEntity()); + mergerContext.getModelMergeDelegate().objEntityAdded(objEntity); + } + + public MergerToken createReverse(MergerTokenFactory factory) { + return factory.createDropTableToDb(getEntity()); + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DbMerger.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DbMerger.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DbMerger.java new file mode 100644 index 0000000..f7d4346 --- /dev/null +++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DbMerger.java @@ -0,0 +1,405 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cayenne.dbsync.merge; + +import org.apache.cayenne.CayenneRuntimeException; +import org.apache.cayenne.access.DataNode; +import org.apache.cayenne.dba.DbAdapter; +import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory; +import org.apache.cayenne.dbsync.reverse.DbLoader; +import org.apache.cayenne.dbsync.reverse.DbLoaderConfiguration; +import org.apache.cayenne.dbsync.reverse.LoggingDbLoaderDelegate; +import org.apache.cayenne.dbsync.reverse.filters.FiltersConfig; +import org.apache.cayenne.map.Attribute; +import org.apache.cayenne.map.DataMap; +import org.apache.cayenne.map.DbAttribute; +import org.apache.cayenne.map.DbEntity; +import org.apache.cayenne.map.DbJoin; +import org.apache.cayenne.map.DbRelationship; +import org.apache.cayenne.map.DetectedDbEntity; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Types; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +/** + * Traverse a {@link DataNode} and a {@link DataMap} and create a group of + * {@link MergerToken}s to alter the {@link DataNode} data store to match the + * {@link DataMap}. + * + */ +public class DbMerger { + + private static final Log LOGGER = LogFactory.getLog(DbMerger.class); + + private final MergerTokenFactory factory; + + private final ValueForNullProvider valueForNull; + + public DbMerger(MergerTokenFactory factory) { + this(factory, null); + } + + public DbMerger(MergerTokenFactory factory, ValueForNullProvider valueForNull) { + this.factory = factory; + this.valueForNull = valueForNull == null ? new EmptyValueForNullProvider() : valueForNull; + } + + /** + * Create and return a {@link List} of {@link MergerToken}s to alter the + * given {@link DataNode} to match the given {@link DataMap} + */ + public List<MergerToken> createMergeTokens(DataSource dataSource, DbAdapter adapter, DataMap existingDataMap, + DbLoaderConfiguration config) { + return createMergeTokens(existingDataMap, loadDataMapFromDb(dataSource, adapter, config), config); + } + + /** + * Create and return a {@link List} of {@link MergerToken}s to alter the + * given {@link DataNode} to match the given {@link DataMap} + */ + public List<MergerToken> createMergeTokens(DataMap existing, DataMap loadedFomDb, DbLoaderConfiguration config) { + + loadedFomDb.setQuotingSQLIdentifiers(existing.isQuotingSQLIdentifiers()); + + List<MergerToken> tokens = createMergeTokens(filter(existing, config.getFiltersConfig()), + loadedFomDb.getDbEntities(), config); + + // sort. use a custom Comparator since only toDb tokens are comparable + // by now + Collections.sort(tokens, new Comparator<MergerToken>() { + + public int compare(MergerToken o1, MergerToken o2) { + if (o1 instanceof AbstractToDbToken && o2 instanceof AbstractToDbToken) { + + return ((AbstractToDbToken) o1).compareTo(o2); + } + return 0; + } + }); + + return tokens; + } + + private Collection<DbEntity> filter(DataMap existing, FiltersConfig filtersConfig) { + Collection<DbEntity> existingFiltered = new LinkedList<DbEntity>(); + for (DbEntity entity : existing.getDbEntities()) { + if (filtersConfig.tableFilter(entity.getCatalog(), entity.getSchema()).isIncludeTable(entity.getName()) != null) { + existingFiltered.add(entity); + } + } + return existingFiltered; + } + + private DataMap loadDataMapFromDb(DataSource dataSource, DbAdapter adapter, DbLoaderConfiguration config) { + try (Connection conn = dataSource.getConnection();) { + + return new DbLoader(conn, adapter, new LoggingDbLoaderDelegate(LOGGER)).load(config); + } catch (SQLException e) { + throw new CayenneRuntimeException("Can't doLoad dataMap from db.", e); + } + } + + public List<MergerToken> createMergeTokens(Collection<DbEntity> existing, Collection<DbEntity> loadedFromDb, + DbLoaderConfiguration config) { + Collection<DbEntity> dbEntitiesToDrop = new LinkedList<DbEntity>(loadedFromDb); + + List<MergerToken> tokens = new LinkedList<MergerToken>(); + for (DbEntity dbEntity : existing) { + String tableName = dbEntity.getName(); + + // look for table + DbEntity detectedEntity = findDbEntity(loadedFromDb, tableName); + if (detectedEntity == null) { + tokens.add(factory.createCreateTableToDb(dbEntity)); + // TODO: does this work properly with createReverse? + for (DbRelationship rel : dbEntity.getRelationships()) { + tokens.add(factory.createAddRelationshipToDb(dbEntity, rel)); + } + continue; + } + + dbEntitiesToDrop.remove(detectedEntity); + + tokens.addAll(checkRelationshipsToDrop(dbEntity, detectedEntity)); + if (!config.isSkipRelationshipsLoading()) { + tokens.addAll(checkRelationshipsToAdd(dbEntity, detectedEntity)); + } + tokens.addAll(checkRows(dbEntity, detectedEntity)); + + if (!config.isSkipPrimaryKeyLoading()) { + MergerToken token = checkPrimaryKeyChange(dbEntity, detectedEntity); + if (token != null) { + tokens.add(token); + } + } + } + + // drop table + // TODO: support drop table. currently, too many tables are marked for + // drop + for (DbEntity e : dbEntitiesToDrop) { + tokens.add(factory.createDropTableToDb(e)); + for (DbRelationship relationship : e.getRelationships()) { + DbEntity detectedEntity = findDbEntity(existing, relationship.getTargetEntityName()); + if (detectedEntity != null) { + tokens.add(factory.createDropRelationshipToDb(detectedEntity, relationship.getReverseRelationship())); + } + } + } + + return tokens; + } + + private List<MergerToken> checkRows(DbEntity existing, DbEntity loadedFromDb) { + List<MergerToken> tokens = new LinkedList<MergerToken>(); + + // columns to drop + for (DbAttribute detected : loadedFromDb.getAttributes()) { + if (findDbAttribute(existing, detected.getName()) == null) { + tokens.add(factory.createDropColumnToDb(existing, detected)); + } + } + + // columns to add or modify + for (DbAttribute attr : existing.getAttributes()) { + String columnName = attr.getName().toUpperCase(); + + DbAttribute detected = findDbAttribute(loadedFromDb, columnName); + + if (detected == null) { + tokens.add(factory.createAddColumnToDb(existing, attr)); + if (attr.isMandatory()) { + if (valueForNull.hasValueFor(existing, attr)) { + tokens.add(factory.createSetValueForNullToDb(existing, attr, valueForNull)); + } + tokens.add(factory.createSetNotNullToDb(existing, attr)); + } + continue; + } + + // check for not null + if (attr.isMandatory() != detected.isMandatory()) { + if (attr.isMandatory()) { + if (valueForNull.hasValueFor(existing, attr)) { + tokens.add(factory.createSetValueForNullToDb(existing, attr, valueForNull)); + } + tokens.add(factory.createSetNotNullToDb(existing, attr)); + } else { + tokens.add(factory.createSetAllowNullToDb(existing, attr)); + } + } + + // TODO: check more types than char/varchar + // TODO: psql report VARCHAR for text column, not clob + switch (detected.getType()) { + case Types.VARCHAR: + case Types.CHAR: + if (attr.getMaxLength() != detected.getMaxLength()) { + tokens.add(factory.createSetColumnTypeToDb(existing, detected, attr)); + } + break; + } + } + + return tokens; + } + + private List<MergerToken> checkRelationshipsToDrop(DbEntity dbEntity, DbEntity detectedEntity) { + List<MergerToken> tokens = new LinkedList<MergerToken>(); + + // relationships to drop + for (DbRelationship detected : detectedEntity.getRelationships()) { + if (findDbRelationship(dbEntity, detected) == null) { + + // alter detected relationship to match entity and attribute + // names. + // (case sensitively) + + DbEntity targetEntity = findDbEntity(dbEntity.getDataMap().getDbEntities(), + detected.getTargetEntityName()); + if (targetEntity == null) { + continue; + } + + detected.setSourceEntity(dbEntity); + detected.setTargetEntityName(targetEntity); + + // manipulate the joins to match the DbAttributes in the model + for (DbJoin join : detected.getJoins()) { + DbAttribute sattr = findDbAttribute(dbEntity, join.getSourceName()); + if (sattr != null) { + join.setSourceName(sattr.getName()); + } + DbAttribute tattr = findDbAttribute(targetEntity, join.getTargetName()); + if (tattr != null) { + join.setTargetName(tattr.getName()); + } + } + + MergerToken token = factory.createDropRelationshipToDb(dbEntity, detected); + if (detected.isToMany()) { + // default toModel as we can not do drop a toMany in the db. + // only + // toOne are represented using foreign key + token = token.createReverse(factory); + } + tokens.add(token); + } + } + + return tokens; + } + + private List<MergerToken> checkRelationshipsToAdd(DbEntity dbEntity, DbEntity detectedEntity) { + + List<MergerToken> tokens = new LinkedList<MergerToken>(); + + for (DbRelationship rel : dbEntity.getRelationships()) { + if (findDbRelationship(detectedEntity, rel) == null) { + AddRelationshipToDb token = (AddRelationshipToDb) factory.createAddRelationshipToDb(dbEntity, rel); + + if (token.shouldGenerateFkConstraint()) { + // TODO I guess we should add relationship always; in order + // to have ability + // TODO generate reverse relationship. If it doesn't have + // anything to execute it will be passed + // TODO through execution without any affect on db + tokens.add(token); + } + } + } + + return tokens; + } + + private MergerToken checkPrimaryKeyChange(DbEntity dbEntity, DbEntity detectedEntity) { + Collection<DbAttribute> primaryKeyOriginal = detectedEntity.getPrimaryKeys(); + Collection<DbAttribute> primaryKeyNew = dbEntity.getPrimaryKeys(); + + String primaryKeyName = null; + if (detectedEntity instanceof DetectedDbEntity) { + primaryKeyName = ((DetectedDbEntity) detectedEntity).getPrimaryKeyName(); + } + + if (upperCaseEntityNames(primaryKeyOriginal).equals(upperCaseEntityNames(primaryKeyNew))) { + return null; + } + + return factory.createSetPrimaryKeyToDb(dbEntity, primaryKeyOriginal, primaryKeyNew, primaryKeyName); + } + + private Set<String> upperCaseEntityNames(Collection<? extends Attribute> attrs) { + Set<String> names = new HashSet<String>(); + for (Attribute attr : attrs) { + names.add(attr.getName().toUpperCase()); + } + return names; + } + + /** + * case insensitive search for a {@link DbEntity} in a {@link DataMap} by + * name + */ + private DbEntity findDbEntity(Collection<DbEntity> dbEntities, String caseInsensitiveName) { + // TODO: create a Map with upper case keys? + for (DbEntity e : dbEntities) { + if (e.getName().equalsIgnoreCase(caseInsensitiveName)) { + return e; + } + } + return null; + } + + /** + * case insensitive search for a {@link DbAttribute} in a {@link DbEntity} + * by name + */ + private DbAttribute findDbAttribute(DbEntity entity, String caseInsensitiveName) { + for (DbAttribute a : entity.getAttributes()) { + if (a.getName().equalsIgnoreCase(caseInsensitiveName)) { + return a; + } + } + return null; + } + + /** + * search for a {@link DbRelationship} like rel in the given + * {@link DbEntity} + */ + private DbRelationship findDbRelationship(DbEntity entity, DbRelationship rel) { + for (DbRelationship candidate : entity.getRelationships()) { + if (equalDbJoinCollections(candidate.getJoins(), rel.getJoins())) { + return candidate; + } + } + return null; + } + + /** + * Return true if the two unordered {@link Collection}s of {@link DbJoin}s + * are equal. Entity and Attribute names are compared case insensitively. + * + * TODO complexity n^2; sort both collection and go through them to compare + * = 2*n*log(n) + n + */ + private static boolean equalDbJoinCollections(Collection<DbJoin> j1s, Collection<DbJoin> j2s) { + if (j1s.size() != j2s.size()) { + return false; + } + + for (DbJoin j1 : j1s) { + if (!havePair(j2s, j1)) { + return false; + } + } + + return true; + } + + private static boolean havePair(Collection<DbJoin> j2s, DbJoin j1) { + for (DbJoin j2 : j2s) { + if (!isNull(j1.getSource()) && !isNull(j1.getTarget()) && !isNull(j2.getSource()) + && !isNull(j2.getTarget()) + && j1.getSource().getEntity().getName().equalsIgnoreCase(j2.getSource().getEntity().getName()) + && j1.getTarget().getEntity().getName().equalsIgnoreCase(j2.getTarget().getEntity().getName()) + && j1.getSourceName().equalsIgnoreCase(j2.getSourceName()) + && j1.getTargetName().equalsIgnoreCase(j2.getTargetName())) { + + return true; + } + } + return false; + } + + private static boolean isNull(DbAttribute attribute) { + return attribute == null || attribute.getEntity() == null; + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DbMergerConfig.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DbMergerConfig.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DbMergerConfig.java new file mode 100644 index 0000000..1cd80df --- /dev/null +++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DbMergerConfig.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cayenne.dbsync.merge; + +import org.apache.cayenne.dbsync.reverse.filters.FiltersConfig; + +/** + * @since 4.0 + */ +public class DbMergerConfig { + + private FiltersConfig filtersConfig; + + private boolean skipRelationships; + + private boolean skipPrimaryKey; + + public DbMergerConfig(FiltersConfig filtersConfig, boolean skipRelationships, boolean skipPrimaryKey) { + this.filtersConfig = filtersConfig; + this.skipRelationships = skipRelationships; + this.skipPrimaryKey = skipPrimaryKey; + } + + public void setSkipRelationships(boolean skipRelationships) { + this.skipRelationships = skipRelationships; + } + + public boolean isSkipRelationships() { + return skipRelationships; + } + + public void setSkipPrimaryKey(boolean skipPrimaryKey) { + this.skipPrimaryKey = skipPrimaryKey; + } + + public boolean isSkipPrimaryKey() { + return skipPrimaryKey; + } + + public FiltersConfig getFiltersConfig() { + return filtersConfig; + } + + public void setFiltersConfig(FiltersConfig filtersConfig) { + this.filtersConfig = filtersConfig; + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DefaultModelMergeDelegate.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DefaultModelMergeDelegate.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DefaultModelMergeDelegate.java new file mode 100644 index 0000000..617aad2 --- /dev/null +++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DefaultModelMergeDelegate.java @@ -0,0 +1,89 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ +package org.apache.cayenne.dbsync.merge; + +import org.apache.cayenne.map.DbAttribute; +import org.apache.cayenne.map.DbEntity; +import org.apache.cayenne.map.DbRelationship; +import org.apache.cayenne.map.ObjAttribute; +import org.apache.cayenne.map.ObjEntity; +import org.apache.cayenne.map.ObjRelationship; + +/** + * A default noop implementation of {@link ModelMergeDelegate}. + */ +public class DefaultModelMergeDelegate implements ModelMergeDelegate { + + @Override + public void dbAttributeAdded(DbAttribute att) { + } + + @Override + public void dbAttributeModified(DbAttribute att) { + } + + @Override + public void dbAttributeRemoved(DbAttribute att) { + } + + @Override + public void dbEntityAdded(DbEntity ent) { + } + + @Override + public void dbEntityRemoved(DbEntity ent) { + } + + @Override + public void dbRelationshipAdded(DbRelationship rel) { + } + + @Override + public void dbRelationshipRemoved(DbRelationship rel) { + } + + @Override + public void objAttributeAdded(ObjAttribute att) { + } + + @Override + public void objAttributeModified(ObjAttribute att) { + } + + @Override + public void objAttributeRemoved(ObjAttribute att) { + } + + @Override + public void objEntityAdded(ObjEntity ent) { + } + + @Override + public void objEntityRemoved(ObjEntity ent) { + } + + @Override + public void objRelationshipAdded(ObjRelationship rel) { + } + + @Override + public void objRelationshipRemoved(ObjRelationship rel) { + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DefaultValueForNullProvider.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DefaultValueForNullProvider.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DefaultValueForNullProvider.java new file mode 100644 index 0000000..9d4e0cd --- /dev/null +++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DefaultValueForNullProvider.java @@ -0,0 +1,62 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ +package org.apache.cayenne.dbsync.merge; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.cayenne.access.jdbc.SQLParameterBinding; +import org.apache.cayenne.map.DbAttribute; +import org.apache.cayenne.map.DbEntity; + +public class DefaultValueForNullProvider implements ValueForNullProvider { + + private Map<String, SQLParameterBinding> values = new HashMap<>(); + + public void set(DbEntity entity, DbAttribute column, Object value, int type) { + values.put(createKey(entity, column), new SQLParameterBinding(value, type, column + .getAttributePrecision())); + } + + protected SQLParameterBinding get(DbEntity entity, DbAttribute column) { + return values.get(createKey(entity, column)); + } + + public List<String> createSql(DbEntity entity, DbAttribute column) { + SQLParameterBinding value = get(entity, column); + if (value == null) { + return Collections.emptyList(); + } + + // TODO: change things so it is possible to use prepared statements here + return Collections.singletonList("UPDATE " + entity.getFullyQualifiedName() + + " SET " + column.getName() + "='" + value.getValue() + "' WHERE " + column.getName() + " IS NULL"); + } + + public boolean hasValueFor(DbEntity entity, DbAttribute column) { + return values.containsKey(createKey(entity, column)); + } + + private String createKey(DbEntity entity, DbAttribute attribute) { + return (entity.getFullyQualifiedName() + "." + attribute.getName()).toUpperCase(); + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropColumnToDb.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropColumnToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropColumnToDb.java new file mode 100644 index 0000000..19914f0 --- /dev/null +++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropColumnToDb.java @@ -0,0 +1,52 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ +package org.apache.cayenne.dbsync.merge; + +import java.util.Collections; +import java.util.List; + +import org.apache.cayenne.dba.DbAdapter; +import org.apache.cayenne.dba.QuotingStrategy; +import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory; +import org.apache.cayenne.map.DbAttribute; +import org.apache.cayenne.map.DbEntity; + +public class DropColumnToDb extends AbstractToDbToken.EntityAndColumn { + + public DropColumnToDb(DbEntity entity, DbAttribute column) { + super("Drop Column", entity, column); + } + + @Override + public List<String> createSql(DbAdapter adapter) { + StringBuilder sqlBuffer = new StringBuilder(); + QuotingStrategy context = adapter.getQuotingStrategy(); + sqlBuffer.append("ALTER TABLE "); + sqlBuffer.append(context.quotedFullyQualifiedName(getEntity())); + sqlBuffer.append(" DROP COLUMN "); + sqlBuffer.append(context.quotedName(getColumn())); + + return Collections.singletonList(sqlBuffer.toString()); + } + + public MergerToken createReverse(MergerTokenFactory factory) { + return factory.createAddColumnToModel(getEntity(), getColumn()); + } + +}