mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
79
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9fbfca5f73 | ||
|
|
9e70ba915f | ||
|
|
a1a5dcd035 | ||
|
|
d2b048b0b4 | ||
|
|
4272c17689 | ||
|
|
11723e3a32 | ||
|
|
237cdcdd3d | ||
|
|
e96bee74ae | ||
|
|
c8b57c3302 | ||
|
|
f65ea96b89 | ||
|
|
a1bd0b469a | ||
|
|
fd5563e3da | ||
|
|
f9156ede55 | ||
|
|
10d2d591db | ||
|
|
998d8ed6f2 | ||
|
|
a8da472872 | ||
|
|
a9cdf9736a | ||
|
|
c930a1dd6b | ||
|
|
67ce6ef58e | ||
|
|
c4f28efc45 | ||
|
|
afa4bc06d9 | ||
|
|
828c9ce128 | ||
|
|
6f37a0bd72 | ||
|
|
036086af02 | ||
|
|
f34b5e52e0 | ||
|
|
a70e289b00 | ||
|
|
4ad0e06aab | ||
|
|
86de7e845e | ||
|
|
38501ca372 | ||
|
|
4790c83fa1 | ||
|
|
b410c83492 | ||
|
|
b19145f16f | ||
|
|
c28cff2f40 | ||
|
|
0c8cf78ecc | ||
|
|
df112815ff | ||
|
|
85522ed8bc | ||
|
|
cf88f2375b | ||
|
|
24151f11fd | ||
|
|
424a83fa0a | ||
|
|
53dd91416c | ||
|
|
5a463c5071 | ||
|
|
bc3cf1348a | ||
|
|
f3b1024283 | ||
|
|
7ba35d7a62 | ||
|
|
990ca2980b | ||
|
|
38dd3d82fc | ||
|
|
6be26aaff2 | ||
|
|
d1462b032a | ||
|
|
6ab974a330 | ||
|
|
64618da6a7 | ||
|
|
d242ff7351 | ||
|
|
7d54aaa9a7 | ||
|
|
0e46184656 | ||
|
|
c9e1a6edbd | ||
|
|
3c0cffd146 | ||
|
|
d31547fb69 | ||
|
|
93adaa12bc | ||
|
|
d1cdbe94de | ||
|
|
e7c6223799 | ||
|
|
d33afd85de | ||
|
|
77bcb4aef9 | ||
|
|
a70fe184c2 | ||
|
|
7238f39be5 | ||
|
|
ae44b59c5f | ||
|
|
ced4e71307 | ||
|
|
9cb43bd5dd | ||
|
|
9665de2dbe | ||
|
|
e2666f0c4c | ||
|
|
89c83c90d1 | ||
|
|
cdd25351a6 | ||
|
|
17e0413837 | ||
|
|
b04419d9c7 | ||
|
|
0453892ebd | ||
|
|
55493c5b7d | ||
|
|
eb2f7cd852 | ||
|
|
f44eb234c8 | ||
|
|
404b57ab21 | ||
|
|
a54f09ca4b | ||
|
|
a3bb909308 |
+32
@@ -0,0 +1,32 @@
|
||||
sudo: required
|
||||
language: java
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
|
||||
git:
|
||||
depth: 3
|
||||
|
||||
addons:
|
||||
postgresql: "9.4"
|
||||
|
||||
services:
|
||||
- postgresql
|
||||
|
||||
before_script:
|
||||
- ./.travis/setup_database
|
||||
|
||||
env:
|
||||
- EBEAN_DB=h2
|
||||
- EBEAN_DB=pg
|
||||
|
||||
install: true
|
||||
|
||||
script:
|
||||
- mvn clean test
|
||||
|
||||
after_failure:
|
||||
- ./.travis/print_surefire_reports
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.m2
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "\n=== SUREFIRE REPORTS ===\n"
|
||||
|
||||
for file in target/surefire-reports/*.txt
|
||||
do
|
||||
echo ${file}
|
||||
cat ${file}
|
||||
echo
|
||||
done
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "\n=== SETUP DATABASE ===\n"
|
||||
|
||||
#set -e
|
||||
#set -x
|
||||
|
||||
# Setup postgres' users and databases
|
||||
sudo -u postgres psql -c "CREATE USER unit WITH PASSWORD 'unit';"
|
||||
sudo -u postgres psql -c 'CREATE DATABASE unit WITH OWNER unit;'
|
||||
sudo -u postgres psql unit -c 'CREATE EXTENSION hstore;'
|
||||
sudo -u postgres psql unit -c 'CREATE EXTENSION pgcrypto;'
|
||||
#sudo -u postgres psql test2 -c 'CREATE EXTENSION postgis;'
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm</artifactId>
|
||||
<version>6.15.1</version>
|
||||
<version>6.16.5</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>avaje-ebeanorm</name>
|
||||
@@ -152,7 +152,7 @@
|
||||
<dependency>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm-agent</artifactId>
|
||||
<version>4.8.1</version>
|
||||
<version>4.9.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -219,7 +219,7 @@
|
||||
<plugin>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm-mavenenhancer</artifactId>
|
||||
<version>4.8.1</version>
|
||||
<version>4.9.1</version>
|
||||
<executions>
|
||||
<!-- Not going to enhance Model bean -->
|
||||
<execution>
|
||||
|
||||
@@ -11,4 +11,14 @@ public interface AutoTune {
|
||||
*/
|
||||
void collectProfiling();
|
||||
|
||||
/**
|
||||
* Output the profiling.
|
||||
* <p>
|
||||
* When profiling updates are applied to tuning at runtime this reports all tuning and profiling combined.
|
||||
* When profiling is not applied at runtime then this reports the diff report with new and diff entries relative
|
||||
* to the existing tuning.
|
||||
* </p>
|
||||
*/
|
||||
void reportProfiling();
|
||||
|
||||
}
|
||||
|
||||
@@ -58,15 +58,18 @@ class PrimaryServer {
|
||||
*/
|
||||
private static String determineDefaultServerName() {
|
||||
|
||||
String defaultServerName = System.getProperty("datasource.default");
|
||||
String defaultServerName = System.getenv("EBEAN_DB");
|
||||
if (isEmpty(defaultServerName)) {
|
||||
defaultServerName = System.getProperty("ebean.default.datasource");
|
||||
}
|
||||
if (isEmpty(defaultServerName)) {
|
||||
defaultServerName = globalProperties.getProperty("datasource.default");
|
||||
}
|
||||
if (isEmpty(defaultServerName)) {
|
||||
defaultServerName = globalProperties.getProperty("ebean.default.datasource");
|
||||
defaultServerName = System.getProperty("datasource.default");
|
||||
if (isEmpty(defaultServerName)) {
|
||||
defaultServerName = System.getProperty("ebean.default.datasource");
|
||||
if (isEmpty(defaultServerName)) {
|
||||
defaultServerName = globalProperties.getProperty("datasource.default");
|
||||
if (isEmpty(defaultServerName)) {
|
||||
defaultServerName = globalProperties.getProperty("ebean.default.datasource");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultServerName;
|
||||
}
|
||||
|
||||
@@ -585,6 +585,9 @@ public final class RawSql implements Serializable {
|
||||
* <p>
|
||||
* For example modify all mappings with table alias "c" to have the path prefix "customer".
|
||||
* </p>
|
||||
* <p>
|
||||
* For the "Root type" you don't need to specify a tableAliasMapping.
|
||||
* </p>
|
||||
*/
|
||||
public void tableAliasMapping(String tableAlias, String path) {
|
||||
|
||||
|
||||
@@ -106,6 +106,9 @@ public class RawSqlBuilder {
|
||||
* <p>
|
||||
* For example modify all mappings with table alias "c" to have the path prefix "customer".
|
||||
* </p>
|
||||
* <p>
|
||||
* For the "Root type" you don't need to specify a tableAliasMapping.
|
||||
* </p>
|
||||
*/
|
||||
public RawSqlBuilder tableAliasMapping(String tableAlias, String path) {
|
||||
columnMapping.tableAliasMapping(tableAlias, path);
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package com.avaje.ebean.bean;
|
||||
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.ValuePair;
|
||||
|
||||
import javax.persistence.EntityNotFoundException;
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.beans.PropertyChangeSupport;
|
||||
@@ -11,12 +16,6 @@ import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.EntityNotFoundException;
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.ValuePair;
|
||||
|
||||
/**
|
||||
* This is the object added to every entity bean using byte code enhancement.
|
||||
* <p>
|
||||
@@ -92,6 +91,8 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
|
||||
private int lazyLoadProperty = -1;
|
||||
|
||||
private Object ownerId;
|
||||
|
||||
/**
|
||||
* Create a intercept with a given entity.
|
||||
* <p>
|
||||
@@ -342,18 +343,11 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the lazy load succeeded. If not then mark this bean as having
|
||||
* failed lazy loading due to the underlying row being deleted.
|
||||
* <p>
|
||||
* We mark the bean this way rather than immediately fail as we might be batch
|
||||
* lazy loading and this bean might not be used by the client code at all.
|
||||
* Instead we will fail as soon as the client code tries to use this bean.
|
||||
* </p>
|
||||
* Set lazy load failure flag.
|
||||
*/
|
||||
public void checkLazyLoadFailure() {
|
||||
if (lazyLoadProperty != -1) {
|
||||
this.lazyLoadFailure = true;
|
||||
}
|
||||
public void setLazyLoadFailure(Object ownerId) {
|
||||
this.lazyLoadFailure = true;
|
||||
this.ownerId = ownerId;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -748,7 +742,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
|
||||
if (lazyLoadFailure) {
|
||||
// failed when batch lazy loaded by another bean in the batch
|
||||
throw new EntityNotFoundException("Bean has been deleted - lazy loading failed");
|
||||
throw new EntityNotFoundException("Lazy loading failed on type:" + owner.getClass().getName() + " id:" + ownerId + " - Bean has been deleted");
|
||||
}
|
||||
|
||||
if (lazyLoadProperty == -1) {
|
||||
@@ -763,7 +757,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
|
||||
if (lazyLoadFailure) {
|
||||
// failed when lazy loading this bean
|
||||
throw new EntityNotFoundException("Bean has been deleted - lazy loading failed");
|
||||
throw new EntityNotFoundException("Lazy loading failed on type:" + owner.getClass().getName() + " id:" + ownerId + " - Bean has been deleted.");
|
||||
}
|
||||
|
||||
// bean should be loaded and intercepting now. setLoaded() has
|
||||
|
||||
@@ -21,9 +21,13 @@ public class AutoTuneConfig {
|
||||
|
||||
private double profilingRate = 0.01;
|
||||
|
||||
private int profilingUpdateFrequency;
|
||||
|
||||
private int garbageCollectionWait = 100;
|
||||
|
||||
private boolean skipCollectionOnShutdown;
|
||||
private boolean skipGarbageCollectionOnShutdown;
|
||||
|
||||
private boolean skipProfileReportingOnShutdown;
|
||||
|
||||
public AutoTuneConfig() {
|
||||
}
|
||||
@@ -56,6 +60,20 @@ public class AutoTuneConfig {
|
||||
this.profilingFile = profilingFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the frequency in seconds the profiling should be collected and automatically applied to the tuning.
|
||||
*/
|
||||
public int getProfilingUpdateFrequency() {
|
||||
return profilingUpdateFrequency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the frequency in seconds the profiling should be collected and automatically applied to the tuning.
|
||||
*/
|
||||
public void setProfilingUpdateFrequency(int profilingUpdateFrequency) {
|
||||
this.profilingUpdateFrequency = profilingUpdateFrequency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mode used when autoTune has not been explicit defined on a
|
||||
* query.
|
||||
@@ -184,17 +202,33 @@ public class AutoTuneConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if profiling collection should be skipped on shutdown.
|
||||
* Return true if triggering garbage collection should be skipped on shutdown.
|
||||
* You might set this when System.GC() slows a application shutdown too much.
|
||||
*/
|
||||
public boolean isSkipCollectionOnShutdown() {
|
||||
return skipCollectionOnShutdown;
|
||||
public boolean isSkipGarbageCollectionOnShutdown() {
|
||||
return skipGarbageCollectionOnShutdown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if profiling collection should be skipped on shutdown.
|
||||
* Set to true if triggering garbage collection should be skipped on shutdown.
|
||||
* You might set this when System.GC() slows a application shutdown too much.
|
||||
*/
|
||||
public void setSkipCollectionOnShutdown(boolean skipCollectionOnShutdown) {
|
||||
this.skipCollectionOnShutdown = skipCollectionOnShutdown;
|
||||
public void setSkipGarbageCollectionOnShutdown(boolean skipGarbageCollectionOnShutdown) {
|
||||
this.skipGarbageCollectionOnShutdown = skipGarbageCollectionOnShutdown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if profile reporting should be skipped on shutdown.
|
||||
*/
|
||||
public boolean isSkipProfileReportingOnShutdown() {
|
||||
return skipProfileReportingOnShutdown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if profile reporting should be skipped on shutdown.
|
||||
*/
|
||||
public void setSkipProfileReportingOnShutdown(boolean skipProfileReportingOnShutdown) {
|
||||
this.skipProfileReportingOnShutdown = skipProfileReportingOnShutdown;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -206,13 +240,15 @@ public class AutoTuneConfig {
|
||||
queryTuningAddVersion = p.getBoolean("autoTune.queryTuningAddVersion", queryTuningAddVersion);
|
||||
queryTuningFile = p.get("autoTune.queryTuningFile", queryTuningFile);
|
||||
|
||||
skipCollectionOnShutdown = p.getBoolean("autoTune.skipCollectionOnShutdown", skipCollectionOnShutdown);
|
||||
|
||||
skipGarbageCollectionOnShutdown = p.getBoolean("autoTune.skipGarbageCollectionOnShutdown", skipGarbageCollectionOnShutdown);
|
||||
skipProfileReportingOnShutdown = p.getBoolean("autoTune.skipProfileReportingOnShutdown", skipProfileReportingOnShutdown);
|
||||
|
||||
mode = p.getEnum(AutoTuneMode.class, "autoTune.mode", mode);
|
||||
|
||||
profiling = p.getBoolean("autoTune.profiling", profiling);
|
||||
profilingBase = p.getInt("autoTune.profilingBase", profilingBase);
|
||||
profilingRate = p.getDouble("autoTune.profilingRate", profilingRate);
|
||||
profilingFile = p.get("autoTune.profilingFile", profilingFile);
|
||||
profilingUpdateFrequency = p.getInt("autoTune.profilingUpdateFrequency", profilingUpdateFrequency);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +88,8 @@ public class DbMigrationConfig {
|
||||
|
||||
protected String modelSuffix = ".model.xml";
|
||||
|
||||
protected boolean includeGeneratedFileComment;
|
||||
|
||||
/**
|
||||
* Return the DB platform to generate migration DDL for.
|
||||
*
|
||||
@@ -236,6 +238,20 @@ public class DbMigrationConfig {
|
||||
this.rollbackSuffix = rollbackSuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the generated file comment should be included.
|
||||
*/
|
||||
public boolean isIncludeGeneratedFileComment() {
|
||||
return includeGeneratedFileComment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if the generated file comment should be included.
|
||||
*/
|
||||
public void setIncludeGeneratedFileComment(boolean includeGeneratedFileComment) {
|
||||
this.includeGeneratedFileComment = includeGeneratedFileComment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the migration version.
|
||||
* <p>
|
||||
@@ -281,6 +297,7 @@ public class DbMigrationConfig {
|
||||
dropSuffix = properties.get("migration.dropSuffix", dropSuffix);
|
||||
rollbackSuffix = properties.get("migration.rollbackSuffix", rollbackSuffix);
|
||||
modelSuffix = properties.get("migration.modelSuffix", modelSuffix);
|
||||
includeGeneratedFileComment = properties.getBoolean("migration.includeGeneratedFileComment", includeGeneratedFileComment);
|
||||
|
||||
platform = properties.getEnum(DbPlatformName.class, "migration.platform", platform);
|
||||
suppressRollback = properties.getBoolean("migration.suppressRollback", suppressRollback);
|
||||
|
||||
@@ -59,6 +59,8 @@ public class DbMigration {
|
||||
|
||||
private static final String initialVersion = "1.0";
|
||||
|
||||
private static final String GENERATED_COMMENT = "THIS IS A GENERATED FILE - DO NOT MODIFY";
|
||||
|
||||
/**
|
||||
* Set to true if DbMigration run with online EbeanServer instance.
|
||||
*/
|
||||
@@ -234,7 +236,7 @@ public class DbMigration {
|
||||
if (databasePlatform != null) {
|
||||
// writer needs the current model to provide table/column details for
|
||||
// history ddl generation (triggers, history tables etc)
|
||||
DdlWrite write = new DdlWrite(new MConfiguration(), currentModel.read());
|
||||
DdlWrite write = new DdlWrite(new MConfiguration(), current);
|
||||
PlatformDdlWriter writer = createDdlWriter(databasePlatform, "");
|
||||
writer.processMigration(dbMigration, write, migrationDir , fullVersion);
|
||||
}
|
||||
@@ -304,7 +306,8 @@ public class DbMigration {
|
||||
if (file.exists()) {
|
||||
return false;
|
||||
}
|
||||
MigrationXmlWriter xmlWriter = new MigrationXmlWriter();
|
||||
String comment = migrationConfig.isIncludeGeneratedFileComment() ? GENERATED_COMMENT : null;
|
||||
MigrationXmlWriter xmlWriter = new MigrationXmlWriter(comment);
|
||||
xmlWriter.write(dbMigration, file);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -10,14 +10,24 @@ import com.avaje.ebean.dbmigration.model.ModelContainer;
|
||||
*/
|
||||
public class DdlWrite {
|
||||
|
||||
public enum Mode {
|
||||
APPLY,
|
||||
ROLLBACK,
|
||||
DROP
|
||||
}
|
||||
|
||||
private final ModelContainer currentModel;
|
||||
|
||||
private final DdlBuffer applyDropDependencies;
|
||||
|
||||
private final DdlBuffer apply;
|
||||
|
||||
private final DdlBuffer applyForeignKeys;
|
||||
|
||||
private final DdlBuffer applyHistory;
|
||||
|
||||
private final DdlBuffer rollbackDropDependencies;
|
||||
|
||||
private final DdlBuffer rollbackForeignKeys;
|
||||
|
||||
private final DdlBuffer rollback;
|
||||
@@ -39,6 +49,11 @@ public class DdlWrite {
|
||||
*/
|
||||
private final DdlBuffer dropHistory;
|
||||
|
||||
/**
|
||||
* Buffer used to drop dependencies early in the 'drop script'.
|
||||
*/
|
||||
private final DdlBuffer dropDropDependencies;
|
||||
|
||||
/**
|
||||
* Create without any configuration or current model (no history support).
|
||||
*/
|
||||
@@ -51,13 +66,16 @@ public class DdlWrite {
|
||||
*/
|
||||
public DdlWrite(MConfiguration configuration, ModelContainer currentModel) {
|
||||
this.currentModel = currentModel;
|
||||
this.applyDropDependencies = new BaseDdlBuffer(configuration);
|
||||
this.apply = new BaseDdlBuffer(configuration);
|
||||
this.applyForeignKeys = new BaseDdlBuffer(configuration);
|
||||
this.applyHistory = new BaseDdlBuffer(configuration);
|
||||
this.rollbackDropDependencies = new BaseDdlBuffer(configuration);
|
||||
this.rollbackForeignKeys = new BaseDdlBuffer(configuration);
|
||||
this.rollback = new BaseDdlBuffer(configuration);
|
||||
this.drop = new BaseDdlBuffer(configuration);
|
||||
this.dropHistory = new BaseDdlBuffer(configuration);
|
||||
this.dropDropDependencies = new BaseDdlBuffer(configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,7 +95,8 @@ public class DdlWrite {
|
||||
public boolean isApplyEmpty() {
|
||||
return apply.getBuffer().isEmpty()
|
||||
&& applyForeignKeys.getBuffer().isEmpty()
|
||||
&& applyHistory.getBuffer().isEmpty();
|
||||
&& applyHistory.getBuffer().isEmpty()
|
||||
&& applyDropDependencies.getBuffer().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,9 +104,51 @@ public class DdlWrite {
|
||||
*/
|
||||
public boolean isApplyRollbackEmpty() {
|
||||
return rollback.getBuffer().isEmpty()
|
||||
&& rollbackForeignKeys.getBuffer().isEmpty();
|
||||
&& rollbackForeignKeys.getBuffer().isEmpty()
|
||||
&& rollbackDropDependencies.getBuffer().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the apply or rollback buffer.
|
||||
*/
|
||||
public DdlBuffer buffer(Mode mode) {
|
||||
switch (mode) {
|
||||
case APPLY: return apply();
|
||||
case ROLLBACK: return rollback();
|
||||
case DROP: return drop();
|
||||
default:
|
||||
throw new IllegalStateException("Invalid mode" + mode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the apply or rollback buffer.
|
||||
*/
|
||||
public DdlBuffer historyBuffer(Mode mode) {
|
||||
switch (mode) {
|
||||
case APPLY: return applyHistory();
|
||||
case ROLLBACK: return rollback();
|
||||
case DROP: return dropHistory();
|
||||
default:
|
||||
throw new IllegalStateException("Invalid mode" + mode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the apply or rollback drop dependencies buffer.
|
||||
*/
|
||||
public DdlBuffer dropDependencies(Mode mode) {
|
||||
|
||||
switch (mode) {
|
||||
case APPLY: return applyDropDependencies();
|
||||
case ROLLBACK: return rollbackDropDependencies();
|
||||
case DROP: return dropDropDependencies();
|
||||
default:
|
||||
throw new IllegalStateException("Invalid mode" + mode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true the drop buffers are empty.
|
||||
*/
|
||||
@@ -102,6 +163,13 @@ public class DdlWrite {
|
||||
return apply;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the buffer that executes early to drop dependencies like views etc.
|
||||
*/
|
||||
public DdlBuffer applyDropDependencies() {
|
||||
return applyDropDependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the buffer that APPLY DDL is written to for foreign keys and their associated indexes.
|
||||
* <p>
|
||||
@@ -119,6 +187,13 @@ public class DdlWrite {
|
||||
return applyHistory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the buffer that rollback executes early to drop dependencies like views.
|
||||
*/
|
||||
public DdlBuffer rollbackDropDependencies() {
|
||||
return rollbackDropDependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the buffer that ROLLBACK DDL is written to for foreign keys and associated indexes.
|
||||
*/
|
||||
@@ -156,5 +231,11 @@ public class DdlWrite {
|
||||
return dropHistory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the buffer that executes early for 'drop' script.
|
||||
*/
|
||||
public DdlBuffer dropDropDependencies() {
|
||||
return dropDropDependencies;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+106
-24
@@ -10,7 +10,6 @@ import com.avaje.ebean.dbmigration.model.MColumn;
|
||||
import com.avaje.ebean.dbmigration.model.MTable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@@ -45,21 +44,71 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
|
||||
this.historySuffix = serverConfig.getHistoryTableSuffix();
|
||||
this.constraintNaming = serverConfig.getConstraintNaming();
|
||||
|
||||
this.sysPeriodStart = sysPeriod+"_start";
|
||||
this.sysPeriodEnd = sysPeriod+"_end";
|
||||
this.sysPeriodStart = sysPeriod + "_start";
|
||||
this.sysPeriodEnd = sysPeriod + "_end";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void regenerateHistoryTriggers(DdlWrite writer, HistoryTableUpdate update) throws IOException {
|
||||
public void updateTriggers(DdlWrite writer, HistoryTableUpdate update) throws IOException {
|
||||
|
||||
MTable table = writer.getTable(update.getBaseTable());
|
||||
if (table == null) {
|
||||
throw new IllegalStateException("MTable "+update.getBaseTable()+" not found in writer? (required for history DDL)");
|
||||
throw new IllegalStateException("MTable " + update.getBaseTable() + " not found in writer? (required for history DDL)");
|
||||
}
|
||||
regenerateHistoryTriggers(writer, table, update);
|
||||
updateTriggers(writer, table, update);
|
||||
}
|
||||
|
||||
protected abstract void regenerateHistoryTriggers(DdlWrite writer, MTable table, HistoryTableUpdate update) throws IOException;
|
||||
/**
|
||||
* Replace the existing triggers/stored procedures/views for history table support given the included columns.
|
||||
*/
|
||||
protected abstract void updateHistoryTriggers(DbTriggerUpdate triggerUpdate) throws IOException;
|
||||
|
||||
/**
|
||||
* Process the HistoryTableUpdate which can result in changes to the apply, rollback
|
||||
* and drop scripts.
|
||||
*/
|
||||
protected void updateTriggers(DdlWrite writer, MTable table, HistoryTableUpdate update) throws IOException {
|
||||
|
||||
DbTriggerUpdate triggerUpdate = createDbTriggerUpdate(writer, table);
|
||||
|
||||
if (update.hasApplyChanges()) {
|
||||
// includes add, include and exclude column changes
|
||||
|
||||
String applyChangeDescription = update.descriptionForApply();
|
||||
List<String> includedColumns = columnNamesForApply(table);
|
||||
|
||||
DdlBuffer apply = writer.applyHistory();
|
||||
apply.append("-- changes: ").append(applyChangeDescription).newLine();
|
||||
|
||||
triggerUpdate.prepare(DdlWrite.Mode.APPLY, includedColumns);
|
||||
updateHistoryTriggers(triggerUpdate);
|
||||
|
||||
// put a reverted version into the rollback buffer
|
||||
update.toRevertedColumns(includedColumns);
|
||||
|
||||
DdlBuffer rollback = writer.rollback();
|
||||
rollback.append("-- revert changes: ").append(applyChangeDescription).newLine();
|
||||
|
||||
triggerUpdate.prepare(DdlWrite.Mode.ROLLBACK, includedColumns);
|
||||
updateHistoryTriggers(triggerUpdate);//writer, DdlWrite.Mode.ROLLBACK, baseTableName, historyTableName, includedColumns);
|
||||
}
|
||||
|
||||
if (update.hasDropChanges()) {
|
||||
// effectively applies the dropped columns changes to history triggers
|
||||
|
||||
DdlBuffer drop = writer.dropHistory();
|
||||
drop.append("-- changes: ").append(update.descriptionForDrop()).newLine();
|
||||
|
||||
triggerUpdate.prepare(DdlWrite.Mode.DROP, columnNamesForDrop(table));
|
||||
updateHistoryTriggers(triggerUpdate);//writer, DdlWrite.Mode.DROP, baseTableName, historyTableName, columnNamesForDrop(table));
|
||||
}
|
||||
}
|
||||
|
||||
protected DbTriggerUpdate createDbTriggerUpdate(DdlWrite writer, MTable table) {
|
||||
String baseTableName = table.getName();
|
||||
String historyTableName = historyTableName(baseTableName);
|
||||
return new DbTriggerUpdate(baseTableName, historyTableName, writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable) throws IOException {
|
||||
@@ -78,7 +127,7 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
|
||||
String baseTable = addHistoryTable.getBaseTable();
|
||||
MTable table = writer.getTable(baseTable);
|
||||
if (table == null) {
|
||||
throw new IllegalStateException("MTable "+baseTable+" not found in writer? (required for history DDL)");
|
||||
throw new IllegalStateException("MTable " + baseTable + " not found in writer? (required for history DDL)");
|
||||
}
|
||||
|
||||
createWithHistory(writer, table);
|
||||
@@ -95,7 +144,7 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
|
||||
dropHistoryTableEtc(writer.rollback(), baseTable);
|
||||
|
||||
addHistoryTable(writer, table, whenCreatedColumn);
|
||||
addStoredFunction(writer, table, null);
|
||||
createStoredFunction(writer, table);
|
||||
createTriggers(writer, table);
|
||||
}
|
||||
|
||||
@@ -103,7 +152,7 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
|
||||
|
||||
protected abstract void dropTriggers(DdlBuffer buffer, String baseTable) throws IOException;
|
||||
|
||||
protected void addStoredFunction(DdlWrite writer, MTable table, HistoryTableUpdate update) throws IOException {
|
||||
protected void createStoredFunction(DdlWrite writer, MTable table) throws IOException {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@@ -158,7 +207,7 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
|
||||
|
||||
apply.append("create table ").append(table.getName()).append(historySuffix).append("(").newLine();
|
||||
|
||||
Collection<MColumn> cols = table.getColumns().values();
|
||||
Collection<MColumn> cols = table.allColumns();
|
||||
for (MColumn column : cols) {
|
||||
if (!column.isDraftOnly()) {
|
||||
writeColumnDefinition(apply, column.getName(), column.getType());
|
||||
@@ -191,6 +240,26 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
|
||||
.endOfStatement().end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or replace the with_history view with explicit columns.
|
||||
*/
|
||||
protected void createWithHistoryView(DbTriggerUpdate update) throws IOException {
|
||||
|
||||
DdlBuffer apply = update.historyBuffer();
|
||||
apply.append("create or replace view ").append(update.getBaseTable()).append(viewSuffix).append(" as select ");
|
||||
appendColumnNames(apply, update.getColumns(), "");
|
||||
appendSysPeriodColumns(apply, ", ");
|
||||
apply.append(" from ").append(update.getBaseTable()).append(" union all select ");
|
||||
appendColumnNames(apply, update.getColumns(), "");
|
||||
appendSysPeriodColumns(apply, ", ");
|
||||
apply.append(" from ").append(update.getHistoryTable()).endOfStatement().end();
|
||||
}
|
||||
|
||||
protected void appendSysPeriodColumns(DdlBuffer apply, String prefix) throws IOException {
|
||||
appendColumnName(apply, prefix, sysPeriodStart);
|
||||
appendColumnName(apply, prefix, sysPeriodEnd);
|
||||
}
|
||||
|
||||
protected void dropHistoryTableEtc(DdlBuffer buffer, String baseTableName) throws IOException {
|
||||
|
||||
buffer.append("drop view ").append(baseTableName).append(viewSuffix).endOfStatement();
|
||||
@@ -203,8 +272,6 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
|
||||
buffer.append("alter table ").append(baseTableName).append(" drop column ").append(sysPeriodEnd).endOfStatement();
|
||||
}
|
||||
|
||||
//protected abstract void addFunction(DdlBuffer apply, String procedureName, String historyTable, List<String> includedColumns) throws IOException;
|
||||
|
||||
protected void appendInsertIntoHistory(DdlBuffer buffer, String historyTable, List<String> columns) throws IOException {
|
||||
|
||||
buffer.append(" insert into ").append(historyTable).append(" (").append(sysPeriodStart).append(",").append(sysPeriodEnd).append(",");
|
||||
@@ -216,7 +283,7 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
|
||||
|
||||
protected void appendColumnNames(DdlBuffer buffer, List<String> columns, String columnPrefix) throws IOException {
|
||||
|
||||
for (int i=0; i< columns.size(); i++) {
|
||||
for (int i = 0; i < columns.size(); i++) {
|
||||
if (i > 0) {
|
||||
buffer.append(", ");
|
||||
}
|
||||
@@ -226,18 +293,33 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of included columns in order.
|
||||
* Append a single column to the buffer if it is not null.
|
||||
*/
|
||||
protected List<String> includedColumnNames(MTable table) throws IOException {
|
||||
protected void appendColumnName(DdlBuffer buffer, String prefix, String columnName) throws IOException {
|
||||
|
||||
Collection<MColumn> columns = table.getColumns().values();
|
||||
List<String> includedColumns = new ArrayList<String>(columns.size());
|
||||
|
||||
for (MColumn column : columns) {
|
||||
if (column.isIncludeInHistory()) {
|
||||
includedColumns.add(column.getName());
|
||||
}
|
||||
if (columnName != null) {
|
||||
buffer.append(prefix).append(columnName);
|
||||
}
|
||||
return includedColumns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the column names included in history for the apply script.
|
||||
* <p>
|
||||
* Note that dropped columns are actually still included at this point as they are going
|
||||
* to be removed from the history handling when the drop script runs that also deletes
|
||||
* the column.
|
||||
* </p>
|
||||
*/
|
||||
protected List<String> columnNamesForApply(MTable table) throws IOException {
|
||||
|
||||
return table.allHistoryColumns(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the column names included in history for the drop script.
|
||||
*/
|
||||
protected List<String> columnNamesForDrop(MTable table) throws IOException {
|
||||
|
||||
return table.allHistoryColumns(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
|
||||
|
||||
import com.avaje.ebean.dbmigration.ddlgeneration.DdlBuffer;
|
||||
import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* DB trigger update when a change occurs on a table with history.
|
||||
*/
|
||||
public class DbTriggerUpdate {
|
||||
|
||||
private final String baseTableName;
|
||||
|
||||
private final String historyTableName;
|
||||
|
||||
private final DdlWrite writer;
|
||||
|
||||
private DdlWrite.Mode mode;
|
||||
|
||||
private List<String> includedColumns;
|
||||
|
||||
public DbTriggerUpdate(String baseTableName, String historyTableName, DdlWrite writer) {
|
||||
this.baseTableName = baseTableName;
|
||||
this.historyTableName = historyTableName;
|
||||
this.writer = writer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare for use given the mode and columns included in history.
|
||||
*/
|
||||
public void prepare(DdlWrite.Mode mode, List<String> includedColumns) {
|
||||
this.mode = mode;
|
||||
this.includedColumns = includedColumns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the appropriate buffer for the current mode.
|
||||
*/
|
||||
public DdlBuffer historyBuffer() {
|
||||
return writer.historyBuffer(mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the appropriate drop dependency buffer for the current mode.
|
||||
*/
|
||||
public DdlBuffer dropDependencyBuffer() {
|
||||
return writer.dropDependencies(mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the base table name.
|
||||
*/
|
||||
public String getBaseTable() {
|
||||
return baseTableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the history table name.
|
||||
*/
|
||||
public String getHistoryTable() {
|
||||
return historyTableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the included columns.
|
||||
*/
|
||||
public List<String> getColumns() {
|
||||
return includedColumns;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite;
|
||||
import com.avaje.ebean.dbmigration.model.MTable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* H2 history support using DB triggers to maintain a history table.
|
||||
@@ -28,40 +27,20 @@ public class H2HistoryDdl extends DbTriggerBasedHistoryDdl {
|
||||
protected void createTriggers(DdlWrite writer, MTable table) throws IOException {
|
||||
|
||||
String baseTableName = table.getName();
|
||||
String historyTableName = historyTableName(baseTableName);
|
||||
List<String> includedColumns = includedColumnNames(table);
|
||||
|
||||
DdlBuffer apply = writer.applyHistory();
|
||||
|
||||
addCreateTrigger(apply, updateTriggerName(baseTableName), baseTableName, historyTableName, includedColumns);
|
||||
addCreateTrigger(apply, updateTriggerName(baseTableName), baseTableName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void regenerateHistoryTriggers(DdlWrite writer, MTable table, HistoryTableUpdate update) throws IOException {
|
||||
protected void updateHistoryTriggers(DbTriggerUpdate update) throws IOException {
|
||||
|
||||
String baseTableName = table.getName();
|
||||
String historyTableName = historyTableName(baseTableName);
|
||||
List<String> includedColumns = includedColumnNames(table);
|
||||
|
||||
DdlBuffer apply = writer.applyHistory();
|
||||
|
||||
apply.append("-- Regenerated ").newLine();
|
||||
apply.append("-- changes: ").append(update.description()).newLine();
|
||||
|
||||
dropTriggers(apply, baseTableName);
|
||||
addCreateTrigger(apply, updateTriggerName(baseTableName), baseTableName, historyTableName, includedColumns);
|
||||
|
||||
// put a reverted version into the rollback buffer
|
||||
update.toRevertedColumns(includedColumns);
|
||||
|
||||
DdlBuffer rollback = writer.rollback();
|
||||
rollback.append("-- Revert regenerated ").newLine();
|
||||
rollback.append("-- revert changes: ").append(update.description()).newLine();
|
||||
dropTriggers(rollback, baseTableName);
|
||||
addCreateTrigger(rollback, updateTriggerName(baseTableName), baseTableName, historyTableName, includedColumns);
|
||||
DdlBuffer buffer = update.historyBuffer();
|
||||
dropTriggers(buffer, update.getBaseTable());
|
||||
addCreateTrigger(buffer, updateTriggerName(update.getBaseTable()), update.getBaseTable());
|
||||
}
|
||||
|
||||
private void addCreateTrigger(DdlBuffer apply, String triggerName, String baseTable, String historyTable, List<String> includedColumns) throws IOException {
|
||||
private void addCreateTrigger(DdlBuffer apply, String triggerName, String baseTable) throws IOException {
|
||||
|
||||
// Note that this does not take into account the historyTable name (excepts _history suffix) and
|
||||
// does not take into account excluded columns (all columns included in history)
|
||||
|
||||
+64
-19
@@ -21,10 +21,12 @@ public class HistoryTableUpdate {
|
||||
EXCLUDE
|
||||
}
|
||||
|
||||
public static class Column {
|
||||
private static class Column {
|
||||
|
||||
final Change change;
|
||||
|
||||
final String column;
|
||||
|
||||
public final Change change;
|
||||
public final String column;
|
||||
public Column(Change change, String column) {
|
||||
this.change = change;
|
||||
this.column = column;
|
||||
@@ -34,18 +36,23 @@ public class HistoryTableUpdate {
|
||||
return change.name().toLowerCase()+" "+column;
|
||||
}
|
||||
|
||||
public void apply(List<String> includedColumns) {
|
||||
private boolean isChangeFor(boolean apply) {
|
||||
return apply ? change != Change.DROP : change == Change.DROP;
|
||||
}
|
||||
|
||||
private void revert(List<String> includedColumns) {
|
||||
switch (change) {
|
||||
case ADD:
|
||||
case INCLUDE: {
|
||||
includedColumns.remove(column);
|
||||
break;
|
||||
}
|
||||
case DROP:
|
||||
case EXCLUDE: {
|
||||
includedColumns.add(column);
|
||||
break;
|
||||
}
|
||||
case DROP:
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected change "+change);
|
||||
}
|
||||
@@ -63,29 +70,73 @@ public class HistoryTableUpdate {
|
||||
this.baseTable = baseTable;
|
||||
}
|
||||
|
||||
private boolean isChangeFor(boolean apply) {
|
||||
for (Column columnChange : columnChanges) {
|
||||
if (columnChange.isChangeFor(apply)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the change includes apply changes (ADD, INCLUDE, EXCLUDE).
|
||||
*/
|
||||
public boolean hasApplyChanges() {
|
||||
return isChangeFor(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the change includes DROP column.
|
||||
*/
|
||||
public boolean hasDropChanges() {
|
||||
return isChangeFor(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a description of the changes that cause the history trigger/function
|
||||
* to be regenerated (added, dropped, included or excluded columns).
|
||||
* to be regenerated (added, included or excluded columns).
|
||||
*/
|
||||
public String description() {
|
||||
public String descriptionForApply() {
|
||||
return descriptionFor(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a description of the changes that cause the history trigger/function
|
||||
* to be regenerated in the drop script (dropped columns only).
|
||||
*/
|
||||
public String descriptionForDrop() {
|
||||
return descriptionFor(false);
|
||||
}
|
||||
|
||||
private String descriptionFor(boolean apply) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(90);
|
||||
for (int i = 0; i < columnChanges.size(); i++) {
|
||||
if (i > 0) {
|
||||
sb.append(", ");
|
||||
boolean first = true;
|
||||
for (Column column : columnChanges) {
|
||||
if (column.isChangeFor(apply)) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append(column.description());
|
||||
}
|
||||
sb.append(columnChanges.get(i).description());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the apply changes which equates to removing any newly added or
|
||||
* included columns.
|
||||
*/
|
||||
public void toRevertedColumns(List<String> includedColumns) {
|
||||
|
||||
for (Column columnChange : columnChanges) {
|
||||
columnChange.apply(includedColumns);
|
||||
columnChange.revert(includedColumns);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a comment for column added, dropped, included or excluded.
|
||||
*/
|
||||
@@ -100,10 +151,4 @@ public class HistoryTableUpdate {
|
||||
return baseTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the comments.
|
||||
*/
|
||||
public List<Column> getColumnChanges() {
|
||||
return columnChanges;
|
||||
}
|
||||
}
|
||||
|
||||
+20
-40
@@ -5,7 +5,6 @@ import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite;
|
||||
import com.avaje.ebean.dbmigration.model.MTable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* MySql history support using DB triggers to maintain a history table.
|
||||
@@ -26,67 +25,48 @@ public class MySqlHistoryDdl extends DbTriggerBasedHistoryDdl {
|
||||
@Override
|
||||
protected void createTriggers(DdlWrite writer, MTable table) throws IOException {
|
||||
|
||||
String baseTableName = table.getName();
|
||||
String historyTableName = historyTableName(baseTableName);
|
||||
List<String> includedColumns = includedColumnNames(table);
|
||||
DbTriggerUpdate update = createDbTriggerUpdate(writer, table);
|
||||
|
||||
DdlBuffer apply = writer.applyHistory();
|
||||
update.prepare(DdlWrite.Mode.APPLY, columnNamesForApply(table));
|
||||
|
||||
addBeforeUpdate(apply, updateTriggerName(baseTableName), baseTableName, historyTableName, includedColumns);
|
||||
addBeforeDelete(apply, deleteTriggerName(baseTableName), baseTableName, historyTableName, includedColumns);
|
||||
addBeforeUpdate(updateTriggerName(update.getBaseTable()), update);
|
||||
addBeforeDelete(deleteTriggerName(update.getBaseTable()), update);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void regenerateHistoryTriggers(DdlWrite writer, MTable table, HistoryTableUpdate update) throws IOException {
|
||||
protected void updateHistoryTriggers(DbTriggerUpdate update) throws IOException {
|
||||
|
||||
String baseTableName = table.getName();
|
||||
String historyTableName = historyTableName(baseTableName);
|
||||
List<String> includedColumns = includedColumnNames(table);
|
||||
DdlBuffer buffer = update.historyBuffer();
|
||||
String baseTable = update.getBaseTable();
|
||||
|
||||
DdlBuffer apply = writer.applyHistory();
|
||||
|
||||
apply.append("-- Regenerated ").newLine();
|
||||
apply.append("-- changes: ").append(update.description()).newLine();
|
||||
// lock the base table while we drop and recreate the triggers
|
||||
apply.append("lock tables ").append(baseTableName).append(" write").endOfStatement();
|
||||
dropTriggers(apply, baseTableName);
|
||||
addBeforeUpdate(apply, updateTriggerName(baseTableName), baseTableName, historyTableName, includedColumns);
|
||||
addBeforeDelete(apply, deleteTriggerName(baseTableName), baseTableName, historyTableName, includedColumns);
|
||||
apply.append("unlock tables").endOfStatement();
|
||||
|
||||
// put a reverted version into the rollback buffer
|
||||
update.toRevertedColumns(includedColumns);
|
||||
|
||||
DdlBuffer rollback = writer.rollback();
|
||||
rollback.append("-- Revert regenerated ").newLine();
|
||||
rollback.append("-- revert changes: ").append(update.description()).newLine();
|
||||
// lock the base table while we drop and recreate the triggers
|
||||
rollback.append("lock tables ").append(baseTableName).append(" write").endOfStatement();
|
||||
dropTriggers(rollback, baseTableName);
|
||||
addBeforeUpdate(rollback, updateTriggerName(baseTableName), baseTableName, historyTableName, includedColumns);
|
||||
addBeforeDelete(rollback, deleteTriggerName(baseTableName), baseTableName, historyTableName, includedColumns);
|
||||
rollback.append("unlock tables").endOfStatement();
|
||||
buffer.append("lock tables ").append(baseTable).append(" write").endOfStatement();
|
||||
dropTriggers(buffer, baseTable);
|
||||
addBeforeUpdate(updateTriggerName(baseTable), update);
|
||||
addBeforeDelete(deleteTriggerName(baseTable), update);
|
||||
buffer.append("unlock tables").endOfStatement();
|
||||
}
|
||||
|
||||
private void addBeforeUpdate(DdlBuffer apply, String triggerName, String baseTable, String historyTable, List<String> includedColumns) throws IOException {
|
||||
private void addBeforeUpdate(String triggerName, DbTriggerUpdate update) throws IOException {
|
||||
|
||||
DdlBuffer apply = update.historyBuffer();
|
||||
apply
|
||||
.append("delimiter $$").newLine()
|
||||
.append("create trigger ").append(triggerName).append(" before update on ").append(baseTable)
|
||||
.append("create trigger ").append(triggerName).append(" before update on ").append(update.getBaseTable())
|
||||
.append(" for each row begin").newLine();
|
||||
appendInsertIntoHistory(apply, historyTable, includedColumns);
|
||||
appendInsertIntoHistory(apply, update.getHistoryTable(), update.getColumns());
|
||||
apply
|
||||
.append(" set NEW.").append(sysPeriod).append("_start = now(6)").endOfStatement()
|
||||
.append("end$$").newLine();
|
||||
}
|
||||
|
||||
private void addBeforeDelete(DdlBuffer apply, String triggerName, String baseTable, String historyTable, List<String> includedColumns) throws IOException {
|
||||
private void addBeforeDelete(String triggerName, DbTriggerUpdate update) throws IOException {
|
||||
|
||||
DdlBuffer apply = update.historyBuffer();
|
||||
apply
|
||||
.append("delimiter $$").newLine()
|
||||
.append("create trigger ").append(triggerName).append(" before delete on ").append(baseTable)
|
||||
.append("create trigger ").append(triggerName).append(" before delete on ").append(update.getBaseTable())
|
||||
.append(" for each row begin").newLine();
|
||||
appendInsertIntoHistory(apply, historyTable, includedColumns);
|
||||
appendInsertIntoHistory(apply, update.getHistoryTable(), update.getColumns());
|
||||
apply.append("end$$").newLine();
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ public class NoHistorySupportDdl implements PlatformHistoryDdl {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void regenerateHistoryTriggers(DdlWrite write, HistoryTableUpdate update) {
|
||||
public void updateTriggers(DdlWrite write, HistoryTableUpdate update) {
|
||||
// does nothing
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ public class PlatformDdl {
|
||||
* Regenerate the history triggers (or function) due to a column being added/dropped/excluded or included.
|
||||
*/
|
||||
public void regenerateHistoryTriggers(DdlWrite write, HistoryTableUpdate update) throws IOException {
|
||||
historyDdl.regenerateHistoryTriggers(write, update);
|
||||
historyDdl.updateTriggers(write, update);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -36,5 +36,5 @@ public interface PlatformHistoryDdl {
|
||||
/**
|
||||
* Regenerate the history triggers/stored function due to column added/dropped/included or excluded.
|
||||
*/
|
||||
void regenerateHistoryTriggers(DdlWrite write, HistoryTableUpdate baseTable) throws IOException;
|
||||
void updateTriggers(DdlWrite write, HistoryTableUpdate baseTable) throws IOException;
|
||||
}
|
||||
|
||||
+29
-28
@@ -14,7 +14,7 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl {
|
||||
|
||||
|
||||
public PostgresHistoryDdl() {
|
||||
this.currentTimestamp = "current_timestamp";
|
||||
this.currentTimestamp = "statement_timestamp()";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,6 +45,11 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendSysPeriodColumns(DdlBuffer apply, String prefix) throws IOException {
|
||||
appendColumnName(apply, prefix, sysPeriod);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dropSysPeriodColumns(DdlBuffer buffer, String baseTableName) throws IOException {
|
||||
buffer.append("alter table ").append(baseTableName).append(" drop column ").append(sysPeriod).endOfStatement();
|
||||
@@ -72,7 +77,7 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl {
|
||||
buffer.end();
|
||||
}
|
||||
|
||||
protected void addFunction(DdlBuffer apply, String procedureName, String historyTable, List<String> includedColumns) throws IOException {
|
||||
protected void createOrReplaceFunction(DdlBuffer apply, String procedureName, String historyTable, List<String> includedColumns) throws IOException {
|
||||
apply
|
||||
.append("create or replace function ").append(procedureName).append("() returns trigger as $$").newLine()
|
||||
.append("begin").newLine();
|
||||
@@ -80,7 +85,7 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl {
|
||||
.append(" if (TG_OP = 'UPDATE') then").newLine();
|
||||
appendInsertIntoHistory(apply, historyTable, includedColumns);
|
||||
apply
|
||||
.append(" NEW.").append(sysPeriod).append(" = tstzrange(CURRENT_TIMESTAMP,null);").newLine()
|
||||
.append(" NEW.").append(sysPeriod).append(" = tstzrange(").append(currentTimestamp).append(",null);").newLine()
|
||||
.append(" return new;").newLine();
|
||||
apply
|
||||
.append(" elsif (TG_OP = 'DELETE') then").newLine();
|
||||
@@ -96,40 +101,36 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void regenerateHistoryTriggers(DdlWrite writer, MTable table, HistoryTableUpdate update) throws IOException {
|
||||
|
||||
// just replace the stored function with 'create or replace'
|
||||
addStoredFunction(writer, table, update);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addStoredFunction(DdlWrite writer, MTable table, HistoryTableUpdate update) throws IOException {
|
||||
protected void createStoredFunction(DdlWrite writer, MTable table) throws IOException {
|
||||
|
||||
String procedureName = procedureName(table.getName());
|
||||
String historyTable = historyTableName(table.getName());
|
||||
|
||||
List<String> includedColumns = includedColumnNames(table);
|
||||
List<String> columnNames = columnNamesForApply(table);
|
||||
createOrReplaceFunction(writer.applyHistory(), procedureName, historyTable, columnNames);
|
||||
}
|
||||
|
||||
DdlBuffer apply = writer.applyHistory();
|
||||
@Override
|
||||
protected void updateHistoryTriggers(DbTriggerUpdate update) throws IOException {
|
||||
|
||||
if (update != null) {
|
||||
apply.append("-- Regenerated ").append(procedureName).newLine();
|
||||
apply.append("-- changes: ").append(update.description()).newLine();
|
||||
}
|
||||
String procedureName = procedureName(update.getBaseTable());
|
||||
|
||||
addFunction(apply, procedureName, historyTable, includedColumns);
|
||||
recreateHistoryView(update);
|
||||
createOrReplaceFunction(update.historyBuffer(), procedureName, update.getHistoryTable(), update.getColumns());
|
||||
}
|
||||
|
||||
/**
|
||||
* For postgres we need to drop and recreate the view. Well, we could add columns to the end of the view
|
||||
* but otherwise we need to drop and create it.
|
||||
*/
|
||||
private void recreateHistoryView(DbTriggerUpdate update) throws IOException {
|
||||
|
||||
if (update != null) {
|
||||
// put a reverted version into the rollback buffer
|
||||
update.toRevertedColumns(includedColumns);
|
||||
DdlBuffer buffer = update.dropDependencyBuffer();
|
||||
// we need to drop the view early/first before any changes to the tables etc
|
||||
buffer.append("drop view if exists ").append(update.getBaseTable()).append(viewSuffix).endOfStatement();
|
||||
|
||||
DdlBuffer rollback = writer.rollback();
|
||||
rollback.append("-- Revert regenerated ").append(procedureName).newLine();
|
||||
rollback.append("-- revert changes: ").append(update.description()).newLine();
|
||||
|
||||
addFunction(rollback, procedureName, historyTable, includedColumns);
|
||||
}
|
||||
// recreate the view with specific columns specified (the columns generally are not dropped until later)
|
||||
createWithHistoryView(update);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -137,7 +138,7 @@ public class PostgresHistoryDdl extends DbTriggerBasedHistoryDdl {
|
||||
|
||||
buffer.append(" insert into ").append(historyTable).append(" (").append(sysPeriod).append(",");
|
||||
appendColumnNames(buffer, columns, "");
|
||||
buffer.append(") values (tstzrange(lower(OLD.").append(sysPeriod).append("), current_timestamp), ");
|
||||
buffer.append(") values (tstzrange(lower(OLD.").append(sysPeriod).append("), ").append(currentTimestamp).append("), ");
|
||||
appendColumnNames(buffer, columns, "OLD.");
|
||||
buffer.append(");").newLine();
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import javax.xml.bind.annotation.XmlType;
|
||||
* <attGroup ref="{http://ebean-orm.github.io/xml/ns/dbmigration}tablespaceAttributes"/>
|
||||
* <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="withHistory" type="{http://www.w3.org/2001/XMLSchema}boolean" />
|
||||
* <attribute name="draft" type="{http://www.w3.org/2001/XMLSchema}boolean" />
|
||||
* <attribute name="identityType" type="{http://ebean-orm.github.io/xml/ns/dbmigration}identityType" />
|
||||
* <attribute name="sequenceName" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="sequenceInitial" type="{http://www.w3.org/2001/XMLSchema}positiveInteger" />
|
||||
@@ -59,6 +60,8 @@ public class CreateTable {
|
||||
protected String name;
|
||||
@XmlAttribute(name = "withHistory")
|
||||
protected Boolean withHistory;
|
||||
@XmlAttribute(name = "draft")
|
||||
protected Boolean draft;
|
||||
@XmlAttribute(name = "identityType")
|
||||
protected IdentityType identityType;
|
||||
@XmlAttribute(name = "sequenceName")
|
||||
@@ -213,6 +216,30 @@ public class CreateTable {
|
||||
this.withHistory = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the draft property.
|
||||
*
|
||||
* @return
|
||||
* possible object is
|
||||
* {@link Boolean }
|
||||
*
|
||||
*/
|
||||
public Boolean isDraft() {
|
||||
return draft;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the draft property.
|
||||
*
|
||||
* @param value
|
||||
* allowed object is
|
||||
* {@link Boolean }
|
||||
*
|
||||
*/
|
||||
public void setDraft(Boolean value) {
|
||||
this.draft = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the identityType property.
|
||||
*
|
||||
|
||||
@@ -7,22 +7,46 @@ import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Marshaller;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Simple writer for output of the Migration/ChangeSet as an XML document.
|
||||
*/
|
||||
public class MigrationXmlWriter {
|
||||
|
||||
private final String comment;
|
||||
|
||||
public MigrationXmlWriter(String comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a Migration to a file as an xml document to the file.
|
||||
*/
|
||||
public void write(Migration migration, File file) {
|
||||
|
||||
try {
|
||||
|
||||
FileWriter writer = new FileWriter(file);
|
||||
writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n");
|
||||
if (comment != null) {
|
||||
writer.write("<!-- ");
|
||||
writer.write(comment);
|
||||
writer.write(" -->\n");
|
||||
}
|
||||
|
||||
JAXBContext jaxbContext = JAXBContext.newInstance(Migration.class);
|
||||
Marshaller marshaller = jaxbContext.createMarshaller();
|
||||
marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
|
||||
marshaller.marshal(migration, file);
|
||||
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
|
||||
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
|
||||
|
||||
marshaller.marshal(migration, writer);
|
||||
|
||||
writer.close();
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
||||
} catch (JAXBException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
||||
@@ -283,7 +283,6 @@ public class MColumn {
|
||||
/**
|
||||
* Compare the column meta data and return true if there is a change that means
|
||||
* the history table column needs
|
||||
|
||||
*/
|
||||
public void compare(ModelDiff modelDiff, MTable table, MColumn newColumn) {
|
||||
|
||||
|
||||
@@ -10,13 +10,15 @@ import com.avaje.ebean.dbmigration.migration.DropHistoryTable;
|
||||
import com.avaje.ebean.dbmigration.migration.DropTable;
|
||||
import com.avaje.ebean.dbmigration.migration.IdentityType;
|
||||
import com.avaje.ebean.dbmigration.migration.UniqueConstraint;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -38,6 +40,8 @@ import java.util.Set;
|
||||
*/
|
||||
public class MTable {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MTable.class);
|
||||
|
||||
/**
|
||||
* Table name.
|
||||
*/
|
||||
@@ -118,6 +122,8 @@ public class MTable {
|
||||
*/
|
||||
private AddColumn addColumn;
|
||||
|
||||
private List<DroppedColumn> droppedColumns = new ArrayList<DroppedColumn>();
|
||||
|
||||
/**
|
||||
* Create a copy of this table structure as a 'draft' table.
|
||||
*
|
||||
@@ -133,7 +139,7 @@ public class MTable {
|
||||
// compoundUniqueConstraints
|
||||
draftTable.identityType = identityType;
|
||||
|
||||
for (MColumn col: columns.values()) {
|
||||
for (MColumn col: allColumns()) {
|
||||
draftTable.addColumn(col.copyForDraft());
|
||||
}
|
||||
|
||||
@@ -150,6 +156,7 @@ public class MTable {
|
||||
this.tablespace = createTable.getTablespace();
|
||||
this.indexTablespace = createTable.getIndexTablespace();
|
||||
this.withHistory = Boolean.TRUE.equals(createTable.isWithHistory());
|
||||
this.draft = Boolean.TRUE.equals(createTable.isDraft());
|
||||
this.sequenceName = createTable.getSequenceName();
|
||||
this.sequenceInitial = toInt(createTable.getSequenceInitial());
|
||||
this.sequenceAllocate = toInt(createTable.getSequenceAllocate());
|
||||
@@ -194,8 +201,11 @@ public class MTable {
|
||||
if (withHistory) {
|
||||
createTable.setWithHistory(Boolean.TRUE);
|
||||
}
|
||||
if (draft) {
|
||||
createTable.setDraft(Boolean.TRUE);
|
||||
}
|
||||
|
||||
for (MColumn column : this.columns.values()) {
|
||||
for (MColumn column : allColumns()) {
|
||||
// filter out draftOnly columns from the base table
|
||||
if (draft || !column.isDraftOnly()) {
|
||||
createTable.getColumn().add(column.createColumn());
|
||||
@@ -244,26 +254,33 @@ public class MTable {
|
||||
|
||||
addColumn = null;
|
||||
|
||||
Set<String> mappedColumns = new LinkedHashSet<String>();
|
||||
Map<String, MColumn> newColumnMap = newTable.getColumns();
|
||||
|
||||
Collection<MColumn> newColumns = newTable.getColumns().values();
|
||||
for (MColumn newColumn : newColumns) {
|
||||
MColumn localColumn = columns.get(newColumn.getName());
|
||||
// compare newColumns to existing columns (look for new and diff columns)
|
||||
for (MColumn newColumn : newColumnMap.values()) {
|
||||
MColumn localColumn = getColumn(newColumn.getName());
|
||||
if (localColumn == null) {
|
||||
diffNewColumn(newColumn);
|
||||
// can ignore if draftOnly column and non-draft table
|
||||
if (!newColumn.isDraftOnly() || draft) {
|
||||
diffNewColumn(newColumn);
|
||||
}
|
||||
} else {
|
||||
// note that if there are alter column changes in here then
|
||||
// the table withHistory is taken into account
|
||||
localColumn.compare(modelDiff, this, newColumn);
|
||||
mappedColumns.add(newColumn.getName());
|
||||
}
|
||||
}
|
||||
|
||||
Collection<MColumn> existingColumns = columns.values();
|
||||
for (MColumn existingColumn : existingColumns) {
|
||||
if (!mappedColumns.contains(existingColumn.getName())) {
|
||||
diffDropColumn(modelDiff, existingColumn);
|
||||
// compare existing columns (look for dropped columns)
|
||||
int columnPosition = 0;
|
||||
for (MColumn existingColumn : allColumns()) {
|
||||
MColumn newColumn = newColumnMap.get(existingColumn.getName());
|
||||
if (newColumn == null) {
|
||||
diffDropColumn(modelDiff, existingColumn, columnPosition, newTable);
|
||||
} else if (newColumn.isDraftOnly() && !draft) {
|
||||
// effectively a drop column (draft only column on a non-draft table)
|
||||
logger.trace("... drop column {} from table {} as now draftOnly", newColumn.getName(), name);
|
||||
diffDropColumn(modelDiff, existingColumn, columnPosition, newTable);
|
||||
}
|
||||
columnPosition++;
|
||||
}
|
||||
|
||||
if (addColumn != null) {
|
||||
@@ -287,7 +304,7 @@ public class MTable {
|
||||
public void apply(AlterColumn alterColumn) {
|
||||
checkTableName(alterColumn.getTableName());
|
||||
String columnName = alterColumn.getColumnName();
|
||||
MColumn existingColumn = columns.get(columnName);
|
||||
MColumn existingColumn = getColumn(columnName);
|
||||
if (existingColumn == null) {
|
||||
throw new IllegalStateException("Column [" + columnName + "] does not exist for AlterColumn change?");
|
||||
}
|
||||
@@ -299,7 +316,10 @@ public class MTable {
|
||||
*/
|
||||
public void apply(DropColumn dropColumn) {
|
||||
checkTableName(dropColumn.getTableName());
|
||||
columns.remove(dropColumn.getColumnName());
|
||||
MColumn removed = columns.remove(dropColumn.getColumnName());
|
||||
if (removed == null) {
|
||||
throw new IllegalStateException("Column [" + dropColumn.getColumnName() + "] does not exist for DropColumn change on table [" + dropColumn.getTableName() + "]?");
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
@@ -313,10 +333,6 @@ public class MTable {
|
||||
return draft;
|
||||
}
|
||||
|
||||
public String getPkName() {
|
||||
return pkName;
|
||||
}
|
||||
|
||||
public void setPkName(String pkName) {
|
||||
this.pkName = pkName;
|
||||
}
|
||||
@@ -345,7 +361,43 @@ public class MTable {
|
||||
this.withHistory = withHistory;
|
||||
}
|
||||
|
||||
public Map<String, MColumn> getColumns() {
|
||||
public List<String> allHistoryColumns(boolean includeDropped) {
|
||||
|
||||
List<String> columnNames = new ArrayList<String>(columns.size());
|
||||
for (MColumn column : columns.values()) {
|
||||
if (column.isIncludeInHistory()) {
|
||||
columnNames.add(column.getName());
|
||||
}
|
||||
}
|
||||
if (includeDropped && !droppedColumns.isEmpty()) {
|
||||
Collections.sort(droppedColumns);
|
||||
for (DroppedColumn droppedColumn : droppedColumns) {
|
||||
if (droppedColumn.columnPosition >= columnNames.size()) {
|
||||
columnNames.add(droppedColumn.name);
|
||||
} else {
|
||||
columnNames.add(droppedColumn.columnPosition, droppedColumn.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return columnNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all the columns (excluding columns marked as dropped).
|
||||
*/
|
||||
public Collection<MColumn> allColumns() {
|
||||
|
||||
return columns.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the column by name.
|
||||
*/
|
||||
public MColumn getColumn(String name) {
|
||||
return columns.get(name);
|
||||
}
|
||||
|
||||
private Map<String, MColumn> getColumns() {
|
||||
return columns;
|
||||
}
|
||||
|
||||
@@ -357,26 +409,14 @@ public class MTable {
|
||||
return compoundKeys;
|
||||
}
|
||||
|
||||
public String getSequenceName() {
|
||||
return sequenceName;
|
||||
}
|
||||
|
||||
public void setSequenceName(String sequenceName) {
|
||||
this.sequenceName = sequenceName;
|
||||
}
|
||||
|
||||
public int getSequenceInitial() {
|
||||
return sequenceInitial;
|
||||
}
|
||||
|
||||
public void setSequenceInitial(int sequenceInitial) {
|
||||
this.sequenceInitial = sequenceInitial;
|
||||
}
|
||||
|
||||
public int getSequenceAllocate() {
|
||||
return sequenceAllocate;
|
||||
}
|
||||
|
||||
public void setSequenceAllocate(int sequenceAllocate) {
|
||||
this.sequenceAllocate = sequenceAllocate;
|
||||
}
|
||||
@@ -399,22 +439,12 @@ public class MTable {
|
||||
this.identityType = identityType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identity type to use for this table.
|
||||
* <p>
|
||||
* If set then this overrides the platform default so for UUID generated values
|
||||
* or DB's supporting both sequences and autoincrement.
|
||||
*/
|
||||
public IdentityType getIdentityType() {
|
||||
return identityType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of columns that make the primary key.
|
||||
*/
|
||||
public List<MColumn> primaryKeyColumns() {
|
||||
List<MColumn> pk = new ArrayList<MColumn>(3);
|
||||
for (MColumn column : columns.values()) {
|
||||
for (MColumn column : allColumns()) {
|
||||
if (column.isPrimaryKey()) {
|
||||
pk.add(column);
|
||||
}
|
||||
@@ -473,7 +503,7 @@ public class MTable {
|
||||
*/
|
||||
public MColumn addColumn(String dbCol, String columnDefn, boolean notnull) {
|
||||
|
||||
MColumn existingColumn = columns.get(dbCol);
|
||||
MColumn existingColumn = getColumn(dbCol);
|
||||
if (existingColumn != null) {
|
||||
if (notnull) {
|
||||
existingColumn.setNotnull(true);
|
||||
@@ -507,20 +537,46 @@ public class MTable {
|
||||
/**
|
||||
* Add a 'drop column' to the diff.
|
||||
*/
|
||||
private void diffDropColumn(ModelDiff modelDiff, MColumn existingColumn) {
|
||||
private void diffDropColumn(ModelDiff modelDiff, MColumn existingColumn, int columnPosition, MTable newTable) {
|
||||
|
||||
DropColumn dropColumn = new DropColumn();
|
||||
dropColumn.setTableName(name);
|
||||
dropColumn.setColumnName(existingColumn.getName());
|
||||
if (withHistory) {
|
||||
if (withHistory && !existingColumn.isHistoryExclude()) {
|
||||
// These dropColumns should occur on the history
|
||||
// table as well as the base table
|
||||
dropColumn.setWithHistory(Boolean.TRUE);
|
||||
newTable.registerDroppedColumn(existingColumn.getName(), columnPosition);
|
||||
}
|
||||
|
||||
modelDiff.addDropColumn(dropColumn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a dropped column with it's previous column position.
|
||||
* We need this for history triggers and views as we don't actually drop the
|
||||
* column until the 'drop script' is run so the 'apply script' for history changes
|
||||
* still needs to include the columns that are going to be dropped.
|
||||
*/
|
||||
protected void registerDroppedColumn(String name, int columnPosition) {
|
||||
droppedColumns.add(new DroppedColumn(name, columnPosition));
|
||||
}
|
||||
|
||||
private static class DroppedColumn implements Comparable<DroppedColumn> {
|
||||
final String name;
|
||||
final int columnPosition;
|
||||
|
||||
DroppedColumn(String name, int columnPosition) {
|
||||
this.name = name;
|
||||
this.columnPosition = columnPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(DroppedColumn o) {
|
||||
return Integer.compare(o.columnPosition, columnPosition);
|
||||
}
|
||||
}
|
||||
|
||||
private int toInt(BigInteger value) {
|
||||
return (value == null) ? 0 : value.intValue();
|
||||
}
|
||||
@@ -563,7 +619,7 @@ public class MTable {
|
||||
*/
|
||||
public void adjustReferences(ModelContainer modelContainer) {
|
||||
|
||||
Collection<MColumn> cols = columns.values();
|
||||
Collection<MColumn> cols = allColumns();
|
||||
for (MColumn col : cols) {
|
||||
String references = col.getReferences();
|
||||
if (references != null) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.avaje.ebean.dbmigration.model;
|
||||
import com.avaje.ebean.config.DbMigrationConfig;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.dbmigration.ddlgeneration.DdlBuffer;
|
||||
import com.avaje.ebean.dbmigration.ddlgeneration.DdlHandler;
|
||||
import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite;
|
||||
import com.avaje.ebean.dbmigration.migration.ChangeSet;
|
||||
@@ -116,6 +117,8 @@ public class PlatformDdlWriter {
|
||||
protected void writeApplyDdl(Writer writer, DdlWrite write) throws IOException {
|
||||
|
||||
// merge the apply buffers in the appropriate order
|
||||
prependDropDependencies(writer, write.applyDropDependencies());
|
||||
writer.append("-- apply changes\n");
|
||||
writer.append(write.apply().getBuffer());
|
||||
writer.append(write.applyForeignKeys().getBuffer());
|
||||
writer.append(write.applyHistory().getBuffer());
|
||||
@@ -127,6 +130,8 @@ public class PlatformDdlWriter {
|
||||
protected void writeApplyRollbackDdl(Writer writer, DdlWrite write) throws IOException {
|
||||
|
||||
// merge the rollback buffers in the appropriate order
|
||||
prependDropDependencies(writer, write.rollbackDropDependencies());
|
||||
writer.append("-- reverse changes\n");
|
||||
writer.append(write.rollbackForeignKeys().getBuffer());
|
||||
writer.append(write.rollback().getBuffer());
|
||||
}
|
||||
@@ -137,10 +142,19 @@ public class PlatformDdlWriter {
|
||||
protected void writeDropDdl(Writer writer, DdlWrite write) throws IOException {
|
||||
|
||||
// merge the rollback buffers in the appropriate order
|
||||
prependDropDependencies(writer, write.dropDropDependencies());
|
||||
writer.append(write.dropHistory().getBuffer());
|
||||
writer.append(write.drop().getBuffer());
|
||||
}
|
||||
|
||||
private void prependDropDependencies(Writer writer, DdlBuffer buffer) throws IOException {
|
||||
if (!buffer.isEmpty()) {
|
||||
writer.append("-- drop dependencies\n");
|
||||
writer.append(buffer.getBuffer());
|
||||
writer.append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the platform specific DdlHandler (to generate DDL).
|
||||
*/
|
||||
|
||||
@@ -43,6 +43,10 @@ public class ModelBuildContext {
|
||||
model.adjustDraftReferences();
|
||||
}
|
||||
|
||||
public String normaliseTable(String baseTable) {
|
||||
return constraintNaming.normaliseTable(baseTable);
|
||||
}
|
||||
|
||||
public String primaryKeyName(String tableName) {
|
||||
return maxLength(constraintNaming.primaryKeyName(tableName), 0);
|
||||
}
|
||||
@@ -154,7 +158,7 @@ public class ModelBuildContext {
|
||||
int fkCount = 0;
|
||||
int ixCount = 0;
|
||||
int uqCount = 0;
|
||||
Collection<MColumn> cols = draftTable.getColumns().values();
|
||||
Collection<MColumn> cols = draftTable.allColumns();
|
||||
for (MColumn col: cols) {
|
||||
if (col.getForeignKeyName() != null) {
|
||||
// Note that we adjust the 'references' table later in a second pass
|
||||
|
||||
+1
-2
@@ -64,8 +64,7 @@ public class ModelBuildIntersectionTable {
|
||||
private void buildFkConstraints(BeanDescriptor<?> desc, TableJoinColumn[] columns, boolean direction) {
|
||||
|
||||
String tableName = intersectionTableJoin.getTable();
|
||||
String baseTable = desc.getBaseTable();
|
||||
|
||||
String baseTable = ctx.normaliseTable(desc.getBaseTable());
|
||||
String fkName = ctx.foreignKeyConstraintName(tableName, baseTable, ++countForeignKey);
|
||||
String fkIndex = ctx.foreignKeyIndexName(tableName, baseTable, countForeignKey);
|
||||
|
||||
|
||||
+1
-1
@@ -84,7 +84,7 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
|
||||
table.setPkName(determinePrimaryKeyName());
|
||||
|
||||
// check if indexes on foreign keys should be suppressed
|
||||
for (MColumn column : table.getColumns().values()) {
|
||||
for (MColumn column : table.allColumns()) {
|
||||
if (hasValue(column.getForeignKeyIndex())) {
|
||||
if (indexSet.contains(column.getName())) {
|
||||
// suppress index on foreign key as there is already
|
||||
|
||||
@@ -1,18 +1,27 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Request for loading ManyToOne and OneToOne relationships.
|
||||
*/
|
||||
public class LoadBeanRequest extends LoadRequest {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(LoadBeanRequest.class);
|
||||
|
||||
private final List<EntityBeanIntercept> batch;
|
||||
|
||||
private final LoadBeanBuffer LoadBuffer;
|
||||
private final LoadBeanBuffer loadBuffer;
|
||||
|
||||
private final String lazyLoadProperty;
|
||||
|
||||
@@ -32,20 +41,28 @@ public class LoadBeanRequest extends LoadRequest {
|
||||
this(LoadBuffer, parentRequest, false, null, false);
|
||||
}
|
||||
|
||||
private LoadBeanRequest(LoadBeanBuffer LoadBuffer, OrmQueryRequest<?> parentRequest, boolean lazy, String lazyLoadProperty, boolean loadCache) {
|
||||
private LoadBeanRequest(LoadBeanBuffer loadBuffer, OrmQueryRequest<?> parentRequest, boolean lazy,
|
||||
String lazyLoadProperty, boolean loadCache) {
|
||||
|
||||
super(parentRequest, lazy);
|
||||
this.LoadBuffer = LoadBuffer;
|
||||
this.batch = LoadBuffer.getBatch();
|
||||
this.loadBuffer = loadBuffer;
|
||||
this.batch = loadBuffer.getBatch();
|
||||
this.lazyLoadProperty = lazyLoadProperty;
|
||||
this.loadCache = loadCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getBeanType() {
|
||||
return loadBuffer.getBeanDescriptor().getBeanType();
|
||||
}
|
||||
|
||||
public boolean isLoadCache() {
|
||||
return loadCache;
|
||||
}
|
||||
|
||||
|
||||
public String getDescription() {
|
||||
return "path:" + LoadBuffer.getFullPath() + " batch:" + batch.size();
|
||||
return "path:" + loadBuffer.getFullPath() + " batch:" + batch.size();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,7 +76,7 @@ public class LoadBeanRequest extends LoadRequest {
|
||||
* Return the load context.
|
||||
*/
|
||||
public LoadBeanBuffer getLoadContext() {
|
||||
return LoadBuffer;
|
||||
return loadBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,4 +89,90 @@ public class LoadBeanRequest extends LoadRequest {
|
||||
public int getBatchSize() {
|
||||
return getLoadContext().getBatchSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of Id values for the beans in the lazy load buffer.
|
||||
*/
|
||||
public List<Object> getIdList(int batchSize) {
|
||||
|
||||
List<Object> idList = new ArrayList<Object>(batchSize);
|
||||
|
||||
BeanDescriptor<?> desc = loadBuffer.getBeanDescriptor();
|
||||
for (int i = 0; i < batch.size(); i++) {
|
||||
EntityBeanIntercept ebi = batch.get(i);
|
||||
EntityBean bean = ebi.getOwner();
|
||||
idList.add(desc.getId(bean));
|
||||
}
|
||||
|
||||
if (!idList.isEmpty()) {
|
||||
int extraIds = batchSize - batch.size();
|
||||
if (extraIds > 0) {
|
||||
// for performance make up the Id's to the batch size
|
||||
// so we get the same query (for Ebean and the db)
|
||||
Object firstId = idList.get(0);
|
||||
for (int i = 0; i < extraIds; i++) {
|
||||
// just add the first Id again
|
||||
idList.add(firstId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return idList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the query for lazy loading execution.
|
||||
*/
|
||||
public void configureQuery(SpiQuery<?> query, List<Object> idList) {
|
||||
|
||||
query.setMode(SpiQuery.Mode.LAZYLOAD_BEAN);
|
||||
query.setPersistenceContext(loadBuffer.getPersistenceContext());
|
||||
|
||||
String mode = isLazy() ? "+lazy" : "+query";
|
||||
query.setLoadDescription(mode, getDescription());
|
||||
|
||||
if (isLazy()) {
|
||||
// cascade the batch size (if set) for further lazy loading
|
||||
query.setLazyLoadBatchSize(getBatchSize());
|
||||
}
|
||||
|
||||
loadBuffer.configureQuery(query, lazyLoadProperty);
|
||||
|
||||
if (idList.size() == 1) {
|
||||
query.where().idEq(idList.get(0));
|
||||
} else {
|
||||
query.where().idIn(idList);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the beans into the L2 cache if that is requested and check for load failures due to deletes.
|
||||
*/
|
||||
public void postLoad(List<?> list) {
|
||||
|
||||
Set<Object> loadedIds = new HashSet<Object>();
|
||||
|
||||
BeanDescriptor<?> desc = loadBuffer.getBeanDescriptor();
|
||||
// collect Ids and maybe load bean cache
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
EntityBean loadedBean = (EntityBean) list.get(i);
|
||||
loadedIds.add(desc.getId(loadedBean));
|
||||
if (isLoadCache()) {
|
||||
desc.cacheBeanPutData(loadedBean);
|
||||
}
|
||||
}
|
||||
|
||||
if (lazyLoadProperty != null) {
|
||||
for (int i = 0; i < batch.size(); i++) {
|
||||
// check if the underlying row in DB was deleted. Mark the bean as 'failed' if
|
||||
// necessary but allow processing to continue until it is accessed by client code
|
||||
EntityBeanIntercept ebi = batch.get(i);
|
||||
Object id = desc.getId(ebi.getOwner());
|
||||
if (!loadedIds.contains(id)) {
|
||||
logger.info("Lazy loading unsuccessful for type:" + desc.getName() + " id:" + id + " - expecting when bean has been deleted");
|
||||
ebi.setLazyLoadFailure(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,25 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Request for loading Associated Many Beans.
|
||||
*/
|
||||
public class LoadManyRequest extends LoadRequest {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(LoadManyRequest.class);
|
||||
|
||||
private final List<BeanCollection<?>> batch;
|
||||
|
||||
private final LoadManyBuffer loadContext;
|
||||
@@ -40,6 +50,11 @@ public class LoadManyRequest extends LoadRequest {
|
||||
this.loadCache = loadCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getBeanType() {
|
||||
return loadContext.getBeanDescriptor().getBeanType();
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return "path:" + loadContext.getFullPath() + " size:" + batch.size();
|
||||
}
|
||||
@@ -83,4 +98,98 @@ public class LoadManyRequest extends LoadRequest {
|
||||
public int getBatchSize() {
|
||||
return loadContext.getBatchSize();
|
||||
}
|
||||
|
||||
private List<Object> getParentIdList(int batchSize) {
|
||||
|
||||
ArrayList<Object> idList = new ArrayList<Object>(batchSize);
|
||||
|
||||
BeanPropertyAssocMany<?> many = getMany();
|
||||
for (int i = 0; i < batch.size(); i++) {
|
||||
BeanCollection<?> bc = batch.get(i);
|
||||
idList.add(many.getParentId(bc.getOwnerBean()));
|
||||
}
|
||||
int extraIds = batchSize - batch.size();
|
||||
if (extraIds > 0) {
|
||||
Object firstId = idList.get(0);
|
||||
for (int i = 0; i < extraIds; i++) {
|
||||
idList.add(firstId);
|
||||
}
|
||||
}
|
||||
|
||||
return idList;
|
||||
}
|
||||
|
||||
private BeanPropertyAssocMany<?> getMany() {
|
||||
return loadContext.getBeanProperty();
|
||||
}
|
||||
|
||||
public SpiQuery<?> createQuery(EbeanServer server, int batchSize) {
|
||||
|
||||
BeanPropertyAssocMany<?> many = getMany();
|
||||
|
||||
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(many.getTargetType());
|
||||
String orderBy = many.getLazyFetchOrderBy();
|
||||
if (orderBy != null) {
|
||||
query.orderBy(orderBy);
|
||||
}
|
||||
|
||||
String extraWhere = many.getExtraWhere();
|
||||
if (extraWhere != null) {
|
||||
// replace special ${ta} placeholder with the base table alias
|
||||
// which is always t0 and add the extra where clause
|
||||
String ew = StringHelper.replaceString(extraWhere, "${ta}", "t0");
|
||||
query.where().raw(ew);
|
||||
}
|
||||
|
||||
query.setLazyLoadForParents(many);
|
||||
|
||||
List<Object> idList = getParentIdList(batchSize);
|
||||
many.addWhereParentIdIn(query, idList);
|
||||
|
||||
query.setPersistenceContext(loadContext.getPersistenceContext());
|
||||
|
||||
String mode = isLazy() ? "+lazy" : "+query";
|
||||
query.setLoadDescription(mode, getDescription());
|
||||
|
||||
if (isLazy()) {
|
||||
// cascade the batch size (if set) for further lazy loading
|
||||
query.setLazyLoadBatchSize(getBatchSize());
|
||||
}
|
||||
|
||||
// potentially changes the joins and selected properties
|
||||
loadContext.configureQuery(query);
|
||||
|
||||
if (isOnlyIds()) {
|
||||
// override to just select the Id values
|
||||
query.select(many.getTargetIdProperty());
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* After the query execution check for empty collections and load L2 cache if desired.
|
||||
*/
|
||||
public void postLoad() {
|
||||
|
||||
BeanDescriptor<?> desc = loadContext.getBeanDescriptor();
|
||||
BeanPropertyAssocMany<?> many = getMany();
|
||||
|
||||
// check for BeanCollection's that where never processed
|
||||
// in the +query or +lazy load due to no rows (predicates)
|
||||
for (int i = 0; i < batch.size(); i++) {
|
||||
BeanCollection<?> bc = batch.get(i);
|
||||
if (bc.checkEmptyLazyLoad()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
EntityBean ownerBean = bc.getOwnerBean();
|
||||
Object parentId = desc.getId(ownerBean);
|
||||
logger.debug("BeanCollection after lazy load was empty. type:" + ownerBean.getClass().getName() + " id:" + parentId + " owner:" + ownerBean);
|
||||
}
|
||||
} else if (isLoadCache()) {
|
||||
Object parentId = desc.getId(bc.getOwnerBean());
|
||||
desc.cacheManyPropPut(many, bc, parentId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,11 @@ public abstract class LoadRequest {
|
||||
this.lazy = lazy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the associated bean type for this load request.
|
||||
*/
|
||||
public abstract Class<?> getBeanType();
|
||||
|
||||
/**
|
||||
* Log the just executed secondary query with the 'root' query if 'logSecondaryQuery' is set to
|
||||
* true. This is for testing purposes to confirm the secondary query executes etc.
|
||||
|
||||
@@ -1,153 +1,153 @@
|
||||
package com.avaje.ebeaninternal.api;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
import com.avaje.ebeaninternal.server.query.SplitName;
|
||||
import com.avaje.ebeaninternal.server.query.SqlJoinType;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* Holds the joins needs to support the many where predicates.
|
||||
* These joins are independent of any 'fetch' joins on the many.
|
||||
*/
|
||||
public class ManyWhereJoins implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -6490181101871795417L;
|
||||
|
||||
private final TreeMap<String,PropertyJoin> joins = new TreeMap<String,PropertyJoin>();
|
||||
private static final long serialVersionUID = -6490181101871795417L;
|
||||
|
||||
private StringBuilder formulaProperties = new StringBuilder();
|
||||
|
||||
private boolean formulaWithJoin;
|
||||
private final TreeMap<String, PropertyJoin> joins = new TreeMap<String, PropertyJoin>();
|
||||
|
||||
private StringBuilder formulaProperties = new StringBuilder();
|
||||
|
||||
private boolean formulaWithJoin;
|
||||
|
||||
/**
|
||||
* 'Mode' indicating that joins added while this is true are required to be outer joins.
|
||||
*/
|
||||
private boolean requireOuterJoins;
|
||||
|
||||
/**
|
||||
* Return the current 'mode' indicating if outer joins are currently required or not.
|
||||
*/
|
||||
public boolean isRequireOuterJoins() {
|
||||
return requireOuterJoins;
|
||||
}
|
||||
private boolean requireOuterJoins;
|
||||
|
||||
/**
|
||||
* Set the 'mode' to be that joins added are required to be outer joins.
|
||||
* This is set during the evaluation of disjunction predicates.
|
||||
*/
|
||||
public void setRequireOuterJoins(boolean requireOuterJoins) {
|
||||
this.requireOuterJoins = requireOuterJoins;
|
||||
}
|
||||
/**
|
||||
* Return the current 'mode' indicating if outer joins are currently required or not.
|
||||
*/
|
||||
public boolean isRequireOuterJoins() {
|
||||
return requireOuterJoins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a many where join.
|
||||
*/
|
||||
public void add(ElPropertyDeploy elProp) {
|
||||
|
||||
String join = elProp.getElPrefix();
|
||||
BeanProperty p = elProp.getBeanProperty();
|
||||
if (p instanceof BeanPropertyAssocMany<?>){
|
||||
join = addManyToJoin(join, p.getName());
|
||||
}
|
||||
if (join != null){
|
||||
addJoin(join);
|
||||
if (p != null) {
|
||||
String secondaryTableJoinPrefix = p.getSecondaryTableJoinPrefix();
|
||||
if (secondaryTableJoinPrefix != null) {
|
||||
addJoin(join+"."+secondaryTableJoinPrefix);
|
||||
}
|
||||
}
|
||||
addParentJoins(join);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For 'many' properties we also need to add the name of the
|
||||
* many property to get the full logical name of the join.
|
||||
*/
|
||||
private String addManyToJoin(String join, String manyPropName){
|
||||
if (join == null){
|
||||
return manyPropName;
|
||||
} else {
|
||||
return join+"."+manyPropName;
|
||||
}
|
||||
}
|
||||
|
||||
private void addParentJoins(String join) {
|
||||
String[] split = SplitName.split(join);
|
||||
if (split[0] != null){
|
||||
addJoin(split[0]);
|
||||
addParentJoins(split[0]);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Set the 'mode' to be that joins added are required to be outer joins.
|
||||
* This is set during the evaluation of disjunction predicates.
|
||||
*/
|
||||
public void setRequireOuterJoins(boolean requireOuterJoins) {
|
||||
this.requireOuterJoins = requireOuterJoins;
|
||||
}
|
||||
|
||||
private void addJoin(String property) {
|
||||
SqlJoinType joinType = (requireOuterJoins) ? SqlJoinType.OUTER: SqlJoinType.INNER;
|
||||
joins.put(property, new PropertyJoin(property, joinType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there are no extra many where joins.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return joins.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the set of many where joins.
|
||||
*/
|
||||
public Collection<PropertyJoin> getPropertyJoins() {
|
||||
return joins.values();
|
||||
}
|
||||
/**
|
||||
* Add a many where join.
|
||||
*/
|
||||
public void add(ElPropertyDeploy elProp) {
|
||||
|
||||
/**
|
||||
* Return the set of property names for the many where joins.
|
||||
*/
|
||||
public TreeSet<String> getPropertyNames() {
|
||||
|
||||
TreeSet<String> propertyNames = new TreeSet<String>();
|
||||
for (PropertyJoin join : joins.values()) {
|
||||
propertyNames.add(join.getProperty());
|
||||
String join = elProp.getElPrefix();
|
||||
BeanProperty p = elProp.getBeanProperty();
|
||||
if (p instanceof BeanPropertyAssocMany<?>) {
|
||||
join = addManyToJoin(join, p.getName());
|
||||
}
|
||||
if (join != null) {
|
||||
addJoin(join);
|
||||
if (p != null) {
|
||||
String secondaryTableJoinPrefix = p.getSecondaryTableJoinPrefix();
|
||||
if (secondaryTableJoinPrefix != null) {
|
||||
addJoin(join + "." + secondaryTableJoinPrefix);
|
||||
}
|
||||
}
|
||||
return propertyNames;
|
||||
addParentJoins(join);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In findRowCount query found a formula property with a join clause so building a select clause
|
||||
* specifically for the findRowCount query.
|
||||
*/
|
||||
public void addFormulaWithJoin(String propertyName) {
|
||||
if (formulaWithJoin) {
|
||||
formulaProperties.append(",");
|
||||
} else {
|
||||
formulaProperties = new StringBuilder();
|
||||
formulaWithJoin = true;
|
||||
}
|
||||
formulaProperties.append(propertyName);
|
||||
/**
|
||||
* For 'many' properties we also need to add the name of the
|
||||
* many property to get the full logical name of the join.
|
||||
*/
|
||||
private String addManyToJoin(String join, String manyPropName) {
|
||||
if (join == null) {
|
||||
return manyPropName;
|
||||
} else {
|
||||
return join + "." + manyPropName;
|
||||
}
|
||||
|
||||
public boolean isHasMany() {
|
||||
return formulaWithJoin || !joins.isEmpty();
|
||||
}
|
||||
|
||||
private void addParentJoins(String join) {
|
||||
String[] split = SplitName.split(join);
|
||||
if (split[0] != null) {
|
||||
addJoin(split[0]);
|
||||
addParentJoins(split[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the findRowCount query just needs the id property in the select clause.
|
||||
*/
|
||||
public boolean isSelectId() {
|
||||
return !formulaWithJoin;
|
||||
}
|
||||
|
||||
private void addJoin(String property) {
|
||||
SqlJoinType joinType = (requireOuterJoins) ? SqlJoinType.OUTER : SqlJoinType.INNER;
|
||||
joins.put(property, new PropertyJoin(property, joinType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there are no extra many where joins.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return joins.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the set of many where joins.
|
||||
*/
|
||||
public Collection<PropertyJoin> getPropertyJoins() {
|
||||
return joins.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the set of property names for the many where joins.
|
||||
*/
|
||||
public TreeSet<String> getPropertyNames() {
|
||||
|
||||
TreeSet<String> propertyNames = new TreeSet<String>();
|
||||
for (PropertyJoin join : joins.values()) {
|
||||
propertyNames.add(join.getProperty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the formula properties to build the select clause for a findRowCount query.
|
||||
*/
|
||||
public String getFormulaProperties() {
|
||||
return formulaProperties.toString();
|
||||
return propertyNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* In findRowCount query found a formula property with a join clause so building a select clause
|
||||
* specifically for the findRowCount query.
|
||||
*/
|
||||
public void addFormulaWithJoin(String propertyName) {
|
||||
if (formulaWithJoin) {
|
||||
formulaProperties.append(",");
|
||||
} else {
|
||||
formulaProperties = new StringBuilder();
|
||||
formulaWithJoin = true;
|
||||
}
|
||||
formulaProperties.append(propertyName);
|
||||
}
|
||||
|
||||
public boolean isHasMany() {
|
||||
return formulaWithJoin || !joins.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the findRowCount query just needs the id property in the select clause.
|
||||
*/
|
||||
public boolean isSelectId() {
|
||||
return !formulaWithJoin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the formula properties to build the select clause for a findRowCount query.
|
||||
*/
|
||||
public String getFormulaProperties() {
|
||||
return formulaProperties.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package com.avaje.ebeaninternal.server.autotune.service;
|
||||
|
||||
import com.avaje.ebeaninternal.server.autotune.model.Autotune;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Event where all tuned query information is collected.
|
||||
* <p>
|
||||
* This is for writing the "all" file on shutdown when using runtime tuning.
|
||||
* </p>
|
||||
*/
|
||||
public class AutoTuneAllCollection {
|
||||
|
||||
final Autotune document = new Autotune();
|
||||
|
||||
final BaseQueryTuner queryTuner;
|
||||
|
||||
/**
|
||||
* Construct to collect/report all tuned queries.
|
||||
*/
|
||||
public AutoTuneAllCollection(BaseQueryTuner queryTuner) {
|
||||
this.queryTuner = queryTuner;
|
||||
loadAllTuned();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of origin elements in the document.
|
||||
*/
|
||||
public int size() {
|
||||
return document.getOrigin().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Autotune document object.
|
||||
*/
|
||||
public Autotune getDocument() {
|
||||
return document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the document as an xml file.
|
||||
*/
|
||||
public void writeFile(String filePrefix) {
|
||||
|
||||
AutoTuneXmlWriter writer = new AutoTuneXmlWriter();
|
||||
writer.write(document, filePrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all the existing query tuning into the document.
|
||||
*/
|
||||
private void loadAllTuned() {
|
||||
|
||||
Collection<TunedQueryInfo> all = queryTuner.getAll();
|
||||
for (TunedQueryInfo tuned: all) {
|
||||
document.getOrigin().add(tuned.getOrigin());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
package com.avaje.ebeaninternal.server.autotune.service;
|
||||
|
||||
import com.avaje.ebean.bean.ObjectGraphOrigin;
|
||||
import com.avaje.ebeaninternal.server.autotune.AutoTuneCollection;
|
||||
import com.avaje.ebeaninternal.server.autotune.model.Autotune;
|
||||
import com.avaje.ebeaninternal.server.autotune.model.Origin;
|
||||
import com.avaje.ebeaninternal.server.autotune.model.ProfileDiff;
|
||||
import com.avaje.ebeaninternal.server.autotune.model.ProfileNew;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
|
||||
/**
|
||||
* Event where profiling information is collected and processed for differences
|
||||
* relative to the current query tuning.
|
||||
*/
|
||||
public class AutoTuneDiffCollection {
|
||||
|
||||
final Autotune document = new Autotune();
|
||||
|
||||
final AutoTuneCollection profiling;
|
||||
|
||||
final BaseQueryTuner queryTuner;
|
||||
|
||||
final boolean updateTuning;
|
||||
|
||||
int newCount;
|
||||
|
||||
int diffCount;
|
||||
|
||||
/**
|
||||
* Construct to collect/report the new/diff query tuning entries.
|
||||
*/
|
||||
public AutoTuneDiffCollection(AutoTuneCollection profiling, BaseQueryTuner queryTuner, boolean updateTuning) {
|
||||
this.profiling = profiling;
|
||||
this.queryTuner = queryTuner;
|
||||
this.updateTuning = updateTuning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there are no new or diff entries.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return newCount == 0 && diffCount == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying Autotune document object.
|
||||
*/
|
||||
public Autotune getDocument() {
|
||||
return document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of diff entries.
|
||||
*/
|
||||
public int getDiffCount() {
|
||||
return diffCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of new entries.
|
||||
*/
|
||||
public int getNewCount() {
|
||||
return newCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total new and diff entries.
|
||||
*/
|
||||
public int getChangeCount() {
|
||||
return newCount + diffCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the underlying document as an xml file.
|
||||
*/
|
||||
public void writeFile(String filePrefix) {
|
||||
|
||||
AutoTuneXmlWriter writer = new AutoTuneXmlWriter();
|
||||
writer.write(document, filePrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process checking profiling entries against existing query tuning.
|
||||
*/
|
||||
public void process() {
|
||||
|
||||
for (AutoTuneCollection.Entry entry : profiling.getEntries()) {
|
||||
addToDocument(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the entry is new or diff and add as necessary.
|
||||
*/
|
||||
private void addToDocument(AutoTuneCollection.Entry entry) {
|
||||
|
||||
ObjectGraphOrigin point = entry.getOrigin();
|
||||
OrmQueryDetail profileDetail = entry.getDetail();
|
||||
|
||||
// compare with the existing query tuning entry
|
||||
OrmQueryDetail tuneDetail = queryTuner.get(point.getKey());
|
||||
if (tuneDetail == null) {
|
||||
addToDocumentNewEntry(entry, point);
|
||||
|
||||
} else if (!tuneDetail.isAutoTuneEqual(profileDetail)) {
|
||||
addToDocumentDiffEntry(entry, point, tuneDetail);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add as a diff entry.
|
||||
*/
|
||||
private void addToDocumentDiffEntry(AutoTuneCollection.Entry entry, ObjectGraphOrigin point, OrmQueryDetail tuneDetail) {
|
||||
|
||||
diffCount++;
|
||||
|
||||
Origin origin = createOrigin(entry, point, tuneDetail.toString());
|
||||
ProfileDiff diff = document.getProfileDiff();
|
||||
if (diff == null) {
|
||||
diff = new ProfileDiff();
|
||||
document.setProfileDiff(diff);
|
||||
}
|
||||
diff.getOrigin().add(origin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add as a "new" entry.
|
||||
*/
|
||||
private void addToDocumentNewEntry(AutoTuneCollection.Entry entry, ObjectGraphOrigin point) {
|
||||
|
||||
newCount++;
|
||||
|
||||
ProfileNew profileNew = document.getProfileNew();
|
||||
if (profileNew == null) {
|
||||
profileNew = new ProfileNew();
|
||||
document.setProfileNew(profileNew);
|
||||
}
|
||||
Origin origin = createOrigin(entry, point, entry.getOriginalQuery());
|
||||
profileNew.getOrigin().add(origin);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create the XML Origin bean for the given entry and ObjectGraphOrigin.
|
||||
*/
|
||||
private Origin createOrigin(AutoTuneCollection.Entry entry, ObjectGraphOrigin point, String query) {
|
||||
|
||||
Origin origin = new Origin();
|
||||
origin.setKey(point.getKey());
|
||||
origin.setBeanType(point.getBeanType());
|
||||
origin.setDetail(entry.getDetail().toString());
|
||||
origin.setCallStack(point.getCallStack().description("\n"));
|
||||
origin.setOriginal(query);
|
||||
|
||||
if (updateTuning) {
|
||||
queryTuner.put(origin);
|
||||
}
|
||||
|
||||
return origin;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,12 +7,29 @@ import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Marshaller;
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Simple writer for output of the AutoTune Profiling as an XML document.
|
||||
*/
|
||||
public class AutoTuneXmlWriter {
|
||||
|
||||
/**
|
||||
* Write the document as xml file with the given prefix.
|
||||
*/
|
||||
public void write(Autotune document, String filePrefix) {
|
||||
|
||||
SortAutoTuneDocument.sort(document);
|
||||
|
||||
SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd-HHmmss");
|
||||
String now = df.format(new Date());
|
||||
|
||||
// write the file with serverName and now suffix as we can output the profiling many times
|
||||
File file = new File(filePrefix + "-" + now + ".xml");
|
||||
write(document, file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write Profiling to a file as xml.
|
||||
*/
|
||||
|
||||
@@ -7,9 +7,11 @@ import com.avaje.ebean.config.AutoTuneMode;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.autotune.ProfilingListener;
|
||||
import com.avaje.ebeaninternal.server.autotune.model.Origin;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -48,6 +50,21 @@ public class BaseQueryTuner {
|
||||
this.skipAll = !queryTuning && !profiling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all the current tuned query entries.
|
||||
*/
|
||||
public Collection<TunedQueryInfo> getAll() {
|
||||
return tunedQueryInfoMap.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a query tuning entry.
|
||||
*/
|
||||
public void put(Origin origin) {
|
||||
|
||||
tunedQueryInfoMap.put(origin.getKey(), new TunedQueryInfo(origin));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the tuned query information.
|
||||
*/
|
||||
|
||||
+121
-154
@@ -1,6 +1,5 @@
|
||||
package com.avaje.ebeaninternal.server.autotune.service;
|
||||
|
||||
import com.avaje.ebean.bean.ObjectGraphOrigin;
|
||||
import com.avaje.ebean.config.AutoTuneConfig;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
@@ -9,24 +8,11 @@ import com.avaje.ebeaninternal.server.autotune.AutoTuneCollection;
|
||||
import com.avaje.ebeaninternal.server.autotune.AutoTuneService;
|
||||
import com.avaje.ebeaninternal.server.autotune.model.Autotune;
|
||||
import com.avaje.ebeaninternal.server.autotune.model.Origin;
|
||||
import com.avaje.ebeaninternal.server.autotune.model.ProfileDiff;
|
||||
import com.avaje.ebeaninternal.server.autotune.model.ProfileEmpty;
|
||||
import com.avaje.ebeaninternal.server.autotune.model.ProfileNew;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetailParser;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Implementation of the AutoTuneService which is comprised of profiling and query tuning.
|
||||
@@ -35,9 +21,13 @@ public class DefaultAutoTuneService implements AutoTuneService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DefaultAutoTuneService.class);
|
||||
|
||||
private final SpiEbeanServer server;
|
||||
|
||||
private final long defaultGarbageCollectionWait;
|
||||
|
||||
private final boolean skipCollectionOnShutdown;
|
||||
private final boolean skipGarbageCollectionOnShutdown;
|
||||
|
||||
private final boolean skipProfileReportingOnShutdown;
|
||||
|
||||
private final BaseQueryTuner queryTuner;
|
||||
|
||||
@@ -53,18 +43,25 @@ public class DefaultAutoTuneService implements AutoTuneService {
|
||||
|
||||
private final String serverName;
|
||||
|
||||
private final int profilingUpdateFrequency;
|
||||
|
||||
private long runtimeChangeCount;
|
||||
|
||||
public DefaultAutoTuneService(SpiEbeanServer server, ServerConfig serverConfig) {
|
||||
|
||||
AutoTuneConfig config = serverConfig.getAutoTuneConfig();
|
||||
|
||||
this.server = server;
|
||||
this.queryTuning = config.isQueryTuning();
|
||||
this.profiling = config.isProfiling();
|
||||
this.tuningFile = config.getQueryTuningFile();
|
||||
this.profilingFile = config.getProfilingFile();
|
||||
this.profilingUpdateFrequency = config.getProfilingUpdateFrequency();
|
||||
this.serverName = server.getName();
|
||||
this.profileManager = new ProfileManager(config, server);
|
||||
this.queryTuner = new BaseQueryTuner(config, server, profileManager);
|
||||
this.skipCollectionOnShutdown = config.isSkipCollectionOnShutdown();
|
||||
this.skipGarbageCollectionOnShutdown = config.isSkipGarbageCollectionOnShutdown();
|
||||
this.skipProfileReportingOnShutdown = config.isSkipProfileReportingOnShutdown();
|
||||
this.defaultGarbageCollectionWait = (long) config.getGarbageCollectionWait();
|
||||
}
|
||||
|
||||
@@ -75,169 +72,122 @@ public class DefaultAutoTuneService implements AutoTuneService {
|
||||
public void startup() {
|
||||
|
||||
if (queryTuning) {
|
||||
File file = new File(tuningFile);
|
||||
if (!file.exists()) {
|
||||
logger.warn("AutoTune file {} not found - no automatic tuning will be applied", file.getAbsolutePath());
|
||||
|
||||
} else {
|
||||
AutoTuneXmlReader reader = new AutoTuneXmlReader();
|
||||
Autotune profiling = reader.read(file);
|
||||
logger.info("AutoTune loading {} tuning entries", profiling.getOrigin().size());
|
||||
for (Origin origin : profiling.getOrigin()) {
|
||||
queryTuner.load(origin.getKey(), createTunedQueryInfo(origin));
|
||||
}
|
||||
loadTuningFile();
|
||||
if (isRuntimeTuningUpdates()) {
|
||||
// periodically gather and update query tuning
|
||||
server.getBackgroundExecutor().executePeriodically(new ProfilingUpdate(), profilingUpdateFrequency, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private TunedQueryInfo createTunedQueryInfo(Origin origin) {
|
||||
OrmQueryDetail detail = new OrmQueryDetailParser(origin.getDetail()).parse();
|
||||
return new TunedQueryInfo(detail);
|
||||
|
||||
/**
|
||||
* Return true if the tuning should update periodically at runtime.
|
||||
*/
|
||||
private boolean isRuntimeTuningUpdates() {
|
||||
return profilingUpdateFrequency > 0;
|
||||
}
|
||||
|
||||
private void saveProfiling(boolean reset) {
|
||||
private class ProfilingUpdate implements Runnable {
|
||||
|
||||
Autotune document = new Autotune();
|
||||
|
||||
AutoTuneCollection autoTuneCollection = profileManager.profilingCollection(reset);
|
||||
|
||||
List<AutoTuneCollection.Entry> entries = autoTuneCollection.getEntries();
|
||||
|
||||
// count "new" and "diff" profiling entries
|
||||
AtomicInteger newCounter = new AtomicInteger();
|
||||
AtomicInteger diffCounter = new AtomicInteger();
|
||||
|
||||
Set<String> profileKeys = new HashSet<String>();
|
||||
for (AutoTuneCollection.Entry entry : entries) {
|
||||
saveProfilingEntry(document, entry, newCounter, diffCounter);
|
||||
profileKeys.add(entry.getOrigin().getKey());
|
||||
@Override
|
||||
public void run() {
|
||||
runtimeTuningUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
// report the origin keys that we didn't collect any profiling on
|
||||
Set<String> tunerKeys = queryTuner.keySet();
|
||||
for (String tuneKey : tunerKeys) {
|
||||
if (!profileKeys.contains(tuneKey)) {
|
||||
ProfileEmpty profileEmpty = document.getProfileEmpty();
|
||||
if (profileEmpty == null) {
|
||||
profileEmpty = new ProfileEmpty();
|
||||
document.setProfileEmpty(profileEmpty);
|
||||
}
|
||||
Origin emptyOrigin = new Origin();
|
||||
emptyOrigin.setKey(tuneKey);
|
||||
profileEmpty.getOrigin().add(emptyOrigin);
|
||||
}
|
||||
}
|
||||
|
||||
int totalNew = newCounter.get();
|
||||
int totalDiff = diffCounter.get();
|
||||
if (totalNew == 0 && totalDiff == 0) {
|
||||
logger.info("No new or diff entries for profiling server:{}", serverName);
|
||||
/**
|
||||
* Load tuning information from an existing tuning file.
|
||||
*/
|
||||
private void loadTuningFile() {
|
||||
File file = new File(tuningFile);
|
||||
if (!file.exists()) {
|
||||
logger.warn("AutoTune file {} not found - no initial automatic query tuning", file.getAbsolutePath());
|
||||
|
||||
} else {
|
||||
sortDocument(document);
|
||||
|
||||
SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd-HHmmss");
|
||||
String now = df.format(new Date());
|
||||
|
||||
// write the file with serverName and now suffix as we can output the profiling many times
|
||||
File file = new File(profilingFile + "-" + serverName + "-" + now + ".xml");
|
||||
AutoTuneXmlWriter writer = new AutoTuneXmlWriter();
|
||||
writer.write(document, file);
|
||||
|
||||
logger.info("writing new:{} diff:{} profiling entries for server:{}", totalNew, totalDiff, serverName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the diff and new entries by bean type followed by key.
|
||||
*/
|
||||
private void sortDocument(Autotune document) {
|
||||
|
||||
ProfileDiff profileDiff = document.getProfileDiff();
|
||||
if (profileDiff != null) {
|
||||
Collections.sort(profileDiff.getOrigin(), new OriginNameKeySort());
|
||||
}
|
||||
ProfileNew profileNew = document.getProfileNew();
|
||||
if (profileNew != null) {
|
||||
Collections.sort(profileNew.getOrigin(), new OriginNameKeySort());
|
||||
}
|
||||
ProfileEmpty profileEmpty = document.getProfileEmpty();
|
||||
if (profileEmpty != null) {
|
||||
Collections.sort(profileEmpty.getOrigin(), new OriginKeySort());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparator sort by bean type then key.
|
||||
*/
|
||||
class OriginNameKeySort implements Comparator<Origin> {
|
||||
|
||||
@Override
|
||||
public int compare(Origin o1, Origin o2) {
|
||||
int comp = o1.getBeanType().compareTo(o2.getBeanType());
|
||||
if (comp == 0) {
|
||||
comp = o1.getKey().compareTo(o2.getKey());
|
||||
AutoTuneXmlReader reader = new AutoTuneXmlReader();
|
||||
Autotune profiling = reader.read(file);
|
||||
logger.info("AutoTune loading {} tuning entries", profiling.getOrigin().size());
|
||||
for (Origin origin : profiling.getOrigin()) {
|
||||
queryTuner.put(origin);
|
||||
}
|
||||
return comp;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparator sort by bean type then key.
|
||||
* Collect profiling, check for new/diff to existing tuning and apply changes.
|
||||
*/
|
||||
class OriginKeySort implements Comparator<Origin> {
|
||||
private void runtimeTuningUpdate() {
|
||||
|
||||
@Override
|
||||
public int compare(Origin o1, Origin o2) {
|
||||
return o1.getKey().compareTo(o2.getKey());
|
||||
synchronized (this) {
|
||||
try {
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
AutoTuneCollection profiling = profileManager.profilingCollection(false);
|
||||
|
||||
AutoTuneDiffCollection event = new AutoTuneDiffCollection(profiling, queryTuner, true);
|
||||
event.process();
|
||||
if (event.isEmpty()) {
|
||||
long exeMillis = System.currentTimeMillis() - start;
|
||||
logger.debug("No query tuning updates for server:{} executionMillis:{}", serverName, exeMillis);
|
||||
|
||||
} else {
|
||||
// report the query tuning changes that have been made
|
||||
runtimeChangeCount += event.getChangeCount();
|
||||
event.writeFile(profilingFile + "-" + serverName + "-update");
|
||||
long exeMillis = System.currentTimeMillis() - start;
|
||||
logger.info("query tuning updates - new:{} diff:{} for server:{} executionMillis:{}", event.getNewCount(), event.getDiffCount(), serverName, exeMillis);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
logger.error("Error collecting or applying automatic query tuning", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveProfilingEntry(Autotune document, AutoTuneCollection.Entry entry, AtomicInteger newCount, AtomicInteger diffCount) {
|
||||
private void saveProfilingOnShutdown(boolean reset) {
|
||||
|
||||
ObjectGraphOrigin point = entry.getOrigin();
|
||||
OrmQueryDetail profileDetail = entry.getDetail();
|
||||
synchronized (this) {
|
||||
if (isRuntimeTuningUpdates()) {
|
||||
runtimeTuningUpdate();
|
||||
outputAllTuning();
|
||||
|
||||
// compare with the existing query tuning entry
|
||||
OrmQueryDetail tuneDetail = queryTuner.get(point.getKey());
|
||||
if (tuneDetail == null) {
|
||||
// New entry
|
||||
newCount.incrementAndGet();
|
||||
ProfileNew profileNew = document.getProfileNew();
|
||||
if (profileNew == null) {
|
||||
profileNew = new ProfileNew();
|
||||
document.setProfileNew(profileNew);
|
||||
} else {
|
||||
|
||||
AutoTuneCollection profiling = profileManager.profilingCollection(reset);
|
||||
|
||||
AutoTuneDiffCollection event = new AutoTuneDiffCollection(profiling, queryTuner, false);
|
||||
event.process();
|
||||
if (event.isEmpty()) {
|
||||
logger.info("No new or diff entries for profiling server:{}", serverName);
|
||||
|
||||
} else {
|
||||
event.writeFile(profilingFile + "-" + serverName);
|
||||
logger.info("writing new:{} diff:{} profiling entries for server:{}", event.getNewCount(), event.getDiffCount(), serverName);
|
||||
}
|
||||
}
|
||||
Origin origin = createOrigin(entry, point);
|
||||
origin.setOriginal(entry.getOriginalQuery());
|
||||
profileNew.getOrigin().add(origin);
|
||||
|
||||
} else if (!tuneDetail.isAutoTuneEqual(profileDetail)) {
|
||||
// Diff entry
|
||||
diffCount.incrementAndGet();
|
||||
Origin origin = createOrigin(entry, point);
|
||||
origin.setOriginal(tuneDetail.toString());
|
||||
ProfileDiff diff = document.getProfileDiff();
|
||||
if (diff == null) {
|
||||
diff = new ProfileDiff();
|
||||
document.setProfileDiff(diff);
|
||||
}
|
||||
diff.getOrigin().add(origin);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the XML Origin bean for the given entry and ObjectGraphOrigin.
|
||||
* Output all the query tuning (the "all" file).
|
||||
* <p>
|
||||
* This is the originally loaded tuning plus any tuning changes picked up and applied at runtime.
|
||||
* </p>
|
||||
* <p>
|
||||
* This "all" file can be used as the next "ebean-autotune.xml" file.
|
||||
* </p>
|
||||
*/
|
||||
@NotNull
|
||||
private Origin createOrigin(AutoTuneCollection.Entry entry, ObjectGraphOrigin point) {
|
||||
Origin origin = new Origin();
|
||||
origin.setKey(point.getKey());
|
||||
origin.setBeanType(point.getBeanType());
|
||||
origin.setDetail(entry.getDetail().toString());
|
||||
origin.setCallStack(point.getCallStack().description("\n"));
|
||||
return origin;
|
||||
private void outputAllTuning() {
|
||||
|
||||
if (runtimeChangeCount == 0) {
|
||||
logger.info("no runtime query tuning changes for server:{}", serverName);
|
||||
|
||||
} else {
|
||||
AutoTuneAllCollection event = new AutoTuneAllCollection(queryTuner);
|
||||
int size = event.size();
|
||||
event.writeFile(profilingFile + "-" + serverName + "-all");
|
||||
logger.info("query tuning detected [{}] changes, writing all [{}] tuning entries for server:{}", runtimeChangeCount, size, serverName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -250,12 +200,29 @@ public class DefaultAutoTuneService implements AutoTuneService {
|
||||
*/
|
||||
@Override
|
||||
public void shutdown() {
|
||||
if (profiling && !skipCollectionOnShutdown) {
|
||||
collectProfiling(-1);
|
||||
saveProfiling(false);
|
||||
if (profiling) {
|
||||
if (!skipGarbageCollectionOnShutdown && !skipProfileReportingOnShutdown) {
|
||||
// trigger GC to update profiling information on recently executed queries
|
||||
collectProfiling(-1);
|
||||
}
|
||||
if (!skipProfileReportingOnShutdown) {
|
||||
saveProfilingOnShutdown(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the profiling.
|
||||
* <p>
|
||||
* When profiling updates are applied to tuning at runtime this reports all tuning and profiling combined.
|
||||
* When profiling is not applied at runtime then this reports the diff report with new and diff entries relative
|
||||
* to the existing tuning.
|
||||
* </p>
|
||||
*/
|
||||
public void reportProfiling() {
|
||||
saveProfilingOnShutdown(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask for a System.gc() so that we gather node usage information.
|
||||
* <p>
|
||||
|
||||
@@ -118,7 +118,6 @@ public class ProfileManager implements ProfilingListener {
|
||||
AutoTuneCollection req = new AutoTuneCollection();
|
||||
|
||||
for (ProfileOrigin origin : profileMap.values()) {
|
||||
|
||||
BeanDescriptor<?> desc = server.getBeanDescriptorById(origin.getOrigin().getBeanType());
|
||||
if (desc != null) {
|
||||
origin.profilingCollection(desc, req, reset);
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package com.avaje.ebeaninternal.server.autotune.service;
|
||||
|
||||
import com.avaje.ebeaninternal.server.autotune.model.Autotune;
|
||||
import com.avaje.ebeaninternal.server.autotune.model.Origin;
|
||||
import com.avaje.ebeaninternal.server.autotune.model.ProfileDiff;
|
||||
import com.avaje.ebeaninternal.server.autotune.model.ProfileEmpty;
|
||||
import com.avaje.ebeaninternal.server.autotune.model.ProfileNew;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Sorts Autotune document by
|
||||
*/
|
||||
public class SortAutoTuneDocument {
|
||||
|
||||
|
||||
/**
|
||||
* Set the diff and new entries by bean type followed by key.
|
||||
*/
|
||||
public static void sort(Autotune document) {
|
||||
|
||||
ProfileDiff profileDiff = document.getProfileDiff();
|
||||
if (profileDiff != null) {
|
||||
Collections.sort(profileDiff.getOrigin(), NAME_KEY_SORT);
|
||||
}
|
||||
ProfileNew profileNew = document.getProfileNew();
|
||||
if (profileNew != null) {
|
||||
Collections.sort(profileNew.getOrigin(), NAME_KEY_SORT);
|
||||
}
|
||||
ProfileEmpty profileEmpty = document.getProfileEmpty();
|
||||
if (profileEmpty != null) {
|
||||
Collections.sort(profileEmpty.getOrigin(), KEY_SORT);
|
||||
}
|
||||
List<Origin> origins = document.getOrigin();
|
||||
if (!origins.isEmpty()) {
|
||||
Collections.sort(origins, NAME_KEY_SORT);
|
||||
}
|
||||
}
|
||||
|
||||
private static final OriginNameKeySort NAME_KEY_SORT = new OriginNameKeySort();
|
||||
|
||||
private static final OriginKeySort KEY_SORT = new OriginKeySort();
|
||||
|
||||
/**
|
||||
* Comparator sort by bean type then key.
|
||||
*/
|
||||
private static class OriginNameKeySort implements Comparator<Origin> {
|
||||
|
||||
@Override
|
||||
public int compare(Origin o1, Origin o2) {
|
||||
int comp = o1.getBeanType().compareTo(o2.getBeanType());
|
||||
if (comp == 0) {
|
||||
comp = o1.getKey().compareTo(o2.getKey());
|
||||
}
|
||||
return comp;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparator sort by bean type then key.
|
||||
*/
|
||||
private static class OriginKeySort implements Comparator<Origin> {
|
||||
|
||||
@Override
|
||||
public int compare(Origin o1, Origin o2) {
|
||||
return o1.getKey().compareTo(o2.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.avaje.ebeaninternal.server.autotune.service;
|
||||
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.autotune.model.Origin;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetailParser;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@@ -11,10 +13,20 @@ import java.io.Serializable;
|
||||
*/
|
||||
public class TunedQueryInfo implements Serializable {
|
||||
|
||||
private final Origin origin;
|
||||
|
||||
private final OrmQueryDetail tunedDetail;
|
||||
|
||||
public TunedQueryInfo(OrmQueryDetail tunedDetail) {
|
||||
this.tunedDetail = tunedDetail;
|
||||
public TunedQueryInfo(Origin origin) {
|
||||
this.origin = origin;
|
||||
this.tunedDetail = new OrmQueryDetailParser(origin.getDetail()).parse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the origin entry (includes call stack and bean type).
|
||||
*/
|
||||
public Origin getOrigin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,23 +6,20 @@ import com.avaje.ebean.bean.BeanCollection;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.bean.EntityBeanIntercept;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebeaninternal.api.LoadBeanBuffer;
|
||||
import com.avaje.ebeaninternal.api.LoadBeanRequest;
|
||||
import com.avaje.ebeaninternal.api.LoadManyBuffer;
|
||||
import com.avaje.ebeaninternal.api.LoadManyRequest;
|
||||
import com.avaje.ebeaninternal.api.LoadRequest;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery.Mode;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
|
||||
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.EntityNotFoundException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -86,89 +83,11 @@ public class DefaultBeanLoader {
|
||||
|
||||
int batchSize = getBatchSize(batch.size());
|
||||
|
||||
LoadManyBuffer ctx = loadRequest.getLoadContext();
|
||||
BeanPropertyAssocMany<?> many = ctx.getBeanProperty();
|
||||
SpiQuery<?> query = loadRequest.createQuery(server, batchSize);
|
||||
|
||||
PersistenceContext pc = ctx.getPersistenceContext();
|
||||
executeQuery(loadRequest, query);
|
||||
|
||||
ArrayList<Object> idList = new ArrayList<Object>(batchSize);
|
||||
|
||||
for (int i = 0; i < batch.size(); i++) {
|
||||
BeanCollection<?> bc = batch.get(i);
|
||||
EntityBean ownerBean = bc.getOwnerBean();
|
||||
Object id = many.getParentId(ownerBean);
|
||||
idList.add(id);
|
||||
}
|
||||
int extraIds = batchSize - batch.size();
|
||||
if (extraIds > 0) {
|
||||
Object firstId = idList.get(0);
|
||||
for (int i = 0; i < extraIds; i++) {
|
||||
idList.add(firstId);
|
||||
}
|
||||
}
|
||||
|
||||
BeanDescriptor<?> desc = ctx.getBeanDescriptor();
|
||||
|
||||
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(many.getTargetType());
|
||||
String orderBy = many.getLazyFetchOrderBy();
|
||||
if (orderBy != null) {
|
||||
query.orderBy(orderBy);
|
||||
}
|
||||
|
||||
String extraWhere = many.getExtraWhere();
|
||||
if (extraWhere != null) {
|
||||
// replace special ${ta} placeholder with the base table alias
|
||||
// which is always t0 and add the extra where clause
|
||||
String ew = StringHelper.replaceString(extraWhere, "${ta}", "t0");
|
||||
query.where().raw(ew);
|
||||
}
|
||||
|
||||
query.setLazyLoadForParents(many);
|
||||
many.addWhereParentIdIn(query, idList);
|
||||
|
||||
query.setPersistenceContext(pc);
|
||||
|
||||
String mode = loadRequest.isLazy() ? "+lazy" : "+query";
|
||||
query.setLoadDescription(mode, loadRequest.getDescription());
|
||||
|
||||
if (loadRequest.isLazy()) {
|
||||
// cascade the batch size (if set) for further lazy loading
|
||||
query.setLazyLoadBatchSize(loadRequest.getBatchSize());
|
||||
}
|
||||
|
||||
// potentially changes the joins and selected properties
|
||||
ctx.configureQuery(query);
|
||||
|
||||
if (loadRequest.isOnlyIds()) {
|
||||
// override to just select the Id values
|
||||
query.select(many.getTargetIdProperty());
|
||||
}
|
||||
|
||||
if (onIterateUseExtraTxn && loadRequest.isParentFindIterate()) {
|
||||
// MySql - we need a different transaction to execute the secondary query
|
||||
SpiTransaction extraTxn = server.createQueryTransaction();
|
||||
try {
|
||||
server.findList(query, extraTxn);
|
||||
} finally {
|
||||
extraTxn.end();
|
||||
}
|
||||
} else {
|
||||
server.findList(query, loadRequest.getTransaction());
|
||||
}
|
||||
|
||||
// check for BeanCollection's that where never processed
|
||||
// in the +query or +lazy load due to no rows (predicates)
|
||||
for (int i = 0; i < batch.size(); i++) {
|
||||
BeanCollection<?> bc = batch.get(i);
|
||||
if (bc.checkEmptyLazyLoad()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("BeanCollection after load was empty. Owner:" + batch.get(i).getOwnerBean());
|
||||
}
|
||||
} else if (loadRequest.isLoadCache()) {
|
||||
Object parentId = desc.getId(bc.getOwnerBean());
|
||||
desc.cacheManyPropPut(many, bc, parentId);
|
||||
}
|
||||
}
|
||||
loadRequest.postLoad();
|
||||
|
||||
// log the query (for testing secondary queries)
|
||||
loadRequest.logSecondaryQuery(query);
|
||||
@@ -274,96 +193,43 @@ public class DefaultBeanLoader {
|
||||
public void loadBean(LoadBeanRequest loadRequest) {
|
||||
|
||||
List<EntityBeanIntercept> batch = loadRequest.getBatch();
|
||||
|
||||
if (batch.isEmpty()) {
|
||||
throw new RuntimeException("Nothing in batch?");
|
||||
}
|
||||
|
||||
int batchSize = getBatchSize(batch.size());
|
||||
|
||||
LoadBeanBuffer ctx = loadRequest.getLoadContext();
|
||||
BeanDescriptor<?> desc = ctx.getBeanDescriptor();
|
||||
|
||||
Class<?> beanType = desc.getBeanType();
|
||||
|
||||
EntityBeanIntercept[] ebis = batch.toArray(new EntityBeanIntercept[batch.size()]);
|
||||
ArrayList<Object> idList = new ArrayList<Object>(batchSize);
|
||||
|
||||
for (int i = 0; i < batch.size(); i++) {
|
||||
EntityBeanIntercept ebi = batch.get(i);
|
||||
EntityBean bean = ebi.getOwner();
|
||||
Object id = desc.getId(bean);
|
||||
idList.add(id);
|
||||
}
|
||||
|
||||
List<Object> idList = loadRequest.getIdList(batchSize);
|
||||
if (idList.isEmpty()) {
|
||||
// everything was loaded from cache
|
||||
return;
|
||||
}
|
||||
|
||||
int extraIds = batchSize - batch.size();
|
||||
if (extraIds > 0) {
|
||||
// for performance make up the Id's to the batch size
|
||||
// so we get the same query (for Ebean and the db)
|
||||
Object firstId = idList.get(0);
|
||||
for (int i = 0; i < extraIds; i++) {
|
||||
// just add the first Id again
|
||||
idList.add(firstId);
|
||||
}
|
||||
}
|
||||
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(loadRequest.getBeanType());
|
||||
loadRequest.configureQuery(query, idList);
|
||||
|
||||
PersistenceContext persistenceContext = ctx.getPersistenceContext();
|
||||
List<?> list = executeQuery(loadRequest, query);
|
||||
loadRequest.postLoad(list);
|
||||
|
||||
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(beanType);
|
||||
// log the query (for testing secondary queries)
|
||||
loadRequest.logSecondaryQuery(query);
|
||||
}
|
||||
|
||||
query.setMode(Mode.LAZYLOAD_BEAN);
|
||||
query.setPersistenceContext(persistenceContext);
|
||||
|
||||
String mode = loadRequest.isLazy() ? "+lazy" : "+query";
|
||||
query.setLoadDescription(mode, loadRequest.getDescription());
|
||||
|
||||
if (loadRequest.isLazy()) {
|
||||
// cascade the batch size (if set) for further lazy loading
|
||||
query.setLazyLoadBatchSize(loadRequest.getBatchSize());
|
||||
}
|
||||
|
||||
ctx.configureQuery(query, loadRequest.getLazyLoadProperty());
|
||||
|
||||
// make sure the query doesn't use the cache
|
||||
// query.setUseCache(false);
|
||||
if (idList.size() == 1) {
|
||||
query.where().idEq(idList.get(0));
|
||||
} else {
|
||||
query.where().idIn(idList);
|
||||
}
|
||||
|
||||
List<?> list;
|
||||
/**
|
||||
* Execute the lazy load query taking into account MySql transaction oddness.
|
||||
*/
|
||||
private List<?> executeQuery(LoadRequest loadRequest, SpiQuery<?> query) {
|
||||
if (onIterateUseExtraTxn && loadRequest.isParentFindIterate()) {
|
||||
// MySql - we need a different transaction to execute the secondary query
|
||||
SpiTransaction extraTxn = server.createQueryTransaction();
|
||||
try {
|
||||
list = server.findList(query, extraTxn);
|
||||
return server.findList(query, extraTxn);
|
||||
} finally {
|
||||
extraTxn.end();
|
||||
}
|
||||
} else {
|
||||
list = server.findList(query, loadRequest.getTransaction());
|
||||
return server.findList(query, loadRequest.getTransaction());
|
||||
}
|
||||
|
||||
if (loadRequest.isLoadCache()) {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
desc.cacheBeanPutData((EntityBean) list.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < ebis.length; i++) {
|
||||
// Check if the underlying row in DB was deleted. Mark this bean as 'failed' if
|
||||
// necessary but allow processing to continue until it is accessed by client code
|
||||
ebis[i].checkLazyLoadFailure();
|
||||
}
|
||||
|
||||
// log the query (for testing secondary queries)
|
||||
loadRequest.logSecondaryQuery(query);
|
||||
}
|
||||
|
||||
public void refresh(EntityBean bean) {
|
||||
|
||||
@@ -2316,7 +2316,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
*/
|
||||
public void appendOrderById(SpiQuery<T> query) {
|
||||
|
||||
if (idProperty != null) {
|
||||
if (idProperty != null && !idProperty.isEmbedded()) {
|
||||
OrderBy<T> orderBy = query.getOrderBy();
|
||||
if (orderBy == null || orderBy.isEmpty()) {
|
||||
query.order().asc(idProperty.getName());
|
||||
|
||||
@@ -85,8 +85,8 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
public void initialise() {
|
||||
// this *MUST* execute after the BeanDescriptor is
|
||||
// put into the map to stop infinite recursion
|
||||
if (!isTransient){
|
||||
targetDescriptor = descriptor.getBeanDescriptor(targetType);
|
||||
targetDescriptor = descriptor.getBeanDescriptor(targetType);
|
||||
if (!isTransient){
|
||||
targetIdBinder = targetDescriptor.getIdBinder();
|
||||
targetInheritInfo = targetDescriptor.getInheritInfo();
|
||||
saveRecurseSkippable = targetDescriptor.isSaveRecurseSkippable();
|
||||
|
||||
@@ -887,7 +887,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
if (help != null) {
|
||||
help.jsonWrite(ctx, name, value, include != null);
|
||||
} else {
|
||||
if (isTransient) {
|
||||
if (isTransient && targetDescriptor == null) {
|
||||
ctx.writeValueUsingObjectMapper(name, value);
|
||||
} else {
|
||||
Collection<?> collection = (Collection<?>)value;
|
||||
|
||||
@@ -101,14 +101,23 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
// no imported or exported information
|
||||
} else if (!oneToOneExported) {
|
||||
importedId = createImportedId(this, targetDescriptor, tableJoin);
|
||||
if (importedId.isScalar()) {
|
||||
// limit JoinColumn mapping to the @Id / primary key
|
||||
TableJoinColumn[] columns = tableJoin.columns();
|
||||
String foreignJoinColumn = columns[0].getForeignDbColumn();
|
||||
String foreignIdColumn = targetDescriptor.getIdProperty().getDbColumn();
|
||||
if (!foreignJoinColumn.equalsIgnoreCase(foreignIdColumn)) {
|
||||
throw new PersistenceException("Mapping limitation - @JoinColumn on " + getFullBeanName() + " needs to map to a primary key as per Issue #529 "
|
||||
+ " - joining to " + foreignJoinColumn + " and not " + foreignIdColumn);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
exportedProperties = createExported();
|
||||
|
||||
String delStmt = "delete from " + targetDescriptor.getBaseTable() + " where ";
|
||||
|
||||
deleteByParentIdSql = delStmt + deriveWhereParentIdSql(false);
|
||||
deleteByParentIdInSql = delStmt + deriveWhereParentIdSql(true);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+45
-46
@@ -1,13 +1,12 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.generatedproperty;
|
||||
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.sql.Types;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
|
||||
/**
|
||||
* Creates "Counter" GeneratedProperty for various types of number.
|
||||
* <p>
|
||||
@@ -16,49 +15,49 @@ import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
*/
|
||||
public class CounterFactory {
|
||||
|
||||
final GeneratedCounterInteger integerCounter = new GeneratedCounterInteger();
|
||||
final GeneratedCounterInteger integerCounter = new GeneratedCounterInteger();
|
||||
|
||||
final GeneratedCounterLong longCounter = new GeneratedCounterLong();
|
||||
final GeneratedCounterLong longCounter = new GeneratedCounterLong();
|
||||
|
||||
public void setCounter(DeployBeanProperty property) {
|
||||
public void setCounter(DeployBeanProperty property) {
|
||||
|
||||
property.setGeneratedProperty(createCounter(property));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the GeneratedProperty based on the property type.
|
||||
*/
|
||||
private GeneratedProperty createCounter(DeployBeanProperty property) {
|
||||
|
||||
Class<?> propType = property.getPropertyType();
|
||||
if (propType.equals(Integer.class) || propType.equals(int.class)) {
|
||||
return integerCounter;
|
||||
}
|
||||
if (propType.equals(Long.class) || propType.equals(long.class)) {
|
||||
return longCounter;
|
||||
}
|
||||
|
||||
int type = getType(propType);
|
||||
return new GeneratedCounter(type);
|
||||
}
|
||||
|
||||
private int getType(Class<?> propType){
|
||||
if (propType.equals(Short.class) || propType.equals(short.class)){
|
||||
return Types.TINYINT;
|
||||
}
|
||||
if (propType.equals(BigDecimal.class)){
|
||||
return Types.DECIMAL;
|
||||
}
|
||||
if (propType.equals(Double.class) || propType.equals(double.class)){
|
||||
return Types.DOUBLE;
|
||||
}
|
||||
if (propType.equals(Float.class) || propType.equals(float.class)){
|
||||
return Types.REAL;
|
||||
}
|
||||
if (propType.equals(BigInteger.class)){
|
||||
return Types.BIGINT;
|
||||
}
|
||||
String msg = "Can not support Counter for type "+propType.getName();
|
||||
throw new PersistenceException(msg);
|
||||
}
|
||||
property.setGeneratedProperty(createCounter(property));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the GeneratedProperty based on the property type.
|
||||
*/
|
||||
private GeneratedProperty createCounter(DeployBeanProperty property) {
|
||||
|
||||
Class<?> propType = property.getPropertyType();
|
||||
if (propType.equals(Integer.class) || propType.equals(int.class)) {
|
||||
return integerCounter;
|
||||
}
|
||||
if (propType.equals(Long.class) || propType.equals(long.class)) {
|
||||
return longCounter;
|
||||
}
|
||||
|
||||
int type = getType(propType);
|
||||
return new GeneratedCounter(type);
|
||||
}
|
||||
|
||||
private int getType(Class<?> propType) {
|
||||
if (propType.equals(Short.class) || propType.equals(short.class)) {
|
||||
return Types.TINYINT;
|
||||
}
|
||||
if (propType.equals(BigDecimal.class)) {
|
||||
return Types.DECIMAL;
|
||||
}
|
||||
if (propType.equals(Double.class) || propType.equals(double.class)) {
|
||||
return Types.DOUBLE;
|
||||
}
|
||||
if (propType.equals(Float.class) || propType.equals(float.class)) {
|
||||
return Types.REAL;
|
||||
}
|
||||
if (propType.equals(BigInteger.class)) {
|
||||
return Types.BIGINT;
|
||||
}
|
||||
String msg = "Can not support Counter for type " + propType.getName();
|
||||
throw new PersistenceException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
+39
-37
@@ -9,49 +9,51 @@ import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
*/
|
||||
public class GeneratedCounter implements GeneratedProperty {
|
||||
|
||||
final int numberType;
|
||||
final int numberType;
|
||||
|
||||
public GeneratedCounter(int numberType) {
|
||||
this.numberType = numberType;
|
||||
}
|
||||
public GeneratedCounter(int numberType) {
|
||||
this.numberType = numberType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Always returns a 1.
|
||||
*/
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return BasicTypeConverter.convert(1, numberType);
|
||||
}
|
||||
/**
|
||||
* Always returns a 1.
|
||||
*/
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return BasicTypeConverter.convert(1, numberType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the current value by one.
|
||||
*/
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
Number currVal = (Number) prop.getValue(bean);
|
||||
Integer nextVal = currVal.intValue() + 1;
|
||||
return BasicTypeConverter.convert(nextVal, numberType);
|
||||
}
|
||||
/**
|
||||
* Increments the current value by one.
|
||||
*/
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
Number currVal = (Number) prop.getValue(bean);
|
||||
Integer nextVal = currVal.intValue() + 1;
|
||||
return BasicTypeConverter.convert(nextVal, numberType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Include this in every update.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Include this in every update.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include this in every insert setting initial counter value to 1.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Include this in every insert setting initial counter value to 1.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+37
-35
@@ -8,46 +8,48 @@ import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
*/
|
||||
public class GeneratedCounterInteger implements GeneratedProperty {
|
||||
|
||||
public GeneratedCounterInteger() {
|
||||
public GeneratedCounterInteger() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Always returns a 1.
|
||||
*/
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return 1;
|
||||
}
|
||||
/**
|
||||
* Always returns a 1.
|
||||
*/
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the current value by one.
|
||||
*/
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
Integer i = (Integer) prop.getValue(bean);
|
||||
return i + 1;
|
||||
}
|
||||
/**
|
||||
* Increments the current value by one.
|
||||
*/
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
Integer i = (Integer) prop.getValue(bean);
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include this in every update.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Include this in every update.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include this in every insert setting initial counter value to 1.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Include this in every insert setting initial counter value to 1.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+37
-35
@@ -8,46 +8,48 @@ import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
*/
|
||||
public class GeneratedCounterLong implements GeneratedProperty {
|
||||
|
||||
public GeneratedCounterLong() {
|
||||
public GeneratedCounterLong() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Always returns a 1.
|
||||
*/
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return (long) 1;
|
||||
}
|
||||
/**
|
||||
* Always returns a 1.
|
||||
*/
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return (long) 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the current value by one.
|
||||
*/
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
Long i = (Long) prop.getValue(bean);
|
||||
return i + 1;
|
||||
}
|
||||
/**
|
||||
* Increments the current value by one.
|
||||
*/
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
Long i = (Long) prop.getValue(bean);
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include this in every update.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Include this in every update.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include this in every insert setting initial counter value to 1.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Include this in every insert setting initial counter value to 1.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+36
-34
@@ -1,50 +1,52 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.generatedproperty;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Used to generate a (java.util.Date) timestamp when a bean is inserted.
|
||||
*/
|
||||
public class GeneratedInsertDate implements GeneratedProperty {
|
||||
|
||||
/**
|
||||
* Return the current time as a Timestamp.
|
||||
*/
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return new Date(System.currentTimeMillis());
|
||||
}
|
||||
/**
|
||||
* Return the current time as a Timestamp.
|
||||
*/
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return new Date(now);
|
||||
}
|
||||
|
||||
/**
|
||||
* Just returns the beans original insert timestamp value.
|
||||
*/
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
return prop.getValue(bean);
|
||||
}
|
||||
/**
|
||||
* Just returns the beans original insert timestamp value.
|
||||
*/
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return prop.getValue(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return false.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Return false.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Return true.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+7
-11
@@ -3,10 +3,6 @@ package com.avaje.ebeaninternal.server.deploy.generatedproperty;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
/**
|
||||
* Support java.time types as GeneratedProperty.
|
||||
*/
|
||||
@@ -35,7 +31,7 @@ public class GeneratedInsertJavaTime {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return prop.getValue(bean);
|
||||
}
|
||||
}
|
||||
@@ -46,8 +42,8 @@ public class GeneratedInsertJavaTime {
|
||||
public static class LocalDT extends Base {
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return LocalDateTime.now();
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return JavaTimeUtils.toLocalDateTime(now);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,8 +53,8 @@ public class GeneratedInsertJavaTime {
|
||||
public static class OffsetDT extends Base {
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return OffsetDateTime.now();
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return JavaTimeUtils.toOffsetDateTime(now);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -69,8 +65,8 @@ public class GeneratedInsertJavaTime {
|
||||
public static class ZonedDT extends Base {
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return ZonedDateTime.now();
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return JavaTimeUtils.toZonedDateTime(now);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
-7
@@ -2,9 +2,8 @@ package com.avaje.ebeaninternal.server.deploy.generatedproperty;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
|
||||
import org.joda.time.LocalDateTime;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* Support joda time types as GeneratedProperty.
|
||||
@@ -34,7 +33,7 @@ public class GeneratedInsertJodaTime {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return prop.getValue(bean);
|
||||
}
|
||||
}
|
||||
@@ -45,8 +44,8 @@ public class GeneratedInsertJodaTime {
|
||||
public static class LocalDT extends Base {
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return new LocalDateTime();
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return new LocalDateTime(now);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,8 +55,8 @@ public class GeneratedInsertJodaTime {
|
||||
public static class DateTimeDT extends Base {
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return new DateTime();
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return new DateTime(now);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+34
-32
@@ -8,41 +8,43 @@ import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
*/
|
||||
public class GeneratedInsertLong implements GeneratedProperty {
|
||||
|
||||
/**
|
||||
* Return the current time as a Timestamp.
|
||||
*/
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
/**
|
||||
* Return the current time as a Timestamp.
|
||||
*/
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return now;
|
||||
}
|
||||
|
||||
/**
|
||||
* Just returns the beans original insert timestamp value.
|
||||
*/
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
return prop.getValue(bean);
|
||||
}
|
||||
/**
|
||||
* Just returns the beans original insert timestamp value.
|
||||
*/
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return prop.getValue(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return false.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Return false.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Return true.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+36
-34
@@ -1,50 +1,52 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.generatedproperty;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
/**
|
||||
* Used to generate a timestamp when a bean is inserted.
|
||||
*/
|
||||
public class GeneratedInsertTimestamp implements GeneratedProperty, GeneratedWhenCreated {
|
||||
|
||||
/**
|
||||
* Return the current time as a Timestamp.
|
||||
*/
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return new Timestamp(System.currentTimeMillis());
|
||||
}
|
||||
/**
|
||||
* Return the current time as a Timestamp.
|
||||
*/
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return new Timestamp(now);
|
||||
}
|
||||
|
||||
/**
|
||||
* Just returns the beans original insert timestamp value.
|
||||
*/
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
return prop.getValue(bean);
|
||||
}
|
||||
/**
|
||||
* Just returns the beans original insert timestamp value.
|
||||
*/
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return prop.getValue(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return false.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Return false.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Return true.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+30
-30
@@ -9,39 +9,39 @@ import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
*/
|
||||
public interface GeneratedProperty {
|
||||
|
||||
/**
|
||||
* Get the generated insert value for a specific property of a bean.
|
||||
*/
|
||||
Object getInsertValue(BeanProperty prop, EntityBean bean);
|
||||
/**
|
||||
* Get the generated insert value for a specific property of a bean.
|
||||
*/
|
||||
Object getInsertValue(BeanProperty prop, EntityBean bean, long now);
|
||||
|
||||
/**
|
||||
* Get the generated update value for a specific property of a bean.
|
||||
*/
|
||||
Object getUpdateValue(BeanProperty prop, EntityBean bean);
|
||||
/**
|
||||
* Get the generated update value for a specific property of a bean.
|
||||
*/
|
||||
Object getUpdateValue(BeanProperty prop, EntityBean bean, long now);
|
||||
|
||||
/**
|
||||
* Return true if this should always be includes in an update statement.
|
||||
* <p>
|
||||
* Used to include GeneratedUpdateTimestamp in dynamic table updates.
|
||||
* </p>
|
||||
*/
|
||||
boolean includeInUpdate();
|
||||
|
||||
/**
|
||||
* Return true if the property should be included in an update even if
|
||||
* it is not loaded (ie. Last Updated Timestamp).
|
||||
*/
|
||||
boolean includeInAllUpdates();
|
||||
/**
|
||||
* Return true if this should always be includes in an update statement.
|
||||
* <p>
|
||||
* Used to include GeneratedUpdateTimestamp in dynamic table updates.
|
||||
* </p>
|
||||
*/
|
||||
boolean includeInUpdate();
|
||||
|
||||
/**
|
||||
* Return true if this should be included in insert statements.
|
||||
*/
|
||||
boolean includeInInsert();
|
||||
/**
|
||||
* Return true if the property should be included in an update even if
|
||||
* it is not loaded (ie. Last Updated Timestamp).
|
||||
*/
|
||||
boolean includeInAllUpdates();
|
||||
|
||||
/**
|
||||
* Return true if the GeneratedProperty implies the DDL to create the DB
|
||||
* column should have a not null constraint.
|
||||
*/
|
||||
boolean isDDLNotNullable();
|
||||
/**
|
||||
* Return true if this should be included in insert statements.
|
||||
*/
|
||||
boolean includeInInsert();
|
||||
|
||||
/**
|
||||
* Return true if the GeneratedProperty implies the DDL to create the DB
|
||||
* column should have a not null constraint.
|
||||
*/
|
||||
boolean isDDLNotNullable();
|
||||
|
||||
}
|
||||
|
||||
+38
-38
@@ -1,19 +1,19 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.generatedproperty;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashSet;
|
||||
|
||||
import com.avaje.ebean.config.ClassLoadConfig;
|
||||
import com.avaje.ebean.config.CurrentUserProvider;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* Default implementation of GeneratedPropertyFactory.
|
||||
*/
|
||||
public class GeneratedPropertyFactory {
|
||||
|
||||
private final CounterFactory counterFactory = new CounterFactory();
|
||||
private final CounterFactory counterFactory = new CounterFactory();
|
||||
|
||||
private final InsertTimestampFactory insertFactory;
|
||||
|
||||
@@ -34,7 +34,7 @@ public class GeneratedPropertyFactory {
|
||||
this.updateFactory = new UpdateTimestampFactory(classLoadConfig);
|
||||
|
||||
CurrentUserProvider currentUserProvider = serverConfig.getCurrentUserProvider();
|
||||
if (currentUserProvider != null) {
|
||||
if (currentUserProvider != null) {
|
||||
generatedWhoCreated = new GeneratedWhoCreated(currentUserProvider);
|
||||
generatedWhoModified = new GeneratedWhoModified(currentUserProvider);
|
||||
} else {
|
||||
@@ -42,47 +42,47 @@ public class GeneratedPropertyFactory {
|
||||
generatedWhoModified = null;
|
||||
}
|
||||
|
||||
numberTypes.add(Integer.class.getName());
|
||||
numberTypes.add(int.class.getName());
|
||||
numberTypes.add(Long.class.getName());
|
||||
numberTypes.add(long.class.getName());
|
||||
numberTypes.add(Short.class.getName());
|
||||
numberTypes.add(short.class.getName());
|
||||
numberTypes.add(Double.class.getName());
|
||||
numberTypes.add(double.class.getName());
|
||||
numberTypes.add(BigDecimal.class.getName());
|
||||
}
|
||||
numberTypes.add(Integer.class.getName());
|
||||
numberTypes.add(int.class.getName());
|
||||
numberTypes.add(Long.class.getName());
|
||||
numberTypes.add(long.class.getName());
|
||||
numberTypes.add(Short.class.getName());
|
||||
numberTypes.add(short.class.getName());
|
||||
numberTypes.add(Double.class.getName());
|
||||
numberTypes.add(double.class.getName());
|
||||
numberTypes.add(BigDecimal.class.getName());
|
||||
}
|
||||
|
||||
public ClassLoadConfig getClassLoadConfig() {
|
||||
return classLoadConfig;
|
||||
}
|
||||
|
||||
private boolean isNumberType(String typeClassName) {
|
||||
return numberTypes.contains(typeClassName);
|
||||
}
|
||||
|
||||
public void setVersion(DeployBeanProperty property) {
|
||||
if (isNumberType(property.getPropertyType().getName())) {
|
||||
setCounter(property);
|
||||
} else {
|
||||
setUpdateTimestamp(property);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCounter(DeployBeanProperty property) {
|
||||
|
||||
counterFactory.setCounter(property);
|
||||
}
|
||||
return numberTypes.contains(typeClassName);
|
||||
}
|
||||
|
||||
public void setInsertTimestamp(DeployBeanProperty property) {
|
||||
|
||||
insertFactory.setInsertTimestamp(property);
|
||||
}
|
||||
public void setVersion(DeployBeanProperty property) {
|
||||
if (isNumberType(property.getPropertyType().getName())) {
|
||||
setCounter(property);
|
||||
} else {
|
||||
setUpdateTimestamp(property);
|
||||
}
|
||||
}
|
||||
|
||||
public void setUpdateTimestamp(DeployBeanProperty property) {
|
||||
|
||||
updateFactory.setUpdateTimestamp(property);
|
||||
}
|
||||
public void setCounter(DeployBeanProperty property) {
|
||||
|
||||
counterFactory.setCounter(property);
|
||||
}
|
||||
|
||||
public void setInsertTimestamp(DeployBeanProperty property) {
|
||||
|
||||
insertFactory.setInsertTimestamp(property);
|
||||
}
|
||||
|
||||
public void setUpdateTimestamp(DeployBeanProperty property) {
|
||||
|
||||
updateFactory.setUpdateTimestamp(property);
|
||||
}
|
||||
|
||||
public void setWhoCreated(DeployBeanProperty property) {
|
||||
if (generatedWhoCreated == null) {
|
||||
|
||||
+34
-34
@@ -1,51 +1,51 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.generatedproperty;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Generate a (java.util.Date) Timestamp whenever the bean is inserted or
|
||||
* updated.
|
||||
*/
|
||||
public class GeneratedUpdateDate implements GeneratedProperty {
|
||||
|
||||
/**
|
||||
* Return now as a Timestamp.
|
||||
*/
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return new Date(System.currentTimeMillis());
|
||||
}
|
||||
/**
|
||||
* Return now as a Timestamp.
|
||||
*/
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return new Date(now);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return now as a Timestamp.
|
||||
*/
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
return new Date(System.currentTimeMillis());
|
||||
}
|
||||
/**
|
||||
* Return now as a Timestamp.
|
||||
*/
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return new Date(now);
|
||||
}
|
||||
|
||||
/**
|
||||
* For dynamic table updates make sure this is included.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* For dynamic table updates make sure this is included.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include this in every insert.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Include this in every insert.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+12
-16
@@ -3,10 +3,6 @@ package com.avaje.ebeaninternal.server.deploy.generatedproperty;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
/**
|
||||
* Support java.time DateTime types as GeneratedProperty.
|
||||
*/
|
||||
@@ -41,13 +37,13 @@ public class GeneratedUpdateJavaTime {
|
||||
public static class LocalDT extends Base {
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return LocalDateTime.now();
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return JavaTimeUtils.toLocalDateTime(now);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
return LocalDateTime.now();
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return JavaTimeUtils.toLocalDateTime(now);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,13 +53,13 @@ public class GeneratedUpdateJavaTime {
|
||||
public static class OffsetDT extends Base {
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return OffsetDateTime.now();
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return JavaTimeUtils.toOffsetDateTime(now);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
return OffsetDateTime.now();
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return JavaTimeUtils.toOffsetDateTime(now);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,13 +69,13 @@ public class GeneratedUpdateJavaTime {
|
||||
public static class ZonedDT extends Base {
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return ZonedDateTime.now();
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return JavaTimeUtils.toZonedDateTime(now);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
return ZonedDateTime.now();
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return JavaTimeUtils.toZonedDateTime(now);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-10
@@ -2,9 +2,8 @@ package com.avaje.ebeaninternal.server.deploy.generatedproperty;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
|
||||
import org.joda.time.LocalDateTime;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* Support java.time DateTime types as GeneratedProperty.
|
||||
@@ -40,13 +39,13 @@ public class GeneratedUpdateJodaTime {
|
||||
public static class LocalDT extends Base {
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return new LocalDateTime();
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return new LocalDateTime(now);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
return new LocalDateTime();
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return new LocalDateTime(now);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,13 +55,13 @@ public class GeneratedUpdateJodaTime {
|
||||
public static class DateTimeDT extends Base {
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return new DateTime();
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return new DateTime(now);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
return new DateTime();
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return new DateTime(now);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+32
-32
@@ -8,41 +8,41 @@ import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
*/
|
||||
public class GeneratedUpdateLong implements GeneratedProperty {
|
||||
|
||||
/**
|
||||
* Return now as a Timestamp.
|
||||
*/
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
/**
|
||||
* Return now as a Timestamp.
|
||||
*/
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return now;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return now as a Timestamp.
|
||||
*/
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
/**
|
||||
* Return now as a Timestamp.
|
||||
*/
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return now;
|
||||
}
|
||||
|
||||
/**
|
||||
* For dynamic table updates make sure this is included.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* For dynamic table updates make sure this is included.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include this in every insert.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Include this in every insert.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+34
-34
@@ -1,50 +1,50 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.generatedproperty;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
/**
|
||||
* Generate a Timestamp whenever the bean is inserted or updated.
|
||||
*/
|
||||
public class GeneratedUpdateTimestamp implements GeneratedProperty, GeneratedWhenModified {
|
||||
|
||||
/**
|
||||
* Return now as a Timestamp.
|
||||
*/
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
return new Timestamp(System.currentTimeMillis());
|
||||
}
|
||||
/**
|
||||
* Return now as a Timestamp.
|
||||
*/
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return new Timestamp(now);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return now as a Timestamp.
|
||||
*/
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
return new Timestamp(System.currentTimeMillis());
|
||||
}
|
||||
/**
|
||||
* Return now as a Timestamp.
|
||||
*/
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return new Timestamp(now);
|
||||
}
|
||||
|
||||
/**
|
||||
* For dynamic table updates make sure this is included.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* For dynamic table updates make sure this is included.
|
||||
*/
|
||||
public boolean includeInUpdate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include this in every insert.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Include this in every insert.
|
||||
*/
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -16,12 +16,12 @@ public class GeneratedWhoCreated implements GeneratedProperty {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return currentUserProvider.currentUser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -16,12 +16,12 @@ public class GeneratedWhoModified implements GeneratedProperty {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean) {
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return currentUserProvider.currentUser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean) {
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return currentUserProvider.currentUser();
|
||||
}
|
||||
|
||||
|
||||
+17
-18
@@ -1,5 +1,9 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.generatedproperty;
|
||||
|
||||
import com.avaje.ebean.config.ClassLoadConfig;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
@@ -7,17 +11,12 @@ import java.time.ZonedDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.config.ClassLoadConfig;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
|
||||
/**
|
||||
* Helper for creating Insert timestamp GeneratedProperty objects.
|
||||
*/
|
||||
public class InsertTimestampFactory {
|
||||
|
||||
final GeneratedInsertLong longTime = new GeneratedInsertLong();
|
||||
final GeneratedInsertLong longTime = new GeneratedInsertLong();
|
||||
|
||||
final Map<Class<?>, GeneratedProperty> map = new HashMap<Class<?>, GeneratedProperty>();
|
||||
|
||||
@@ -41,21 +40,21 @@ public class InsertTimestampFactory {
|
||||
|
||||
public void setInsertTimestamp(DeployBeanProperty property) {
|
||||
|
||||
property.setGeneratedProperty(createInsertTimestamp(property));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the insert GeneratedProperty depending on the property type.
|
||||
*/
|
||||
public GeneratedProperty createInsertTimestamp(DeployBeanProperty property) {
|
||||
|
||||
Class<?> propType = property.getPropertyType();
|
||||
property.setGeneratedProperty(createInsertTimestamp(property));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the insert GeneratedProperty depending on the property type.
|
||||
*/
|
||||
public GeneratedProperty createInsertTimestamp(DeployBeanProperty property) {
|
||||
|
||||
Class<?> propType = property.getPropertyType();
|
||||
GeneratedProperty generatedProperty = map.get(propType);
|
||||
if (generatedProperty != null) {
|
||||
return generatedProperty;
|
||||
}
|
||||
|
||||
throw new PersistenceException("Generated Insert Timestamp not supported on "+propType.getName());
|
||||
}
|
||||
|
||||
throw new PersistenceException("Generated Insert Timestamp not supported on " + propType.getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.generatedproperty;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
/**
|
||||
* Helper methods for Java time conversion.
|
||||
*/
|
||||
public class JavaTimeUtils {
|
||||
|
||||
/**
|
||||
* Return the system millis time as a LocalDateTime.
|
||||
*/
|
||||
public static Object toLocalDateTime(long systemMillis) {
|
||||
return new Timestamp(systemMillis).toLocalDateTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the system millis time as a OffsetDateTime.
|
||||
*/
|
||||
public static Object toOffsetDateTime(long systemMillis) {
|
||||
return OffsetDateTime.ofInstant(Instant.ofEpochMilli(systemMillis), ZoneId.systemDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the system millis time as a ZonedDateTime.
|
||||
*/
|
||||
public static Object toZonedDateTime(long systemMillis) {
|
||||
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(systemMillis), ZoneId.systemDefault());
|
||||
}
|
||||
}
|
||||
+16
-17
@@ -1,5 +1,9 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.generatedproperty;
|
||||
|
||||
import com.avaje.ebean.config.ClassLoadConfig;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
@@ -7,17 +11,12 @@ import java.time.ZonedDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.config.ClassLoadConfig;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
|
||||
/**
|
||||
* Helper for creating Update timestamp GeneratedProperty objects.
|
||||
*/
|
||||
public class UpdateTimestampFactory {
|
||||
|
||||
final GeneratedUpdateLong longTime = new GeneratedUpdateLong();
|
||||
final GeneratedUpdateLong longTime = new GeneratedUpdateLong();
|
||||
|
||||
final Map<Class<?>, GeneratedProperty> map = new HashMap<Class<?>, GeneratedProperty>();
|
||||
|
||||
@@ -38,15 +37,15 @@ public class UpdateTimestampFactory {
|
||||
}
|
||||
}
|
||||
|
||||
public void setUpdateTimestamp(DeployBeanProperty property) {
|
||||
public void setUpdateTimestamp(DeployBeanProperty property) {
|
||||
|
||||
property.setGeneratedProperty(createUpdateTimestamp(property));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the update GeneratedProperty depending on the property type.
|
||||
*/
|
||||
protected GeneratedProperty createUpdateTimestamp(DeployBeanProperty property) {
|
||||
property.setGeneratedProperty(createUpdateTimestamp(property));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the update GeneratedProperty depending on the property type.
|
||||
*/
|
||||
protected GeneratedProperty createUpdateTimestamp(DeployBeanProperty property) {
|
||||
|
||||
Class<?> propType = property.getPropertyType();
|
||||
GeneratedProperty generatedProperty = map.get(propType);
|
||||
@@ -54,7 +53,7 @@ public class UpdateTimestampFactory {
|
||||
return generatedProperty;
|
||||
}
|
||||
|
||||
throw new PersistenceException("Generated update Timestamp not supported on "+propType.getName());
|
||||
}
|
||||
|
||||
throw new PersistenceException("Generated update Timestamp not supported on " + propType.getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=iso-8859-1">
|
||||
<TITLE>Counter, Insert Timestamp, Update Timestamp support</TITLE>
|
||||
<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=iso-8859-1">
|
||||
<TITLE>Counter, Insert Timestamp, Update Timestamp support</TITLE>
|
||||
</HEAD>
|
||||
<Body BGCOLOR="#ffffff">
|
||||
Counter, Insert Timestamp, Update Timestamp support
|
||||
|
||||
+32
-14
@@ -14,6 +14,8 @@ import com.avaje.ebeaninternal.api.SpiExpressionValidation;
|
||||
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
import com.avaje.ebeaninternal.server.query.SplitName;
|
||||
|
||||
/**
|
||||
* A "Query By Example" type of expression.
|
||||
@@ -207,25 +209,41 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
|
||||
OrmQueryRequest<?> r = (OrmQueryRequest<?>) request;
|
||||
BeanDescriptor<?> beanDescriptor = r.getBeanDescriptor();
|
||||
|
||||
for (BeanProperty beanProperty : beanDescriptor.propertiesAll()) {
|
||||
|
||||
String propName = beanProperty.getName();
|
||||
Object value = beanProperty.getValue(entity);
|
||||
addExpressions(list, beanDescriptor, entity, null);
|
||||
|
||||
if (beanProperty.isScalar() && value != null) {
|
||||
if (value instanceof String) {
|
||||
list.add(new LikeExpression(propName, (String) value, caseInsensitive, likeType));
|
||||
} else {
|
||||
// exclude the zero values typically to weed out
|
||||
// primitive int and long that initialise to 0
|
||||
if (includeZeros || !isZero(value)) {
|
||||
list.add(new SimpleExpression(propName, Op.EQ, value));
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add expressions to the list for all the non-null properties (and do this recursively).
|
||||
*/
|
||||
private void addExpressions(ArrayList<SpiExpression> list, BeanDescriptor<?> beanDescriptor, EntityBean bean, String prefix) {
|
||||
|
||||
for (BeanProperty beanProperty : beanDescriptor.propertiesAll()) {
|
||||
|
||||
if (!beanProperty.isTransient()) {
|
||||
Object value = beanProperty.getValue(bean);
|
||||
if (value != null) {
|
||||
String propName = SplitName.add(prefix, beanProperty.getName());
|
||||
if (beanProperty.isScalar()) {
|
||||
if (value instanceof String) {
|
||||
list.add(new LikeExpression(propName, (String) value, caseInsensitive, likeType));
|
||||
} else {
|
||||
// exclude the zero values typically to weed out
|
||||
// primitive int and long that initialise to 0
|
||||
if (includeZeros || !isZero(value)) {
|
||||
list.add(new SimpleExpression(propName, Op.EQ, value));
|
||||
}
|
||||
}
|
||||
|
||||
} else if ((beanProperty instanceof BeanPropertyAssocOne) && (value instanceof EntityBean)) {
|
||||
BeanPropertyAssocOne assocOne = (BeanPropertyAssocOne)beanProperty;
|
||||
BeanDescriptor targetDescriptor = assocOne.getTargetDescriptor();
|
||||
addExpressions(list, targetDescriptor, (EntityBean)value, propName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -39,6 +39,8 @@ public abstract class DmlHandler implements PersistHandler, BindableRequest {
|
||||
|
||||
protected final boolean logLevelSql;
|
||||
|
||||
protected final long now;
|
||||
|
||||
/**
|
||||
* The PreparedStatement used for the dml.
|
||||
*/
|
||||
@@ -49,6 +51,7 @@ public abstract class DmlHandler implements PersistHandler, BindableRequest {
|
||||
protected ArrayList<UpdateGenValue> updateGenValues;
|
||||
|
||||
protected DmlHandler(PersistRequestBean<?> persistRequest, boolean emptyStringToNull) {
|
||||
this.now = System.currentTimeMillis();
|
||||
this.persistRequest = persistRequest;
|
||||
this.emptyStringToNull = emptyStringToNull;
|
||||
this.transaction = persistRequest.getTransaction();
|
||||
@@ -60,6 +63,11 @@ public abstract class DmlHandler implements PersistHandler, BindableRequest {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long now() {
|
||||
return now;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistRequestBean<?> getPersistRequest() {
|
||||
return persistRequest;
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ public class BindablePropertyInsertGenerated extends BindableProperty {
|
||||
|
||||
public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
|
||||
|
||||
Object value = gen.getInsertValue(prop, bean);
|
||||
Object value = gen.getInsertValue(prop, bean, request.now());
|
||||
|
||||
// generated value should be the correct type
|
||||
if (bean != null) {
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ public class BindablePropertyUpdateGenerated extends BindableProperty {
|
||||
|
||||
public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
|
||||
|
||||
Object value = gen.getUpdateValue(prop, bean);
|
||||
Object value = gen.getUpdateValue(prop, bean, request.now());
|
||||
|
||||
// generated value should be the correct type
|
||||
request.bind(value, prop);
|
||||
|
||||
@@ -56,4 +56,11 @@ public interface BindableRequest {
|
||||
PersistRequestBean<?> getPersistRequest();
|
||||
|
||||
void registerDerivedRelationship(DerivedRelationshipData assocBean);
|
||||
|
||||
/**
|
||||
* Return the system current time in millis. This is expected to the same time used
|
||||
* by multiple generated properties for a single request.
|
||||
*/
|
||||
long now();
|
||||
|
||||
}
|
||||
|
||||
@@ -603,7 +603,7 @@ public class SqlTreeBuilder {
|
||||
// no extra join required for embedded beans
|
||||
return null;
|
||||
}
|
||||
SqlTreeNodeExtraJoin extraJoin = new SqlTreeNodeExtraJoin(propertyName, assocProp);
|
||||
SqlTreeNodeExtraJoin extraJoin = new SqlTreeNodeExtraJoin(propertyName, assocProp, elGetValue.containsMany());
|
||||
joinRegister.put(propertyName, extraJoin);
|
||||
return extraJoin;
|
||||
}
|
||||
|
||||
@@ -29,11 +29,14 @@ public class SqlTreeNodeExtraJoin implements SqlTreeNode {
|
||||
|
||||
private final boolean manyJoin;
|
||||
|
||||
private final boolean pathContainsMany;
|
||||
|
||||
private List<SqlTreeNodeExtraJoin> children;
|
||||
|
||||
public SqlTreeNodeExtraJoin(String prefix, BeanPropertyAssoc<?> assocBeanProperty) {
|
||||
public SqlTreeNodeExtraJoin(String prefix, BeanPropertyAssoc<?> assocBeanProperty, boolean pathContainsMany) {
|
||||
this.prefix = prefix;
|
||||
this.assocBeanProperty = assocBeanProperty;
|
||||
this.pathContainsMany = pathContainsMany;
|
||||
this.manyJoin = assocBeanProperty instanceof BeanPropertyAssocMany<?>;
|
||||
}
|
||||
|
||||
@@ -95,13 +98,17 @@ public class SqlTreeNodeExtraJoin implements SqlTreeNode {
|
||||
}
|
||||
}
|
||||
|
||||
if (pathContainsMany) {
|
||||
// "promote" to left outer as the path contains a many
|
||||
joinType = SqlJoinType.OUTER;
|
||||
}
|
||||
if (!manyToMany) {
|
||||
assocBeanProperty.addJoin(joinType, prefix, ctx);
|
||||
}
|
||||
|
||||
if (children != null) {
|
||||
|
||||
if (manyJoin) {
|
||||
if (manyJoin || pathContainsMany) {
|
||||
// if AUTO then make all descendants use OUTER JOIN
|
||||
joinType = joinType.autoToOuter();
|
||||
}
|
||||
|
||||
+21
@@ -2,11 +2,13 @@ package com.avaje.ebeaninternal.server.transaction;
|
||||
|
||||
import com.avaje.ebean.BackgroundExecutor;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
import com.avaje.ebeaninternal.server.cluster.ClusterManager;
|
||||
import com.avaje.ebeaninternal.server.core.BootupClasses;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
|
||||
/**
|
||||
@@ -29,4 +31,23 @@ public class ExplicitTransactionManager extends TransactionManager {
|
||||
return new ExplicitJdbcTransaction(prefix + id, explicit, c, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the initialise of OnQueryOnly with the intention not to use CLOSE with ExplicitJdbcTransaction.
|
||||
*/
|
||||
@Override
|
||||
protected DatabasePlatform.OnQueryOnly initOnQueryOnly(DatabasePlatform.OnQueryOnly dbPlatformOnQueryOnly, DataSource ds) {
|
||||
|
||||
// first check for a system property 'override'
|
||||
String systemPropertyValue = System.getProperty("ebean.transaction.onqueryonly");
|
||||
if (systemPropertyValue != null) {
|
||||
return DatabasePlatform.OnQueryOnly.valueOf(systemPropertyValue.trim().toUpperCase());
|
||||
}
|
||||
|
||||
if (DatabasePlatform.OnQueryOnly.CLOSE.equals(dbPlatformOnQueryOnly)) {
|
||||
// Not using OnQueryOnly.CLOSE with ExplicitJdbcTransaction
|
||||
return DatabasePlatform.OnQueryOnly.COMMIT;
|
||||
}
|
||||
// default to commit if not defined on the platform
|
||||
return dbPlatformOnQueryOnly == null ? DatabasePlatform.OnQueryOnly.COMMIT : dbPlatformOnQueryOnly;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ public class TransactionManager {
|
||||
* just for queries do need to be committed or rollback after the query.
|
||||
* </p>
|
||||
*/
|
||||
private OnQueryOnly initOnQueryOnly(OnQueryOnly dbPlatformOnQueryOnly, DataSource ds) {
|
||||
protected OnQueryOnly initOnQueryOnly(OnQueryOnly dbPlatformOnQueryOnly, DataSource ds) {
|
||||
|
||||
// first check for a system property 'override'
|
||||
String systemPropertyValue = System.getProperty("ebean.transaction.onqueryonly");
|
||||
@@ -190,7 +190,7 @@ public class TransactionManager {
|
||||
/**
|
||||
* Return true if the isolation level is read committed.
|
||||
*/
|
||||
private boolean isReadCommittedIsolation(DataSource ds) {
|
||||
protected boolean isReadCommittedIsolation(DataSource ds) {
|
||||
|
||||
if (DbOffline.isSet()) {
|
||||
return true;
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="withHistory" type="xsd:boolean"/>
|
||||
<xsd:attribute name="draft" type="xsd:boolean"/>
|
||||
<xsd:attribute name="identityType" type="identityType"/>
|
||||
<xsd:attribute name="sequenceName" type="xsd:string"/>
|
||||
<xsd:attribute name="sequenceInitial" type="xsd:positiveInteger"/>
|
||||
|
||||
@@ -24,4 +24,9 @@ public class BaseTestCase {
|
||||
SpiEbeanServer spi = (SpiEbeanServer)Ebean.getDefaultServer();
|
||||
return spi.getDatabasePlatform().getName().startsWith("mssqlserver");
|
||||
}
|
||||
|
||||
public boolean isH2() {
|
||||
SpiEbeanServer spi = (SpiEbeanServer)Ebean.getDefaultServer();
|
||||
return spi.getDatabasePlatform().getName().equals("h2");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ public class PrimaryServerTest {
|
||||
public void testLoadProperties() throws Exception {
|
||||
|
||||
Properties properties = PrimaryServer.getProperties();
|
||||
System.out.println(properties);
|
||||
assertTrue(properties.size() > 0);
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
|
||||
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.config.dbplatform.H2Platform;
|
||||
import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite;
|
||||
import com.avaje.ebean.dbmigration.model.CurrentModel;
|
||||
import com.avaje.ebean.dbmigration.model.MConfiguration;
|
||||
import com.avaje.ebean.dbmigration.model.ModelContainer;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.StrictAssertions.assertThat;
|
||||
|
||||
|
||||
public class H2HistoryDdlTest {
|
||||
|
||||
@Test
|
||||
public void testRegenerateHistoryTriggers() throws Exception {
|
||||
|
||||
SpiEbeanServer ebeanServer = (SpiEbeanServer)Ebean.getDefaultServer();
|
||||
|
||||
HistoryTableUpdate update = new HistoryTableUpdate("c_user");
|
||||
update.add(HistoryTableUpdate.Change.ADD, "one");
|
||||
update.add(HistoryTableUpdate.Change.DROP, "two");
|
||||
|
||||
|
||||
CurrentModel currentModel = new CurrentModel(ebeanServer);
|
||||
ModelContainer modelContainer = currentModel.read();
|
||||
DdlWrite write = new DdlWrite(new MConfiguration(), modelContainer);
|
||||
|
||||
H2Platform h2Platform = new H2Platform();
|
||||
PlatformDdl h2Ddl = h2Platform.getPlatformDdl();
|
||||
h2Ddl.configure(ebeanServer.getServerConfig());
|
||||
h2Ddl.regenerateHistoryTriggers(write, update);
|
||||
|
||||
assertThat(write.dropHistory().isEmpty()).isFalse();
|
||||
assertThat(write.dropHistory().getBuffer()).contains("drop two");
|
||||
|
||||
assertThat(write.applyHistory().isEmpty()).isFalse();
|
||||
assertThat(write.applyHistory().getBuffer()).contains("add one");
|
||||
assertThat(write.applyHistory().getBuffer()).doesNotContain("two");
|
||||
|
||||
assertThat(write.rollback().isEmpty()).isFalse();
|
||||
|
||||
}
|
||||
}
|
||||
+31
-3
@@ -14,7 +14,11 @@ public class HistoryTableUpdateTest {
|
||||
public void testToRevertedColumns_add() throws Exception {
|
||||
|
||||
HistoryTableUpdate upd = new HistoryTableUpdate("mytab");
|
||||
assertThat(upd.getBaseTable()).isEqualTo("mytab");
|
||||
|
||||
upd.add(HistoryTableUpdate.Change.ADD, "two");
|
||||
assertThat(upd.hasApplyChanges()).isTrue();
|
||||
assertThat(upd.hasDropChanges()).isFalse();
|
||||
|
||||
List<String> current = current();
|
||||
upd.toRevertedColumns(current);
|
||||
@@ -26,6 +30,8 @@ public class HistoryTableUpdateTest {
|
||||
|
||||
HistoryTableUpdate upd = new HistoryTableUpdate("mytab");
|
||||
upd.add(HistoryTableUpdate.Change.INCLUDE, "two");
|
||||
assertThat(upd.hasApplyChanges()).isTrue();
|
||||
assertThat(upd.hasDropChanges()).isFalse();
|
||||
|
||||
List<String> current = current();
|
||||
upd.toRevertedColumns(current);
|
||||
@@ -36,11 +42,13 @@ public class HistoryTableUpdateTest {
|
||||
public void testToRevertedColumns_drop() throws Exception {
|
||||
|
||||
HistoryTableUpdate upd = new HistoryTableUpdate("mytab");
|
||||
upd.add(HistoryTableUpdate.Change.DROP, "four");
|
||||
upd.add(HistoryTableUpdate.Change.DROP, "three");
|
||||
assertThat(upd.hasApplyChanges()).isFalse();
|
||||
assertThat(upd.hasDropChanges()).isTrue();
|
||||
|
||||
List<String> current = current();
|
||||
upd.toRevertedColumns(current);
|
||||
assertThat(current).contains("one","two","three","four");
|
||||
assertThat(current).contains("one","two","three");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -48,6 +56,8 @@ public class HistoryTableUpdateTest {
|
||||
|
||||
HistoryTableUpdate upd = new HistoryTableUpdate("mytab");
|
||||
upd.add(HistoryTableUpdate.Change.EXCLUDE, "four");
|
||||
assertThat(upd.hasApplyChanges()).isTrue();
|
||||
assertThat(upd.hasDropChanges()).isFalse();
|
||||
|
||||
List<String> current = current();
|
||||
upd.toRevertedColumns(current);
|
||||
@@ -61,8 +71,26 @@ public class HistoryTableUpdateTest {
|
||||
HistoryTableUpdate upd = new HistoryTableUpdate("mytab");
|
||||
upd.add(HistoryTableUpdate.Change.ADD, "two");
|
||||
upd.add(HistoryTableUpdate.Change.DROP, "four");
|
||||
assertThat(upd.hasApplyChanges()).isTrue();
|
||||
assertThat(upd.hasDropChanges()).isTrue();
|
||||
|
||||
assertThat(upd.description()).isEqualTo("add two, drop four");
|
||||
assertThat(upd.descriptionForApply()).isEqualTo("add two");
|
||||
assertThat(upd.descriptionForDrop()).isEqualTo("drop four");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDescription_withIncludeExclude() throws Exception {
|
||||
|
||||
HistoryTableUpdate upd = new HistoryTableUpdate("mytab");
|
||||
upd.add(HistoryTableUpdate.Change.ADD, "two");
|
||||
upd.add(HistoryTableUpdate.Change.INCLUDE, "five");
|
||||
upd.add(HistoryTableUpdate.Change.EXCLUDE, "six");
|
||||
upd.add(HistoryTableUpdate.Change.DROP, "four");
|
||||
assertThat(upd.hasApplyChanges()).isTrue();
|
||||
assertThat(upd.hasDropChanges()).isTrue();
|
||||
|
||||
assertThat(upd.descriptionForApply()).isEqualTo("add two, include five, exclude six");
|
||||
assertThat(upd.descriptionForDrop()).isEqualTo("drop four");
|
||||
}
|
||||
|
||||
List<String> current() {
|
||||
|
||||
+15
-7
@@ -2,25 +2,33 @@ package com.avaje.ebean.dbmigration.migrationreader;
|
||||
|
||||
import com.avaje.ebean.dbmigration.migration.Migration;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class MigrationXmlWriterTest {
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
Logger logger = LoggerFactory.getLogger(MigrationXmlWriterTest.class);
|
||||
public class MigrationXmlWriterTest {
|
||||
|
||||
@Test
|
||||
public void testReadWrite() throws Exception {
|
||||
|
||||
Migration migration = MigrationXmlReader.read("/container/test-create-table.xml");
|
||||
assertThat(migration.getChangeSet()).hasSize(1);
|
||||
assertThat(migration.getChangeSet().get(0).getChangeSetChildren()).hasSize(3);
|
||||
|
||||
File temp = File.createTempFile("migrationWrite",".xml");
|
||||
MigrationXmlWriter writer = new MigrationXmlWriter();
|
||||
writer.write(migration, temp);
|
||||
new MigrationXmlWriter("THIS IS A GENERATED FILE - DO NOT MODIFY").write(migration, temp);
|
||||
|
||||
logger.info("wrote migration file: "+temp.getAbsolutePath());
|
||||
Migration migrationRead = MigrationXmlReader.read(temp);
|
||||
assertThat(migrationRead.getChangeSet()).hasSize(1);
|
||||
assertThat(migrationRead.getChangeSet().get(0).getChangeSetChildren()).hasSize(3);
|
||||
|
||||
temp = File.createTempFile("migrationWrite",".xml");
|
||||
new MigrationXmlWriter(null).write(migration, temp);
|
||||
|
||||
Migration migrationRead2 = MigrationXmlReader.read(temp);
|
||||
|
||||
assertThat(migrationRead2.getChangeSet()).hasSize(1);
|
||||
assertThat(migrationRead.getChangeSet().get(0).getChangeSetChildren()).hasSize(3);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
package com.avaje.ebean.dbmigration.model;
|
||||
|
||||
import com.avaje.ebean.dbmigration.migration.AddColumn;
|
||||
import com.avaje.ebean.dbmigration.migration.AddHistoryTable;
|
||||
import com.avaje.ebean.dbmigration.migration.AlterColumn;
|
||||
import com.avaje.ebean.dbmigration.migration.DropColumn;
|
||||
import com.avaje.ebean.dbmigration.migration.DropHistoryTable;
|
||||
import com.avaje.ebean.dbmigration.migration.DropTable;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
@@ -51,7 +54,26 @@ public class MTableTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompare_addColumnDropColumn() throws Exception {
|
||||
public void test_allHistoryColumns() throws Exception {
|
||||
|
||||
MTable base = base();
|
||||
base.registerDroppedColumn("fullName",2);
|
||||
base.registerDroppedColumn("last",4);
|
||||
|
||||
assertThat(base.allHistoryColumns(false)).containsExactly("id","name","status");
|
||||
assertThat(base.allHistoryColumns(true)).containsExactly("id","name","fullName","status","last");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_dropTable() {
|
||||
|
||||
MTable base = base();
|
||||
DropTable dropTable = base.dropTable();
|
||||
assertThat(dropTable.getName()).isEqualTo(base.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_compare_addColumnDropColumn() throws Exception {
|
||||
|
||||
ModelDiff diff = new ModelDiff();
|
||||
diff.compareTables(base(), newTable());
|
||||
@@ -71,7 +93,7 @@ public class MTableTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompare_addTwoColumnsToSameTable() throws Exception {
|
||||
public void test_compare_addTwoColumnsToSameTable() throws Exception {
|
||||
|
||||
ModelDiff diff = new ModelDiff();
|
||||
diff.compareTables(base(), newTableAdd2Columns());
|
||||
@@ -88,7 +110,7 @@ public class MTableTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompare_modifyColumn() throws Exception {
|
||||
public void test_compare_modifyColumn() throws Exception {
|
||||
|
||||
ModelDiff diff = new ModelDiff();
|
||||
diff.compareTables(base(), newTableModifiedColumn());
|
||||
@@ -107,4 +129,99 @@ public class MTableTest {
|
||||
assertThat(diff.getDropChanges()).hasSize(0);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_apply_dropColumn() {
|
||||
|
||||
MTable base = base();
|
||||
|
||||
DropColumn dropColumn = new DropColumn();
|
||||
dropColumn.setTableName("tab");
|
||||
dropColumn.setColumnName("name");
|
||||
|
||||
base.apply(dropColumn);
|
||||
assertThat(base.getColumn("name")).isNull();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void test_apply_dropColumn_doesNotExist() {
|
||||
|
||||
MTable base = base();
|
||||
|
||||
DropColumn dropColumn = new DropColumn();
|
||||
dropColumn.setTableName(base.getName());
|
||||
dropColumn.setColumnName("DoesNotExist");
|
||||
base.apply(dropColumn);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void test_apply_alterColumn_doesNotExist() {
|
||||
|
||||
MTable base = base();
|
||||
|
||||
AlterColumn alterColumn = new AlterColumn();
|
||||
alterColumn.setTableName(base.getName());
|
||||
alterColumn.setColumnName("DoesNotExist");
|
||||
alterColumn.setType("integer");
|
||||
base.apply(alterColumn);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_apply_alterColumn_type() {
|
||||
|
||||
MTable base = base();
|
||||
|
||||
AlterColumn alterColumn = new AlterColumn();
|
||||
alterColumn.setTableName(base.getName());
|
||||
alterColumn.setColumnName("id");
|
||||
alterColumn.setType("uuid");
|
||||
base.apply(alterColumn);
|
||||
|
||||
assertThat(base.getColumn("id").getType()).isEqualTo("uuid");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_compare_addAndDropColumn() throws Exception {
|
||||
|
||||
MTable base = base();
|
||||
MTable newTable = newTable();
|
||||
|
||||
ModelDiff diff = new ModelDiff();
|
||||
base.compare(diff, newTable);
|
||||
|
||||
assertThat(diff.getApplyChanges()).hasSize(1);
|
||||
assertThat(diff.getDropChanges()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_compare_addHistoryToTable() {
|
||||
|
||||
MTable base = base();
|
||||
MTable withHistory = base();
|
||||
withHistory.setWithHistory(true);
|
||||
|
||||
ModelDiff diff = new ModelDiff();
|
||||
base.compare(diff, withHistory);
|
||||
|
||||
assertThat(diff.getDropChanges()).isEmpty();
|
||||
assertThat(diff.getApplyChanges()).hasSize(1);
|
||||
assertThat(diff.getApplyChanges().get(0)).isInstanceOf(AddHistoryTable.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_compare_removeHistoryFromTable() throws Exception {
|
||||
|
||||
MTable withHistory = base();
|
||||
withHistory.setWithHistory(true);
|
||||
|
||||
MTable noHistory = base();
|
||||
|
||||
ModelDiff diff = new ModelDiff();
|
||||
withHistory.compare(diff, noHistory);
|
||||
|
||||
assertThat(diff.getApplyChanges()).isEmpty();
|
||||
assertThat(diff.getDropChanges()).hasSize(1);
|
||||
assertThat(diff.getDropChanges().get(0)).isInstanceOf(DropHistoryTable.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,6 +36,6 @@ public class ModelContainerApplyTest {
|
||||
assertThat(foo.getTablespace()).isEqualTo("fooSpace");
|
||||
assertThat(foo.getIndexTablespace()).isEqualTo("fooIndexSpace");
|
||||
assertThat(foo.isWithHistory()).isEqualTo(false);
|
||||
assertThat(foo.getColumns()).containsKeys("col1", "col3", "added_to_foo");
|
||||
assertThat(foo.allColumns()).extracting("name").contains("col1", "col3", "added_to_foo");
|
||||
}
|
||||
}
|
||||
@@ -37,8 +37,6 @@ public class ModelBuildBeanVisitorTest extends BaseTestCase {
|
||||
assertThat(item.primaryKeyColumns()).hasSize(2);
|
||||
|
||||
MTable customer = model.getTable("o_customer");
|
||||
|
||||
assertThat(customer).isNotNull();
|
||||
assertThat(customer.getSequenceName()).isNull();
|
||||
}
|
||||
}
|
||||
@@ -61,11 +61,8 @@ public class TestBasicPush extends BaseTestCase {
|
||||
|
||||
generator.close();
|
||||
String json = writer.toString();
|
||||
System.out.println(json);
|
||||
|
||||
String response = post("http://localhost:9200/_bulk", json);
|
||||
|
||||
System.out.println(response);
|
||||
post("http://localhost:9200/_bulk", json);
|
||||
|
||||
//curl -s -XPOST localhost:9200/_bulk
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ public class ChangeJsonBuilderTest extends BaseTestCase {
|
||||
for (int i = 0; i < changes.size(); i++) {
|
||||
StringWriter buffer = new StringWriter();
|
||||
builder.writeBeanJson(buffer, changes.get(i), changeSet, i);
|
||||
System.out.println(buffer.toString());
|
||||
|
||||
assertThat(buffer.toString()).contains("\"source\"");
|
||||
assertThat(buffer.toString()).contains("\"userId\"");
|
||||
assertThat(buffer.toString()).contains("\"userIpAddress\"");
|
||||
|
||||
@@ -19,8 +19,6 @@ public class DeployPropertyParserMapTests {
|
||||
DeployPropertyParserMap parser = new DeployPropertyParserMap(map);
|
||||
|
||||
String output = parser.parse("(lower(customer.name) like ? escape'' or id > ?)");
|
||||
|
||||
System.out.println(output);
|
||||
Assert.assertEquals("(lower(t1.name) like ? escape'' or t0.id > ?)", output);
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -8,7 +8,7 @@ import java.sql.Timestamp;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class InsertTimestampFactoryTest {
|
||||
|
||||
@@ -20,7 +20,7 @@ public class InsertTimestampFactoryTest {
|
||||
DeployBeanProperty prop = new DeployBeanProperty(null, LocalDateTime.class, null, null);
|
||||
|
||||
GeneratedProperty insertTimestamp = factory.createInsertTimestamp(prop);
|
||||
Object value = insertTimestamp.getInsertValue(null, null);
|
||||
Object value = insertTimestamp.getInsertValue(null, null, System.currentTimeMillis());
|
||||
assertTrue(value instanceof LocalDateTime);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ public class InsertTimestampFactoryTest {
|
||||
DeployBeanProperty prop = new DeployBeanProperty(null, OffsetDateTime.class, null, null);
|
||||
|
||||
GeneratedProperty insertTimestamp = factory.createInsertTimestamp(prop);
|
||||
Object value = insertTimestamp.getInsertValue(null, null);
|
||||
Object value = insertTimestamp.getInsertValue(null, null, System.currentTimeMillis());
|
||||
assertTrue(value instanceof OffsetDateTime);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public class InsertTimestampFactoryTest {
|
||||
DeployBeanProperty prop = new DeployBeanProperty(null, Timestamp.class, null, null);
|
||||
|
||||
GeneratedProperty insertTimestamp = factory.createInsertTimestamp(prop);
|
||||
Object value = insertTimestamp.getInsertValue(null, null);
|
||||
Object value = insertTimestamp.getInsertValue(null, null, System.currentTimeMillis());
|
||||
assertTrue(value instanceof Timestamp);
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ public class InsertTimestampFactoryTest {
|
||||
DeployBeanProperty prop = new DeployBeanProperty(null, java.util.Date.class, null, null);
|
||||
|
||||
GeneratedProperty insertTimestamp = factory.createInsertTimestamp(prop);
|
||||
Object value = insertTimestamp.getInsertValue(null, null);
|
||||
Object value = insertTimestamp.getInsertValue(null, null, System.currentTimeMillis());
|
||||
assertTrue(value instanceof java.util.Date);
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -21,7 +21,7 @@ public class UpdateTimestampFactoryTest {
|
||||
DeployBeanProperty prop = new DeployBeanProperty(null, LocalDateTime.class, null, null);
|
||||
|
||||
GeneratedProperty generatedProperty = factory.createUpdateTimestamp(prop);
|
||||
Object value = generatedProperty.getInsertValue(null, null);
|
||||
Object value = generatedProperty.getInsertValue(null, null, System.currentTimeMillis());
|
||||
assertTrue(value instanceof LocalDateTime);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ public class UpdateTimestampFactoryTest {
|
||||
DeployBeanProperty prop = new DeployBeanProperty(null, OffsetDateTime.class, null, null);
|
||||
|
||||
GeneratedProperty generatedProperty = factory.createUpdateTimestamp(prop);
|
||||
Object value = generatedProperty.getInsertValue(null, null);
|
||||
Object value = generatedProperty.getInsertValue(null, null, System.currentTimeMillis());
|
||||
assertTrue(value instanceof OffsetDateTime);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ public class UpdateTimestampFactoryTest {
|
||||
DeployBeanProperty prop = new DeployBeanProperty(null, Timestamp.class, null, null);
|
||||
|
||||
GeneratedProperty generatedProperty = factory.createUpdateTimestamp(prop);
|
||||
Object value = generatedProperty.getInsertValue(null, null);
|
||||
Object value = generatedProperty.getInsertValue(null, null, System.currentTimeMillis());
|
||||
assertTrue(value instanceof Timestamp);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public class UpdateTimestampFactoryTest {
|
||||
DeployBeanProperty prop = new DeployBeanProperty(null, java.util.Date.class, null, null);
|
||||
|
||||
GeneratedProperty generatedProperty = factory.createUpdateTimestamp(prop);
|
||||
Object value = generatedProperty.getInsertValue(null, null);
|
||||
Object value = generatedProperty.getInsertValue(null, null, System.currentTimeMillis());
|
||||
assertTrue(value instanceof java.util.Date);
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.LikeType;
|
||||
import com.avaje.ebean.Query;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.tests.model.basic.Address;
|
||||
import com.avaje.tests.model.basic.Customer;
|
||||
import com.avaje.tests.model.basic.ResetBasicData;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
||||
public class DefaultExampleExpressionTest extends BaseTestCase {
|
||||
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
SpiEbeanServer server = (SpiEbeanServer)Ebean.getDefaultServer();
|
||||
BeanDescriptor<Customer> desc = server.getBeanDescriptor(Customer.class);
|
||||
|
||||
SpiQuery<Customer> query = (SpiQuery<Customer>)server.find(Customer.class);
|
||||
|
||||
Address address = new Address();
|
||||
address.setCity("billingAddress.city");
|
||||
|
||||
Customer customer = new Customer();
|
||||
customer.setName("name");
|
||||
customer.setBillingAddress(address);
|
||||
|
||||
|
||||
DefaultExampleExpression expr = new DefaultExampleExpression((EntityBean)customer, false, LikeType.EQUAL_TO);
|
||||
|
||||
|
||||
BeanQueryRequest<?> request = create(query, desc);
|
||||
HashQueryPlanBuilder builder = new HashQueryPlanBuilder();
|
||||
expr.queryPlanHash(request, builder);
|
||||
|
||||
TDSpiExpressionRequest req = new TDSpiExpressionRequest(desc);
|
||||
expr.addBindValues(req);
|
||||
|
||||
assertThat(req.bindValues).contains("name", "billingAddress.city");
|
||||
|
||||
address.setCity("Auckland");
|
||||
customer.setName("Rob");
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Customer> query1 = server.find(Customer.class)
|
||||
.where().exampleLike(customer)
|
||||
.query();
|
||||
|
||||
query1.findList();
|
||||
|
||||
assertThat(query1.getGeneratedSql()).contains("(t0.name like ? ");
|
||||
assertThat(query1.getGeneratedSql()).contains(" and t1.city like ? ");
|
||||
|
||||
}
|
||||
|
||||
private <T> OrmQueryRequest<T> create(SpiQuery<T> query, BeanDescriptor<T> desc) {
|
||||
return new OrmQueryRequest<T>(null, null, query, desc, null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
import com.avaje.ebeaninternal.server.core.JsonExpressionHandler;
|
||||
import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Test double for testing with SpiExpressionRequest.
|
||||
*/
|
||||
public class TDSpiExpressionRequest implements SpiExpressionRequest {
|
||||
|
||||
List<Object> bindValues = new ArrayList<Object>();
|
||||
|
||||
final BeanDescriptor<?> descriptor;
|
||||
|
||||
public TDSpiExpressionRequest(BeanDescriptor<?> descriptor) {
|
||||
this.descriptor = descriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonExpressionHandler getJsonHandler() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parseDeploy(String logicalProp) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanDescriptor<?> getBeanDescriptor() {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiOrmQueryRequest<?> getQueryRequest() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiExpressionRequest append(String sql) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindEncryptKey(Object encryptKey) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValue(Object bindValue) {
|
||||
bindValues.add(bindValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSql() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<Object> getBindValues() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextParameter() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendLike() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -49,8 +49,6 @@ public class TestDataSourceMax extends BaseTestCase {
|
||||
bg.execute(new ConnRunner(pool, 4000, i));
|
||||
}
|
||||
|
||||
System.out.println("main thread sleep ... " + pool.getStatus(false));
|
||||
|
||||
Thread.sleep(10000);
|
||||
pool.getStatistics(true);
|
||||
|
||||
@@ -81,9 +79,7 @@ public class TestDataSourceMax extends BaseTestCase {
|
||||
|
||||
private void waitSomeTime(long count) {
|
||||
try {
|
||||
System.out.println(position+" sleep " + sleepMillis+" count:"+count);
|
||||
Thread.sleep(sleepMillis);
|
||||
System.out.println(position+" sleep done");
|
||||
} catch (InterruptedException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
@@ -30,9 +30,6 @@ public class TestDataSourceMaxWithEntity extends BaseTestCase {
|
||||
bg.execute(new ConnRunner(server, 4000, i));
|
||||
}
|
||||
|
||||
System.out.println("main thread sleep ... ");
|
||||
|
||||
|
||||
Thread.sleep(30000);
|
||||
|
||||
server.shutdown(true, false);
|
||||
|
||||
@@ -24,7 +24,6 @@ public class TestRawSqlParsing extends TestCase {
|
||||
Sql rs = rawSql.getSql();
|
||||
|
||||
String s = rs.toString();
|
||||
System.out.println(s);
|
||||
assertTrue(s, s.contains("[order_id, sum"));
|
||||
|
||||
}
|
||||
|
||||
@@ -42,7 +42,6 @@ public class TestJsonWriteVisitor extends BaseTestCase {
|
||||
options.setPathVisitor("contacts", new ContactVisitor());
|
||||
|
||||
String jsonContent = json.toJson(list, options);
|
||||
System.out.println(jsonContent);
|
||||
|
||||
assertThat(jsonContent).contains("customerExtra");
|
||||
assertThat(jsonContent).contains("contactExtra");
|
||||
|
||||
+4
-7
@@ -42,14 +42,11 @@ public class TestTextJsonBeanReadVisitor extends BaseTestCase {
|
||||
options.addVisitor("shippingAddress", new ASVisitor());
|
||||
|
||||
String s = json.toJson(list);
|
||||
System.out.println(s);
|
||||
|
||||
List<Customer> mList = json.toList(Customer.class, s, options);
|
||||
System.out.println("VIA STRING: " + mList);
|
||||
|
||||
StringReader reader = new StringReader(s);
|
||||
List<Customer> mList2 = json.toList(Customer.class, reader);
|
||||
System.out.println("VIA READER: " + mList2);
|
||||
|
||||
assertEquals(mList.size(), mList2.size());
|
||||
}
|
||||
@@ -58,28 +55,28 @@ public class TestTextJsonBeanReadVisitor extends BaseTestCase {
|
||||
|
||||
@Override
|
||||
public void visit(Customer bean, Map<String, Object> unmapped) {
|
||||
System.out.println("visit customer: " + bean);
|
||||
bean.getId();
|
||||
}
|
||||
}
|
||||
|
||||
private static class AVisitor implements JsonReadBeanVisitor<Address> {
|
||||
|
||||
public void visit(Address bean, Map<String, Object> unmapped) {
|
||||
System.out.println("visit billing address: " + bean);
|
||||
bean.getId();
|
||||
}
|
||||
}
|
||||
|
||||
private static class ASVisitor implements JsonReadBeanVisitor<Address> {
|
||||
|
||||
public void visit(Address bean, Map<String, Object> unmapped) {
|
||||
System.out.println("visit shipping address: " + bean);
|
||||
bean.getId();
|
||||
}
|
||||
}
|
||||
|
||||
private static class ContactVisitor implements JsonReadBeanVisitor<Contact> {
|
||||
|
||||
public void visit(Contact bean, Map<String, Object> unmapped) {
|
||||
System.out.println("visit contact: " + bean);
|
||||
bean.getId();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,6 @@ public class ScalarTypeLocalDateTest {
|
||||
LocalDate date = LocalDate.of(2014, 5, 20);
|
||||
long millis = type.convertToMillis(date);
|
||||
|
||||
System.out.println(date);
|
||||
|
||||
LocalDate parseDate = type.convertFromMillis(millis);
|
||||
assertEquals(date, parseDate);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user