mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#671 - ENH: Add support for @View and extra-ddl.xml to nicely support entity beans based on views
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
package com.avaje.ebean.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotate an entity bean with @View to indicates the bean is based on a view.
|
||||
* <p>
|
||||
* As such typically the view is defined in <code>extra-ddl.xml</code> using
|
||||
* <code>create or replace view ...</code>.
|
||||
* </p>
|
||||
* <p>
|
||||
* When using extra-ddl.xml Ebean will run the resulting DDL script after the
|
||||
* <code>create-all</code> DDL (which is typically used during development) and for
|
||||
* DB Migration will copy the scripts as <code>repeatable migration scripts</code> that
|
||||
* will be run by FlywayDb (or Ebean's own migration runner) when their MD5 hash changes.
|
||||
* </p>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface View {
|
||||
|
||||
/**
|
||||
* The name of the view this entity bean is based on.
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* Tables this view is dependent on.
|
||||
* <p>
|
||||
* This is used with l2 caching to invalidate the query cache. Changes to these
|
||||
* tables invalidate the query cache for the entity based on this view.
|
||||
* </p>
|
||||
*/
|
||||
String[] dependentTables() default {};
|
||||
}
|
||||
@@ -25,10 +25,14 @@ import com.avaje.ebean.dbmigration.model.ModelContainer;
|
||||
import com.avaje.ebean.dbmigration.model.ModelDiff;
|
||||
import com.avaje.ebean.dbmigration.model.PlatformDdlWriter;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.extraddl.model.DdlScript;
|
||||
import com.avaje.ebeaninternal.extraddl.model.ExtraDdl;
|
||||
import com.avaje.ebeaninternal.extraddl.model.ExtraDdlXmlReader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -42,7 +46,7 @@ import java.util.List;
|
||||
* and drop objects (drop tables, drop columns).
|
||||
* </p>
|
||||
* <p>
|
||||
* This does not run the migration or ddl scripts but just generates them.
|
||||
* This does not run the migration or ddl scripts but just generates them.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
@@ -98,7 +102,7 @@ public class DbMigration {
|
||||
|
||||
/**
|
||||
* Set the path from the current working directory to the application resources.
|
||||
*
|
||||
* <p>
|
||||
* This defaults to maven style 'src/main/resources'.
|
||||
*/
|
||||
public void setPathToResources(String pathToResources) {
|
||||
@@ -169,7 +173,7 @@ public class DbMigration {
|
||||
/**
|
||||
* Generate the next migration xml file and associated apply and rollback sql scripts.
|
||||
* <p>
|
||||
* This does not run the migration or ddl scripts but just generates them.
|
||||
* This does not run the migration or ddl scripts but just generates them.
|
||||
* </p>
|
||||
* <h3>Example: Run for a single specific platform</h3>
|
||||
* <pre>{@code
|
||||
@@ -181,7 +185,7 @@ public class DbMigration {
|
||||
* migration.generateMigration();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* <h3>Example: Run migration generating DDL for multiple platforms</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
@@ -206,6 +210,8 @@ public class DbMigration {
|
||||
try {
|
||||
Request request = createRequest();
|
||||
|
||||
generateExtraDdl(request);
|
||||
|
||||
String pendingVersion = generatePendingDrop();
|
||||
if (pendingVersion != null) {
|
||||
generatePendingDrop(request, pendingVersion);
|
||||
@@ -220,6 +226,49 @@ public class DbMigration {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate "repeatable" migration scripts.
|
||||
* <p>
|
||||
* These take scrips from extra-dll.xml (typically views) and outputs "repeatable"
|
||||
* migration scripts (starting with "R__") to be run by FlywayDb or Ebean's own
|
||||
* migration runner.
|
||||
* </p>
|
||||
*/
|
||||
private void generateExtraDdl(Request request) throws IOException {
|
||||
|
||||
if (databasePlatform != null) {
|
||||
ExtraDdl extraDdl = ExtraDdlXmlReader.read("/extra-ddl.xml");
|
||||
if (extraDdl != null) {
|
||||
List<DdlScript> ddlScript = extraDdl.getDdlScript();
|
||||
for (DdlScript script : ddlScript) {
|
||||
if (ExtraDdlXmlReader.matchPlatform(databasePlatform.getName(), script.getPlatforms())) {
|
||||
writeExtraDdl(request, script);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write (or override) the "repeatable" migration script.
|
||||
*/
|
||||
private void writeExtraDdl(Request request, DdlScript script) throws IOException {
|
||||
|
||||
String fullName = repeatableMigrationName(script.getName());
|
||||
|
||||
logger.info("writing repeatable script {}", fullName);
|
||||
|
||||
File file = new File(request.migrationDir, fullName);
|
||||
FileWriter writer = new FileWriter(file);
|
||||
writer.write(script.getValue());
|
||||
writer.flush();
|
||||
writer.close();
|
||||
}
|
||||
|
||||
private String repeatableMigrationName(String scriptName) {
|
||||
return "R__" + scriptName.replace(' ', '_') + migrationConfig.getApplySuffix();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the diff migration.
|
||||
*/
|
||||
@@ -319,7 +368,7 @@ public class DbMigration {
|
||||
// history ddl generation (triggers, history tables etc)
|
||||
DdlWrite write = new DdlWrite(new MConfiguration(), request.current);
|
||||
PlatformDdlWriter writer = createDdlWriter(databasePlatform, "");
|
||||
writer.processMigration(dbMigration, write, request.migrationDir , fullVersion);
|
||||
writer.processMigration(dbMigration, write, request.migrationDir, fullVersion);
|
||||
}
|
||||
writeExtraPlatformDdl(fullVersion, request.currentModel, dbMigration, request.migrationDir);
|
||||
}
|
||||
@@ -339,7 +388,7 @@ public class DbMigration {
|
||||
|
||||
/**
|
||||
* Return the full version for the migration being generated.
|
||||
*
|
||||
* <p>
|
||||
* The full version can contain a comment suffix after a "__" double underscore.
|
||||
*/
|
||||
private String getFullVersion(MigrationModel migrationModel, String dropsFor) {
|
||||
@@ -366,7 +415,7 @@ public class DbMigration {
|
||||
* Replace spaces with underscores.
|
||||
*/
|
||||
private String toUnderScore(String name) {
|
||||
return name.replace(' ','_');
|
||||
return name.replace(' ', '_');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -444,7 +493,7 @@ public class DbMigration {
|
||||
*/
|
||||
protected File getModelDirectory(File migrationDirectory) {
|
||||
String modelPath = migrationConfig.getModelPath();
|
||||
if (modelPath == null || modelPath.isEmpty()) {
|
||||
if (modelPath == null || modelPath.isEmpty()) {
|
||||
return migrationDirectory;
|
||||
}
|
||||
File modelDir = new File(migrationDirectory, migrationConfig.getModelPath());
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package com.avaje.ebean.dbmigration;
|
||||
|
||||
import com.avaje.ebean.Transaction;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.dbmigration.model.CurrentModel;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.util.JdbcClose;
|
||||
import com.avaje.ebeaninternal.extraddl.model.ExtraDdlXmlReader;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.File;
|
||||
@@ -15,6 +16,7 @@ import java.io.InputStreamReader;
|
||||
import java.io.LineNumberReader;
|
||||
import java.io.Reader;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Controls the generation and execution of "Create All" and "Drop All" DDL scripts.
|
||||
@@ -89,12 +91,19 @@ public class DdlGenerator {
|
||||
public int runScript(boolean expectErrors, String content, String scriptName) {
|
||||
|
||||
DdlRunner runner = new DdlRunner(expectErrors, scriptName);
|
||||
// get a connection without threadLocal
|
||||
Connection connection = server.createTransaction().getConnection();
|
||||
|
||||
Transaction transaction = server.createTransaction();
|
||||
Connection connection = transaction.getConnection();
|
||||
try {
|
||||
return runner.runAll(content, connection);
|
||||
int count = runner.runAll(content, connection);
|
||||
transaction.commit();
|
||||
return count;
|
||||
|
||||
} catch (SQLException e) {
|
||||
throw new PersistenceException("Failed to run script", e);
|
||||
|
||||
} finally {
|
||||
JdbcClose.close(connection);
|
||||
transaction.end();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,6 +121,14 @@ public class DdlGenerator {
|
||||
createAllContent = readFile(getCreateFileName());
|
||||
}
|
||||
runScript(false, createAllContent, getCreateFileName());
|
||||
|
||||
String ignoreExtraDdl = System.getProperty("ebean.ignoreExtraDdl");
|
||||
if (!"true".equalsIgnoreCase(ignoreExtraDdl)) {
|
||||
String extraApply = ExtraDdlXmlReader.buildExtra(server.getDatabasePlatform().getName());
|
||||
if (extraApply != null) {
|
||||
runScript(false, extraApply, "extra-dll");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void runInitSql() throws IOException {
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.avaje.ebean.dbmigration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.StringReader;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
@@ -18,11 +17,11 @@ public class DdlRunner {
|
||||
|
||||
protected static final Logger logger = LoggerFactory.getLogger(DdlRunner.class);
|
||||
|
||||
protected DdlParser ddlParser = new DdlParser();
|
||||
private DdlParser ddlParser = new DdlParser();
|
||||
|
||||
protected final String scriptName;
|
||||
private final String scriptName;
|
||||
|
||||
protected final boolean expectErrors;
|
||||
private final boolean expectErrors;
|
||||
|
||||
/**
|
||||
* Construct with a script name (for logging) and flag indicating if errors are expected.
|
||||
@@ -35,7 +34,7 @@ public class DdlRunner {
|
||||
/**
|
||||
* Parse the content into sql statements and execute them in a transaction.
|
||||
*/
|
||||
public int runAll(String content, Connection connection) {
|
||||
public int runAll(String content, Connection connection) throws SQLException {
|
||||
|
||||
List<String> statements = ddlParser.parse(new StringReader(content));
|
||||
return runStatements(statements, connection);
|
||||
@@ -44,31 +43,7 @@ public class DdlRunner {
|
||||
/**
|
||||
* Execute all the statements in a single transaction.
|
||||
*/
|
||||
public int runStatements(List<String> statements, Connection connection) {
|
||||
|
||||
try {
|
||||
int statementCount = runStatements(expectErrors, statements, connection);
|
||||
connection.commit();
|
||||
return statementCount;
|
||||
|
||||
} catch (Exception e) {
|
||||
rollback(connection);
|
||||
throw new PersistenceException("Error: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void rollback(Connection connection) {
|
||||
try {
|
||||
connection.rollback();
|
||||
} catch (SQLException e) {
|
||||
logger.error("Error trying to rollback connection", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the list of statements.
|
||||
*/
|
||||
private int runStatements(boolean expectErrors, List<String> statements, Connection c) {
|
||||
private int runStatements(List<String> statements, Connection connection) throws SQLException {
|
||||
|
||||
List<String> noDuplicates = new ArrayList<String>();
|
||||
|
||||
@@ -82,7 +57,7 @@ public class DdlRunner {
|
||||
|
||||
for (int i = 0; i < noDuplicates.size(); i++) {
|
||||
String xOfy = (i + 1) + " of " + noDuplicates.size();
|
||||
runStatement(expectErrors, xOfy, noDuplicates.get(i), c);
|
||||
runStatement(expectErrors, xOfy, noDuplicates.get(i), connection);
|
||||
}
|
||||
|
||||
return noDuplicates.size();
|
||||
@@ -91,7 +66,7 @@ public class DdlRunner {
|
||||
/**
|
||||
* Execute the statement.
|
||||
*/
|
||||
private void runStatement(boolean expectErrors, String oneOf, String stmt, Connection c) {
|
||||
private void runStatement(boolean expectErrors, String oneOf, String stmt, Connection c) throws SQLException {
|
||||
|
||||
PreparedStatement pstmt = null;
|
||||
try {
|
||||
@@ -111,12 +86,12 @@ public class DdlRunner {
|
||||
pstmt = c.prepareStatement(stmt);
|
||||
pstmt.execute();
|
||||
|
||||
} catch (Exception e) {
|
||||
} catch (SQLException e) {
|
||||
if (expectErrors) {
|
||||
logger.debug(" ... ignoring error executing " + getSummary(stmt) + " error: " + e.getMessage());
|
||||
} else {
|
||||
String msg = "Error executing stmt[" + stmt + "] error[" + e.getMessage() + "]";
|
||||
throw new RuntimeException(msg, e);
|
||||
throw new SQLException(msg, e);
|
||||
}
|
||||
|
||||
} finally {
|
||||
|
||||
@@ -7,6 +7,10 @@ import java.util.Arrays;
|
||||
*/
|
||||
public class MigrationVersion implements Comparable<MigrationVersion> {
|
||||
|
||||
private static final int[] REPEAT_ORDERING = {Integer.MAX_VALUE};
|
||||
|
||||
private static final boolean[] REPEAT_UNDERSCORES = {false};
|
||||
|
||||
/**
|
||||
* The raw version text.
|
||||
*/
|
||||
@@ -21,6 +25,19 @@ public class MigrationVersion implements Comparable<MigrationVersion> {
|
||||
|
||||
private final String comment;
|
||||
|
||||
/**
|
||||
* Construct for "repeatable" version.
|
||||
*/
|
||||
private MigrationVersion(String raw, String comment) {
|
||||
this.raw = raw;
|
||||
this.comment = comment;
|
||||
this.ordering = REPEAT_ORDERING;
|
||||
this.underscores = REPEAT_UNDERSCORES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for "normal" version.
|
||||
*/
|
||||
private MigrationVersion(String raw, int[] ordering, boolean[] underscores, String comment) {
|
||||
this.raw = raw;
|
||||
this.ordering = ordering;
|
||||
@@ -28,6 +45,13 @@ public class MigrationVersion implements Comparable<MigrationVersion> {
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is a "repeatable" version.
|
||||
*/
|
||||
public boolean isRepeatable() {
|
||||
return ordering == REPEAT_ORDERING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the full version.
|
||||
*/
|
||||
@@ -76,11 +100,15 @@ public class MigrationVersion implements Comparable<MigrationVersion> {
|
||||
|
||||
/**
|
||||
* Returns the version part of the string.
|
||||
*
|
||||
* <p>
|
||||
* Normalised means always use '.' delimiters (no underscores).
|
||||
* NextVersion means bump/increase the last version number by 1.
|
||||
*/
|
||||
private String formattedVersion(boolean normalised, boolean nextVersion) {
|
||||
|
||||
if (ordering == REPEAT_ORDERING) {
|
||||
return "R";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < ordering.length; i++) {
|
||||
if (i < ordering.length - 1) {
|
||||
@@ -110,8 +138,7 @@ public class MigrationVersion implements Comparable<MigrationVersion> {
|
||||
return (ordering[i] > other.ordering[i]) ? 1 : -1;
|
||||
}
|
||||
}
|
||||
// considered the same
|
||||
return 0;
|
||||
return comment.compareTo(other.comment);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,6 +153,10 @@ public class MigrationVersion implements Comparable<MigrationVersion> {
|
||||
*/
|
||||
public static MigrationVersion parse(String raw) {
|
||||
|
||||
if (raw.startsWith("V") || raw.startsWith("v")) {
|
||||
raw = raw.substring(1);
|
||||
}
|
||||
|
||||
String comment = "";
|
||||
String value = raw;
|
||||
int commentStart = raw.indexOf("__");
|
||||
@@ -139,6 +170,11 @@ public class MigrationVersion implements Comparable<MigrationVersion> {
|
||||
|
||||
String[] sections = value.split("\\.");
|
||||
|
||||
if ("r".equalsIgnoreCase(sections[0])) {
|
||||
// a "repeatable" version (does not have a version number)
|
||||
return new MigrationVersion(raw, comment);
|
||||
}
|
||||
|
||||
boolean[] underscores = new boolean[sections.length];
|
||||
int[] ordering = new int[sections.length];
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ public class VisitAllUsing {
|
||||
|
||||
public void visitAllBeans() {
|
||||
for (BeanDescriptor<?> desc : descriptors) {
|
||||
if (desc.getBaseTable() != null) {
|
||||
if (desc.isBaseTable()) {
|
||||
visitBean(desc, visitor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,16 @@ import org.avaje.classpath.scanner.Resource;
|
||||
*/
|
||||
public class LocalMigrationResource implements Comparable<LocalMigrationResource> {
|
||||
|
||||
/**
|
||||
* Code for repeatable migrations.
|
||||
*/
|
||||
private static final String REPEAT_TYPE = "R";
|
||||
|
||||
/**
|
||||
* Code for version migrations.
|
||||
*/
|
||||
private static final String VERSION_TYPE = "V";
|
||||
|
||||
private final MigrationVersion version;
|
||||
|
||||
private final String location;
|
||||
@@ -27,6 +37,32 @@ public class LocalMigrationResource implements Comparable<LocalMigrationResource
|
||||
return version.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the underlying version is "repeatable".
|
||||
*/
|
||||
public boolean isRepeatable() {
|
||||
return version.isRepeatable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the "key" that identifies the migration.
|
||||
*/
|
||||
public String key() {
|
||||
if (isRepeatable()) {
|
||||
return version.getComment().toLowerCase();
|
||||
} else {
|
||||
return version.normalised();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the migration comment.
|
||||
*/
|
||||
public String getComment() {
|
||||
String comment = version.getComment();
|
||||
return (comment == null || comment.isEmpty()) ? "-" : comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default ordering by version.
|
||||
*/
|
||||
@@ -55,4 +91,11 @@ public class LocalMigrationResource implements Comparable<LocalMigrationResource
|
||||
public String getContent() {
|
||||
return resource.loadAsString("UTF-8");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type code ("R" or "V") for this migration.
|
||||
*/
|
||||
public String getType() {
|
||||
return isRepeatable() ? REPEAT_TYPE : VERSION_TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,9 @@ class MigrationMetaRow {
|
||||
|
||||
private int id;
|
||||
|
||||
private String status;
|
||||
private String type;
|
||||
|
||||
private String runVersion;
|
||||
|
||||
private String depVersion;
|
||||
private String version;
|
||||
|
||||
private String comment;
|
||||
|
||||
@@ -29,14 +27,14 @@ class MigrationMetaRow {
|
||||
/**
|
||||
* Construct for inserting into table.
|
||||
*/
|
||||
MigrationMetaRow(int id, String runVersion, String priorVersion, String comment, int checksum, String runBy) {
|
||||
MigrationMetaRow(int id, String type, String version, String comment, int checksum, String runBy, Timestamp runOn) {
|
||||
this.id = id;
|
||||
this.runVersion = runVersion;
|
||||
this.depVersion = priorVersion;
|
||||
this.type = type;
|
||||
this.version = version;
|
||||
this.checksum = checksum;
|
||||
this.comment = comment;
|
||||
this.runBy = runBy;
|
||||
this.runOn = new Timestamp(System.currentTimeMillis());
|
||||
this.runOn = runOn;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,17 +42,16 @@ class MigrationMetaRow {
|
||||
*/
|
||||
MigrationMetaRow(SqlRow row) {
|
||||
id = row.getInteger("id");
|
||||
status = row.getString("status");
|
||||
runVersion = row.getString("run_version");
|
||||
depVersion = row.getString("dep_version");
|
||||
comment = row.getString("comment");
|
||||
checksum = row.getInteger("checksum");
|
||||
type = row.getString("mtype");
|
||||
version = row.getString("mversion");
|
||||
comment = row.getString("mcomment");
|
||||
checksum = row.getInteger("mchecksum");
|
||||
runOn = row.getTimestamp("run_on");
|
||||
runBy = row.getString("run_by");
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "id:" + id + " status:" + status + " runVersion:" + runVersion + " comment:" + comment + " runOn:" + runOn + " runBy:" + runBy;
|
||||
return "id:" + id + " type:" + type + " runVersion:" + version + " comment:" + comment + " runOn:" + runOn + " runBy:" + runBy;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,8 +64,8 @@ class MigrationMetaRow {
|
||||
/**
|
||||
* Return the normalised version for this migration.
|
||||
*/
|
||||
String getRunVersion() {
|
||||
return runVersion;
|
||||
String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,9 +80,9 @@ class MigrationMetaRow {
|
||||
*/
|
||||
void bindInsert(SqlUpdate insert) {
|
||||
insert.setParameter(1, id);
|
||||
insert.setParameter(2, "success");
|
||||
insert.setParameter(3, runVersion);
|
||||
insert.setParameter(4, depVersion);
|
||||
insert.setParameter(2, type);
|
||||
insert.setParameter(3, "SUCCESS");
|
||||
insert.setParameter(4, version);
|
||||
insert.setParameter(5, comment);
|
||||
insert.setParameter(6, checksum);
|
||||
insert.setParameter(7, runOn);
|
||||
@@ -93,12 +90,35 @@ class MigrationMetaRow {
|
||||
insert.setParameter(9, "ip");
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind to an update statement.
|
||||
*/
|
||||
public void bindUpdate(int checksum, String runBy, Timestamp runOn, SqlUpdate update) {
|
||||
|
||||
this.checksum = checksum;
|
||||
this.runOn = runOn;
|
||||
this.runBy = runBy;
|
||||
|
||||
update.setParameter(1, checksum);
|
||||
update.setParameter(2, runOn);
|
||||
update.setParameter(3, runBy);
|
||||
update.setParameter(4, "ip");
|
||||
update.setParameter(5, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SQL insert given the table migration meta data is stored in.
|
||||
*/
|
||||
static String insertSql(String table) {
|
||||
return "insert into " + table
|
||||
+ " (id, status, run_version, dep_version, comment, checksum, run_on, run_by, run_ip)"
|
||||
+ " (id, mtype, mstatus, mversion, mcomment, mchecksum, run_on, run_by, run_ip)"
|
||||
+ " values (?,?,?,?,?,?,?,?,?)";
|
||||
}
|
||||
|
||||
static String updateSql(String table) {
|
||||
return "update " + table
|
||||
+ " set mchecksum = ?, run_on = ?, run_by = ?, run_ip = ?"
|
||||
+ " where id = ?";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.avaje.ebean.dbmigration.runner;
|
||||
import com.avaje.ebean.dbmigration.DdlRunner;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Runs the DDL migration scripts.
|
||||
@@ -21,7 +22,7 @@ public class MigrationScriptRunner {
|
||||
/**
|
||||
* Execute all the DDL statements in the script.
|
||||
*/
|
||||
public int runScript(boolean expectErrors, String content, String scriptName) {
|
||||
int runScript(boolean expectErrors, String content, String scriptName) throws SQLException {
|
||||
|
||||
DdlRunner runner = new DdlRunner(expectErrors, scriptName);
|
||||
return runner.runAll(content, connection);
|
||||
|
||||
@@ -17,6 +17,7 @@ import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Enumeration;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -29,7 +30,7 @@ public class MigrationTable {
|
||||
|
||||
private static final Logger logger = MigrationRunner.logger;
|
||||
|
||||
private final Connection connection;
|
||||
private final Connection connection;
|
||||
|
||||
private final EbeanServer server;
|
||||
|
||||
@@ -41,11 +42,15 @@ public class MigrationTable {
|
||||
private final ServerConfig serverConfig;
|
||||
private final String envUserName;
|
||||
|
||||
private final Timestamp runTime = new Timestamp(System.currentTimeMillis());
|
||||
|
||||
private final ScriptTransform scriptTransform;
|
||||
|
||||
private final String insertSql;
|
||||
|
||||
private final LinkedHashMap<String,MigrationMetaRow> migrations;
|
||||
private final String updateSql;
|
||||
|
||||
private final LinkedHashMap<String, MigrationMetaRow> migrations;
|
||||
|
||||
private MigrationMetaRow lastMigration;
|
||||
|
||||
@@ -65,6 +70,7 @@ public class MigrationTable {
|
||||
this.schema = null;
|
||||
this.table = migrationConfig.getMetaTable();
|
||||
this.insertSql = MigrationMetaRow.insertSql(table);
|
||||
this.updateSql = MigrationMetaRow.updateSql(table);
|
||||
|
||||
this.scriptTransform = createScriptTransform(migrationConfig);
|
||||
this.envUserName = System.getProperty("user.name");
|
||||
@@ -96,16 +102,17 @@ public class MigrationTable {
|
||||
}
|
||||
|
||||
ExternalJdbcTransaction t = new ExternalJdbcTransaction(connection);
|
||||
SqlQuery sqlQuery = server.createSqlQuery("select * from "+table+" order by id for update");
|
||||
SqlQuery sqlQuery = server.createSqlQuery("select * from " + table + " order by id for update");
|
||||
List<SqlRow> metaRows = server.findList(sqlQuery, t);
|
||||
|
||||
for (SqlRow row : metaRows) {
|
||||
addMigration(new MigrationMetaRow(row));
|
||||
MigrationMetaRow metaRow = new MigrationMetaRow(row);
|
||||
addMigration(metaRow.getVersion(), metaRow);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void createTable(Connection connection) throws IOException {
|
||||
private void createTable(Connection connection) throws IOException, SQLException {
|
||||
|
||||
String script = ScriptTransform.table(table, getCreateTableScript());
|
||||
|
||||
@@ -146,82 +153,101 @@ public class MigrationTable {
|
||||
/**
|
||||
* Return true if the migration ran successfully and false if the migration failed.
|
||||
*/
|
||||
public boolean shouldRun(LocalMigrationResource localVersion, LocalMigrationResource priorVersion) {
|
||||
public boolean shouldRun(LocalMigrationResource localVersion, LocalMigrationResource priorVersion) throws SQLException {
|
||||
|
||||
if (priorVersion != null) {
|
||||
// check priorVersion is installed
|
||||
MigrationMetaRow existing = migrations.get(priorVersion.getVersion().normalised());
|
||||
if (existing == null) {
|
||||
logger.warn("Migration {} requires prior migration {} which has not been run", localVersion.getVersion(), priorVersion.getVersion());
|
||||
if (priorVersion != null && !localVersion.isRepeatable()) {
|
||||
if (!migrationExists(priorVersion)) {
|
||||
logger.error("Migration {} requires prior migration {} which has not been run", localVersion.getVersion(), priorVersion.getVersion());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
MigrationMetaRow existing = migrations.get(localVersion.getVersion().normalised());
|
||||
if (existing == null) {
|
||||
runMigration(localVersion, priorVersion);
|
||||
return true;
|
||||
|
||||
} else {
|
||||
// check checksum and return ok, or re-run if repeatable script?
|
||||
existing.getChecksum();
|
||||
return true;
|
||||
}
|
||||
MigrationMetaRow existing = migrations.get(localVersion.key());
|
||||
return runMigration(localVersion, existing);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the migration script.
|
||||
*
|
||||
* @param local The local migration resource
|
||||
* @param existing The information for this migration existing in the table
|
||||
*
|
||||
* @return True if the migrations should continue
|
||||
*/
|
||||
private void runMigration(LocalMigrationResource localVersion, LocalMigrationResource prior) {
|
||||
private boolean runMigration(LocalMigrationResource local, MigrationMetaRow existing) throws SQLException {
|
||||
|
||||
logger.debug("run migration "+localVersion.getLocation());
|
||||
String script = convertScript(local.getContent());
|
||||
int checksum = Checksum.calculate(script);
|
||||
|
||||
String script = convertScript(localVersion.getContent());
|
||||
if (existing != null) {
|
||||
|
||||
boolean matchChecksum = (existing.getChecksum() == checksum);
|
||||
|
||||
if (!local.isRepeatable()) {
|
||||
if (!matchChecksum) {
|
||||
logger.error("Checksum mismatch on migration {}", local.getLocation());
|
||||
}
|
||||
return true;
|
||||
|
||||
} else if (matchChecksum) {
|
||||
logger.trace("... skip unchanged repeatable migration {}", local.getLocation());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
runMigration(local, existing, script, checksum);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a migration script as new migration or update on existing repeatable migration.
|
||||
*/
|
||||
private void runMigration(LocalMigrationResource local, MigrationMetaRow existing, String script, int checksum) throws SQLException {
|
||||
|
||||
logger.debug("run migration {}", local.getLocation());
|
||||
|
||||
MigrationScriptRunner run = new MigrationScriptRunner(connection);
|
||||
run.runScript(false, script, "run migration version: "+localVersion.getVersion());
|
||||
run.runScript(false, script, "run migration version: " + local.getVersion());
|
||||
|
||||
if (existing != null) {
|
||||
// update existing migration row
|
||||
SqlUpdate update = server.createSqlUpdate(updateSql);
|
||||
existing.bindUpdate(checksum, envUserName, runTime, update);
|
||||
server.execute(update, new ExternalJdbcTransaction(connection));
|
||||
|
||||
int checksum = Checksum.calculate(script);
|
||||
MigrationMetaRow metaRow = createMetaRow(localVersion, prior, checksum);
|
||||
} else {
|
||||
// insert new migration row
|
||||
SqlUpdate insert = server.createSqlUpdate(insertSql);
|
||||
MigrationMetaRow metaRow = createMetaRow(local, checksum);
|
||||
metaRow.bindInsert(insert);
|
||||
server.execute(insert, new ExternalJdbcTransaction(connection));
|
||||
|
||||
SqlUpdate insert = server.createSqlUpdate(insertSql);
|
||||
metaRow.bindInsert(insert);
|
||||
server.execute(insert, new ExternalJdbcTransaction(connection));
|
||||
|
||||
addMigration(metaRow);
|
||||
addMigration(local.key(), metaRow);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the MigrationMetaRow for this migration.
|
||||
*/
|
||||
private MigrationMetaRow createMetaRow(LocalMigrationResource localVersion, LocalMigrationResource prior, int checksum) {
|
||||
private MigrationMetaRow createMetaRow(LocalMigrationResource migration, int checksum) {
|
||||
|
||||
int nextId = 1;
|
||||
if (lastMigration != null) {
|
||||
nextId = lastMigration.getId() + 1;
|
||||
}
|
||||
|
||||
String runVersion = localVersion.getVersion().normalised();
|
||||
String comment = getMigrationComment(localVersion);
|
||||
String priorVersion = getMigrationPriorVersion(prior);
|
||||
String type = migration.getType();
|
||||
String runVersion = migration.key();
|
||||
String comment = migration.getComment();
|
||||
|
||||
return new MigrationMetaRow(nextId, runVersion, priorVersion, comment, checksum, envUserName);
|
||||
return new MigrationMetaRow(nextId, type, runVersion, comment, checksum, envUserName, runTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the prior migration normalised version.
|
||||
* Return true if the migration exists.
|
||||
*/
|
||||
private String getMigrationPriorVersion(LocalMigrationResource prior) {
|
||||
return prior != null ? prior.getVersion().normalised() : "-";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the migration comment.
|
||||
*/
|
||||
private String getMigrationComment(LocalMigrationResource localVersion) {
|
||||
String comment = localVersion.getVersion().getComment();
|
||||
return comment == null || comment.isEmpty() ? "-" : comment;
|
||||
private boolean migrationExists(LocalMigrationResource priorVersion) {
|
||||
return migrations.containsKey(priorVersion.key());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -234,12 +260,11 @@ public class MigrationTable {
|
||||
/**
|
||||
* Register the successfully executed migration (to allow dependant scripts to run).
|
||||
*/
|
||||
private void addMigration(MigrationMetaRow metaRow) {
|
||||
private void addMigration(String key, MigrationMetaRow metaRow) {
|
||||
lastMigration = metaRow;
|
||||
String runVersion = metaRow.getRunVersion();
|
||||
if (runVersion == null) {
|
||||
if (metaRow.getVersion() == null) {
|
||||
throw new IllegalStateException("No runVersion in db migration table row? " + metaRow);
|
||||
}
|
||||
migrations.put(runVersion, metaRow);
|
||||
migrations.put(key, metaRow);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,8 +108,8 @@ public class TransactionEvent implements Serializable {
|
||||
/**
|
||||
* Build and return the cache changeSet.
|
||||
*/
|
||||
public CacheChangeSet buildCacheChanges() {
|
||||
CacheChangeSet changeSet = new CacheChangeSet();
|
||||
public CacheChangeSet buildCacheChanges(boolean viewInvalidation) {
|
||||
CacheChangeSet changeSet = new CacheChangeSet(viewInvalidation);
|
||||
if (eventBeans != null) {
|
||||
eventBeans.notifyCache(changeSet);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
|
||||
package com.avaje.ebeaninternal.extraddl.model;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlType;
|
||||
import javax.xml.bind.annotation.XmlValue;
|
||||
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <simpleContent>
|
||||
* <extension base="<http://www.w3.org/2001/XMLSchema>string">
|
||||
* <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="platforms" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* </extension>
|
||||
* </simpleContent>
|
||||
* </complexType>
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
*/
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlType(name = "", propOrder = {
|
||||
"value"
|
||||
})
|
||||
@XmlRootElement(name = "ddl-script")
|
||||
public class DdlScript {
|
||||
|
||||
@XmlValue
|
||||
protected String value;
|
||||
@XmlAttribute(name = "name", required = true)
|
||||
protected String name;
|
||||
@XmlAttribute(name = "platforms")
|
||||
protected String platforms;
|
||||
|
||||
/**
|
||||
* Gets the value of the value property.
|
||||
*
|
||||
* @return
|
||||
* possible object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the value property.
|
||||
*
|
||||
* @param value
|
||||
* allowed object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the name property.
|
||||
*
|
||||
* @return
|
||||
* possible object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the name property.
|
||||
*
|
||||
* @param value
|
||||
* allowed object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public void setName(String value) {
|
||||
this.name = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the platforms property.
|
||||
*
|
||||
* @return
|
||||
* possible object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public String getPlatforms() {
|
||||
return platforms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the platforms property.
|
||||
*
|
||||
* @param value
|
||||
* allowed object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public void setPlatforms(String value) {
|
||||
this.platforms = value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
|
||||
package com.avaje.ebeaninternal.extraddl.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
|
||||
* <sequence>
|
||||
* <element ref="{http://ebean-orm.github.io/xml/ns/extraddl}ddl-script" maxOccurs="unbounded"/>
|
||||
* </sequence>
|
||||
* </restriction>
|
||||
* </complexContent>
|
||||
* </complexType>
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
*/
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlType(name = "", propOrder = {
|
||||
"ddlScript"
|
||||
})
|
||||
@XmlRootElement(name = "extra-ddl")
|
||||
public class ExtraDdl {
|
||||
|
||||
@XmlElement(name = "ddl-script", required = true)
|
||||
protected List<DdlScript> ddlScript;
|
||||
|
||||
/**
|
||||
* Gets the value of the ddlScript property.
|
||||
*
|
||||
* <p>
|
||||
* This accessor method returns a reference to the live list,
|
||||
* not a snapshot. Therefore any modification you make to the
|
||||
* returned list will be present inside the JAXB object.
|
||||
* This is why there is not a <CODE>set</CODE> method for the ddlScript property.
|
||||
*
|
||||
* <p>
|
||||
* For example, to add a new item, do as follows:
|
||||
* <pre>
|
||||
* getDdlScript().add(newItem);
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* Objects of the following type(s) are allowed in the list
|
||||
* {@link DdlScript }
|
||||
*
|
||||
*
|
||||
*/
|
||||
public List<DdlScript> getDdlScript() {
|
||||
if (ddlScript == null) {
|
||||
ddlScript = new ArrayList<DdlScript>();
|
||||
}
|
||||
return this.ddlScript;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.avaje.ebeaninternal.extraddl.model;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Read ExtraDdl from an XML document.
|
||||
*/
|
||||
public class ExtraDdlXmlReader {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ExtraDdlXmlReader.class);
|
||||
|
||||
/**
|
||||
* Return the combined extra DDL that should be run given the platform name.
|
||||
*/
|
||||
public static String buildExtra(String platformName) {
|
||||
|
||||
ExtraDdl read = ExtraDdlXmlReader.read("/extra-ddl.xml");
|
||||
if (read == null) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(300);
|
||||
for (DdlScript script : read.getDdlScript()) {
|
||||
if (matchPlatform(platformName, script.getPlatforms())) {
|
||||
logger.debug("include script {}", script.getName());
|
||||
sb.append(script.getValue()).append("\n");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the script platforms is a match/supported for the given platform.
|
||||
* @param platformName The database platform we are generating/running DDL for
|
||||
* @param platforms The platforms (comma delimited) this script should run for
|
||||
*/
|
||||
public static boolean matchPlatform(String platformName, String platforms) {
|
||||
if (platforms == null || platforms.trim().length() == 0) {
|
||||
return true;
|
||||
}
|
||||
String[] names = platforms.split("[,;]");
|
||||
for (String name : names) {
|
||||
if (name.trim().toLowerCase().contains(platformName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and return a ExtraDdl from an xml document at the given resource path.
|
||||
*/
|
||||
public static ExtraDdl read(String resourcePath) {
|
||||
|
||||
InputStream is = ExtraDdlXmlReader.class.getResourceAsStream(resourcePath);
|
||||
if (is == null) {
|
||||
// we expect this and check for null
|
||||
return null;
|
||||
}
|
||||
return read(is);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and return a ExtraDdl from an xml document.
|
||||
*/
|
||||
public static ExtraDdl read(InputStream is) {
|
||||
|
||||
try {
|
||||
JAXBContext jaxbContext = JAXBContext.newInstance(ExtraDdl.class);
|
||||
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
|
||||
return (ExtraDdl) unmarshaller.unmarshal(is);
|
||||
|
||||
} catch (JAXBException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
|
||||
package com.avaje.ebeaninternal.extraddl.model;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRegistry;
|
||||
|
||||
|
||||
/**
|
||||
* This object contains factory methods for each
|
||||
* Java content interface and Java element interface
|
||||
* generated in the com.avaje.ebeaninternal.extraddl.model package.
|
||||
* <p>An ObjectFactory allows you to programatically
|
||||
* construct new instances of the Java representation
|
||||
* for XML content. The Java representation of XML
|
||||
* content can consist of schema derived interfaces
|
||||
* and classes representing the binding of schema
|
||||
* type definitions, element declarations and model
|
||||
* groups. Factory methods for each of these are
|
||||
* provided in this class.
|
||||
*
|
||||
*/
|
||||
@XmlRegistry
|
||||
public class ObjectFactory {
|
||||
|
||||
|
||||
/**
|
||||
* Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: com.avaje.ebeaninternal.extraddl.model
|
||||
*
|
||||
*/
|
||||
public ObjectFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link DdlScript }
|
||||
*
|
||||
*/
|
||||
public DdlScript createDdlScript() {
|
||||
return new DdlScript();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link ExtraDdl }
|
||||
*
|
||||
*/
|
||||
public ExtraDdl createExtraDdl() {
|
||||
return new ExtraDdl();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
@javax.xml.bind.annotation.XmlSchema(namespace = "http://ebean-orm.github.io/xml/ns/extraddl", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
|
||||
package com.avaje.ebeaninternal.extraddl.model;
|
||||
@@ -22,9 +22,25 @@ public class CacheChangeSet {
|
||||
private final Map<ManyKey, ManyChange> manyChangeMap = new HashMap<ManyKey, ManyChange>();
|
||||
|
||||
/**
|
||||
* Apply all the changes to the L2 cache.
|
||||
* Set of "base tables" modified used to invalidate entities based on views.
|
||||
*/
|
||||
public void apply() {
|
||||
private final Set<String> viewInvalidation = new HashSet<String>();
|
||||
|
||||
private final boolean viewEntityInvalidation;
|
||||
|
||||
/**
|
||||
* Construct specifying if we also need to process invalidation for entities based on views.
|
||||
*/
|
||||
public CacheChangeSet(boolean viewEntityInvalidation) {
|
||||
this.viewEntityInvalidation = viewEntityInvalidation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the changes to the L2 cache except entity/view invalidation.
|
||||
*
|
||||
* Return the set of table changes to process invalidation for entities based on views.
|
||||
*/
|
||||
public Set<String> apply() {
|
||||
for (BeanDescriptor entry : queryCaches) {
|
||||
entry.queryCacheClear();
|
||||
}
|
||||
@@ -34,6 +50,7 @@ public class CacheChangeSet {
|
||||
for (CacheChange entry : manyChangeMap.values()) {
|
||||
entry.apply();
|
||||
}
|
||||
return viewInvalidation;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,11 +81,23 @@ public class CacheChangeSet {
|
||||
many(desc, manyProperty).addPut(parentId, entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* On bean insert register table for view based entity invalidation.
|
||||
*/
|
||||
public void addBeanInsert(String baseTable) {
|
||||
if (viewEntityInvalidation) {
|
||||
viewInvalidation.add(baseTable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a bean from the cache.
|
||||
*/
|
||||
public <T> void addBeanRemove(BeanDescriptor<T> desc, Object id) {
|
||||
entries.add(new CacheChangeBeanRemove(desc, id));
|
||||
if (viewEntityInvalidation) {
|
||||
viewInvalidation.add(desc.getBaseTable());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,6 +105,9 @@ public class CacheChangeSet {
|
||||
*/
|
||||
public <T> void addBeanUpdate(BeanDescriptor<T> desc, Object id, Map<String, Object> changes, boolean updateNaturalKey, long version) {
|
||||
entries.add(new CacheChangeBeanUpdate(desc, id, changes, updateNaturalKey, version));
|
||||
if (viewEntityInvalidation) {
|
||||
viewInvalidation.add(desc.getBaseTable());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -133,7 +133,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
}
|
||||
|
||||
public enum EntityType {
|
||||
ORM, EMBEDDED, SQL
|
||||
ORM, EMBEDDED, VIEW, SQL
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,6 +179,8 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
|
||||
private final CompoundUniqueConstraint[] compoundUniqueConstraints;
|
||||
|
||||
private final String[] dependentTables;
|
||||
|
||||
/**
|
||||
* The base database table.
|
||||
*/
|
||||
@@ -446,6 +448,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
this.baseTable = InternString.intern(deploy.getBaseTable());
|
||||
this.baseTableAsOf = deploy.getBaseTableAsOf();
|
||||
this.baseTableVersionsBetween = deploy.getBaseTableVersionsBetween();
|
||||
this.dependentTables = deploy.getDependentTables();
|
||||
this.dbComment = deploy.getDbComment();
|
||||
this.autoTunable = EntityType.ORM.equals(entityType) && (beanFinder == null);
|
||||
|
||||
@@ -1057,6 +1060,13 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
return cacheHelp.isBeanCaching();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there is query caching for this type of bean.
|
||||
*/
|
||||
public boolean isQueryCaching() {
|
||||
return cacheHelp.isQueryCaching();
|
||||
}
|
||||
|
||||
public boolean isManyPropCaching() {
|
||||
return isBeanCaching();
|
||||
}
|
||||
@@ -2325,6 +2335,16 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
return dbComment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the dependent tables for a view based entity.
|
||||
* <p>
|
||||
* These tables
|
||||
* </p>
|
||||
*/
|
||||
public String[] getDependentTables() {
|
||||
return dependentTables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the base table. Only properties mapped to the base table are by
|
||||
* default persisted.
|
||||
@@ -2333,6 +2353,13 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
return baseTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this type is a base table entity type.
|
||||
*/
|
||||
public boolean isBaseTable() {
|
||||
return baseTable != null && entityType == EntityType.ORM;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the base table to use given the query temporal mode.
|
||||
*/
|
||||
|
||||
@@ -98,7 +98,7 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
/**
|
||||
* Return true if there is currently query caching for this type of bean.
|
||||
*/
|
||||
private boolean isQueryCaching() {
|
||||
boolean isQueryCaching() {
|
||||
return queryCache != null;
|
||||
}
|
||||
|
||||
@@ -598,6 +598,7 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
void handleInsert(PersistRequestBean<T> insertRequest, CacheChangeSet changeSet) {
|
||||
queryCacheClear(changeSet);
|
||||
cacheDeleteImported(false, insertRequest.getEntityBean(), changeSet);
|
||||
changeSet.addBeanInsert(desc.getBaseTable());
|
||||
}
|
||||
|
||||
private void cacheDeleteImported(boolean clear, EntityBean entityBean, CacheChangeSet changeSet) {
|
||||
|
||||
@@ -124,7 +124,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
private final DocStoreFactory docStoreFactory;
|
||||
|
||||
private int enhancedClassCount;
|
||||
|
||||
|
||||
private final boolean updateChangesOnly;
|
||||
|
||||
private final BootupClasses bootupClasses;
|
||||
@@ -143,6 +143,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
private final Map<String, List<BeanDescriptor<?>>> tableToDescMap = new HashMap<String, List<BeanDescriptor<?>>>();
|
||||
|
||||
private final Map<String, List<BeanDescriptor<?>>> tableToViewDescMap = new HashMap<String, List<BeanDescriptor<?>>>();
|
||||
|
||||
private List<BeanDescriptor<?>> immutableDescriptorList;
|
||||
|
||||
private final DbIdentity dbIdentity;
|
||||
@@ -174,12 +176,12 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
/**
|
||||
* Map of base tables to 'with history views' used to support 'as of' queries.
|
||||
*/
|
||||
private final Map<String,String> asOfTableMap = new HashMap<String, String>();
|
||||
private final Map<String, String> asOfTableMap = new HashMap<String, String>();
|
||||
|
||||
/**
|
||||
* Map of base tables to 'draft' tables.
|
||||
*/
|
||||
private final Map<String,String> draftTableMap = new HashMap<String, String>();
|
||||
private final Map<String, String> draftTableMap = new HashMap<String, String>();
|
||||
|
||||
/**
|
||||
* Create for a given database dbConfig.
|
||||
@@ -235,7 +237,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
DbHistorySupport historySupport = databasePlatform.getHistorySupport();
|
||||
// with historySupport returns a simple view suffix or the sql2011 as of timestamp suffix
|
||||
return (historySupport == null ) ? serverConfig.getAsOfViewSuffix() : historySupport.getAsOfViewSuffix(serverConfig.getAsOfViewSuffix());
|
||||
return (historySupport == null) ? serverConfig.getAsOfViewSuffix() : historySupport.getAsOfViewSuffix(serverConfig.getAsOfViewSuffix());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -245,7 +247,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
DbHistorySupport historySupport = databasePlatform.getHistorySupport();
|
||||
// with historySupport returns a simple view suffix or the sql2011 versions between timestamp suffix
|
||||
return (historySupport == null ) ? serverConfig.getAsOfViewSuffix() : historySupport.getVersionsBetweenSuffix(serverConfig.getAsOfViewSuffix());
|
||||
return (historySupport == null) ? serverConfig.getAsOfViewSuffix() : historySupport.getVersionsBetweenSuffix(serverConfig.getAsOfViewSuffix());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -300,14 +302,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
/**
|
||||
* Return the map of base tables to draft tables.
|
||||
*/
|
||||
public Map<String,String> getDraftTableMap() {
|
||||
public Map<String, String> getDraftTableMap() {
|
||||
return draftTableMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deploy returning the asOfTableMap (which is required by the SQL builders).
|
||||
*/
|
||||
public Map<String,String> deploy() {
|
||||
public Map<String, String> deploy() {
|
||||
|
||||
try {
|
||||
createListeners();
|
||||
@@ -356,10 +358,19 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
*/
|
||||
public void cacheNotify(TransactionEventTable.TableIUD tableIUD) {
|
||||
|
||||
List<BeanDescriptor<?>> list = getBeanDescriptors(tableIUD.getTableName());
|
||||
if (list != null) {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
list.get(i).cacheHandleBulkUpdate(tableIUD);
|
||||
String tableName = tableIUD.getTableName().toLowerCase();
|
||||
List<BeanDescriptor<?>> normalBeanTypes = tableToDescMap.get(tableName);
|
||||
if (normalBeanTypes != null) {
|
||||
// 'normal' entity beans based on a "base table"
|
||||
for (int i = 0; i < normalBeanTypes.size(); i++) {
|
||||
normalBeanTypes.get(i).cacheHandleBulkUpdate(tableIUD);
|
||||
}
|
||||
}
|
||||
List<BeanDescriptor<?>> viewBeans = tableToViewDescMap.get(tableName);
|
||||
if (viewBeans != null) {
|
||||
// entity beans based on a "view"
|
||||
for (int i = 0; i < viewBeans.size(); i++) {
|
||||
viewBeans.get(i).cacheHandleBulkUpdate(tableIUD);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -378,6 +389,21 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
return tableToDescMap.get(tableName.toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate entity beans based on views via their dependent tables.
|
||||
*/
|
||||
public void processViewInvalidation(Set<String> viewInvalidation) {
|
||||
|
||||
for (String depTable : viewInvalidation) {
|
||||
List<BeanDescriptor<?>> list = tableToViewDescMap.get(depTable.toLowerCase());
|
||||
if (list == null) {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
list.get(i).queryCacheClear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a map of table names to BeanDescriptors.
|
||||
* <p>
|
||||
@@ -390,7 +416,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
String baseTable = desc.getBaseTable();
|
||||
if (baseTable != null) {
|
||||
baseTable = baseTable.toLowerCase();
|
||||
|
||||
List<BeanDescriptor<?>> list = tableToDescMap.get(baseTable);
|
||||
if (list == null) {
|
||||
list = new ArrayList<BeanDescriptor<?>>(1);
|
||||
@@ -398,6 +423,22 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
}
|
||||
list.add(desc);
|
||||
}
|
||||
if (desc.getEntityType() == EntityType.VIEW && desc.isQueryCaching()) {
|
||||
// build map of tables to view entities dependent on those tables
|
||||
// for the purpose of invalidating appropriate query caches
|
||||
String[] dependentTables = desc.getDependentTables();
|
||||
if (dependentTables != null && dependentTables.length > 0) {
|
||||
for (String depTable : dependentTables) {
|
||||
depTable = depTable.toLowerCase();
|
||||
List<BeanDescriptor<?>> list = tableToViewDescMap.get(depTable);
|
||||
if (list == null) {
|
||||
list = new ArrayList<BeanDescriptor<?>>(1);
|
||||
tableToViewDescMap.put(depTable, list);
|
||||
}
|
||||
list.add(desc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,6 +528,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
throw new PersistenceException(msg, source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there are 'view based entities' using l2 query caching and so need
|
||||
* to be invalidated based on changes to dependent tables.
|
||||
*/
|
||||
public boolean requiresViewEntityCacheInvalidation() {
|
||||
return !tableToViewDescMap.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an immutable list of all the BeanDescriptors.
|
||||
*/
|
||||
@@ -539,7 +588,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* Return the bean deploy info for the given class.
|
||||
*/
|
||||
public <T> DeployBeanInfo<T> getDeploy(Class<T> cls) {
|
||||
return (DeployBeanInfo<T>) deplyInfoMap.get(cls);
|
||||
return (DeployBeanInfo<T>) deplyInfoMap.get(cls);
|
||||
}
|
||||
|
||||
private void registerBeanDescriptor(BeanDescriptor<?> desc) {
|
||||
@@ -651,7 +700,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private void readEntityRelationships() {
|
||||
|
||||
// We only perform 'circular' checks etc after we have
|
||||
@@ -667,9 +716,9 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
// Set inheritance info
|
||||
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
|
||||
setInheritanceInfo(info);
|
||||
setInheritanceInfo(info);
|
||||
}
|
||||
|
||||
|
||||
for (DeployBeanInfo<?> info : deplyInfoMap.values()) {
|
||||
registerBeanDescriptor(new BeanDescriptor(this, info.getDescriptor()));
|
||||
}
|
||||
@@ -681,24 +730,24 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* @param info the new inheritance info
|
||||
*/
|
||||
private void setInheritanceInfo(DeployBeanInfo<?> info) {
|
||||
|
||||
for (DeployBeanPropertyAssocOne<?> oneProp : info.getDescriptor().propertiesAssocOne()) {
|
||||
if (!oneProp.isTransient()) {
|
||||
DeployBeanInfo<?> assoc = deplyInfoMap.get(oneProp.getTargetType());
|
||||
if (assoc != null){
|
||||
oneProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (DeployBeanPropertyAssocMany<?> manyProp : info.getDescriptor().propertiesAssocMany()) {
|
||||
if (!manyProp.isTransient()) {
|
||||
DeployBeanInfo<?> assoc = deplyInfoMap.get(manyProp.getTargetType());
|
||||
if (assoc != null){
|
||||
manyProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (DeployBeanPropertyAssocOne<?> oneProp : info.getDescriptor().propertiesAssocOne()) {
|
||||
if (!oneProp.isTransient()) {
|
||||
DeployBeanInfo<?> assoc = deplyInfoMap.get(oneProp.getTargetType());
|
||||
if (assoc != null) {
|
||||
oneProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (DeployBeanPropertyAssocMany<?> manyProp : info.getDescriptor().propertiesAssocMany()) {
|
||||
if (!manyProp.isTransient()) {
|
||||
DeployBeanInfo<?> assoc = deplyInfoMap.get(manyProp.getTargetType());
|
||||
if (assoc != null) {
|
||||
manyProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void secondaryPropsJoins(DeployBeanInfo<?> info) {
|
||||
@@ -845,7 +894,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* into the order_id column on the order_lines table).
|
||||
* </p>
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private void makeUnidirectional(DeployBeanInfo<?> info, DeployBeanPropertyAssocMany<?> oneToMany) {
|
||||
|
||||
DeployBeanDescriptor<?> targetDesc = getTargetDescriptor(oneToMany);
|
||||
@@ -1081,7 +1130,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
desc.setUpdateChangesOnly(updateChangesOnly);
|
||||
|
||||
beanLifecycleAdapterFactory.addLifecycleMethods(desc);
|
||||
|
||||
|
||||
// set bean controller, finder and listener
|
||||
setBeanControllerFinderListener(desc);
|
||||
deplyInherit.process(desc);
|
||||
@@ -1103,8 +1152,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
readXml(desc);
|
||||
|
||||
if (!EntityType.ORM.equals(desc.getEntityType())) {
|
||||
// not using base table
|
||||
if (EntityType.SQL == desc.getEntityType()) {
|
||||
desc.setBaseTable(null, null, null);
|
||||
}
|
||||
|
||||
@@ -1373,7 +1421,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
Class<?> beanType = desc.getBeanType();
|
||||
|
||||
BeanPropertiesReader reflectProps = new BeanPropertiesReader(beanType);
|
||||
|
||||
|
||||
BeanPropertyInfo beanReflect = reflectFactory.create(beanType);
|
||||
desc.setProperties(reflectProps.getProperties());
|
||||
|
||||
@@ -1384,7 +1432,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
if (isPersistentField(prop)) {
|
||||
throw new IllegalStateException("Property " + propName + " not found in " + reflectProps + " for type " + beanType);
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
final int propertyIndex = pos;
|
||||
prop.setPropertyIndex(propertyIndex);
|
||||
@@ -1459,7 +1507,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
Class<?> beanClass = desc.getBeanType();
|
||||
|
||||
if (!hasEntityBeanInterface(beanClass)) {
|
||||
throw new IllegalStateException("Bean "+beanClass+" is not enhanced?");
|
||||
throw new IllegalStateException("Bean " + beanClass + " is not enhanced?");
|
||||
}
|
||||
|
||||
// the bean already implements EntityBean
|
||||
@@ -1475,7 +1523,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* enhanced or all dynamically subclassed).
|
||||
*/
|
||||
private void checkInheritedClasses(Class<?> beanClass) {
|
||||
|
||||
|
||||
Class<?> superclass = beanClass.getSuperclass();
|
||||
if (Object.class.equals(superclass)) {
|
||||
// we got to the top of the inheritance
|
||||
@@ -1490,9 +1538,9 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
// ok to stop and treat just the same as Object.class
|
||||
return;
|
||||
}
|
||||
throw new IllegalStateException("Super type "+superclass+" is not enhanced?");
|
||||
throw new IllegalStateException("Super type " + superclass + " is not enhanced?");
|
||||
}
|
||||
|
||||
|
||||
// recursively continue up the inheritance hierarchy
|
||||
checkInheritedClasses(superclass);
|
||||
}
|
||||
@@ -1502,7 +1550,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* If so it is ok for it not to be enhanced.
|
||||
*/
|
||||
private boolean isMappedSuperWithNoProperties(Class<?> beanClass) {
|
||||
|
||||
|
||||
MappedSuperclass annotation = beanClass.getAnnotation(MappedSuperclass.class);
|
||||
if (annotation == null) {
|
||||
return false;
|
||||
|
||||
@@ -129,6 +129,8 @@ public class DeployBeanDescriptor<T> {
|
||||
|
||||
private String draftTable;
|
||||
|
||||
private String[] dependentTables;
|
||||
|
||||
private boolean historySupport;
|
||||
|
||||
private boolean readAuditing;
|
||||
@@ -582,6 +584,13 @@ public class DeployBeanDescriptor<T> {
|
||||
return draftTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* For view based entity return the dependant tables.
|
||||
*/
|
||||
public String[] getDependentTables() {
|
||||
return dependentTables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the base table. Only properties mapped to the base table are by
|
||||
* default persisted.
|
||||
@@ -611,6 +620,15 @@ public class DeployBeanDescriptor<T> {
|
||||
return baseTableFull;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set when entity is based on a view.
|
||||
*/
|
||||
public void setView(String viewName, String[] dependentTables) {
|
||||
this.entityType = EntityType.VIEW;
|
||||
this.dependentTables = this.dependentTables;
|
||||
setBaseTable(new TableName(viewName), "", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the base table. Only properties mapped to the base table are by default persisted.
|
||||
*/
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.deploy.parse;
|
||||
|
||||
import com.avaje.ebean.config.TableName;
|
||||
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanTable;
|
||||
|
||||
/**
|
||||
* Read the annotations for BeanTable.
|
||||
* <p>
|
||||
* Refer to BeanTable but basically determining base table, table alias
|
||||
* and the unique id properties.
|
||||
* </p>
|
||||
*/
|
||||
public class AnnotationBeanTable extends AnnotationBase {
|
||||
|
||||
final DeployBeanTable beanTable;
|
||||
|
||||
public AnnotationBeanTable(DeployUtil util, DeployBeanTable beanTable){
|
||||
super(util);
|
||||
this.beanTable = beanTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the annotations.
|
||||
*/
|
||||
public void parse() {
|
||||
|
||||
TableName tableName = namingConvention.getTableName(beanTable.getBeanType());
|
||||
|
||||
beanTable.setBaseTable(tableName.getQualifiedName());
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import com.avaje.ebean.annotation.NamedUpdate;
|
||||
import com.avaje.ebean.annotation.NamedUpdates;
|
||||
import com.avaje.ebean.annotation.ReadAudit;
|
||||
import com.avaje.ebean.annotation.UpdateMode;
|
||||
import com.avaje.ebean.annotation.View;
|
||||
import com.avaje.ebean.config.TableName;
|
||||
import com.avaje.ebeaninternal.server.core.CacheOptions;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
|
||||
@@ -136,13 +137,15 @@ public class AnnotationClass extends AnnotationParser {
|
||||
descriptor.addCompoundUniqueConstraint(new CompoundUniqueConstraint(uc.columnNames()));
|
||||
}
|
||||
|
||||
View view = cls.getAnnotation(View.class);
|
||||
if (view != null) {
|
||||
descriptor.setView(view.name(), view.dependentTables());
|
||||
}
|
||||
Table table = cls.getAnnotation(Table.class);
|
||||
if (table != null) {
|
||||
UniqueConstraint[] uniqueConstraints = table.uniqueConstraints();
|
||||
if (uniqueConstraints != null) {
|
||||
for (UniqueConstraint c : uniqueConstraints) {
|
||||
descriptor.addCompoundUniqueConstraint(new CompoundUniqueConstraint(c.columnNames()));
|
||||
}
|
||||
for (UniqueConstraint c : uniqueConstraints) {
|
||||
descriptor.addCompoundUniqueConstraint(new CompoundUniqueConstraint(c.columnNames()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -84,9 +84,9 @@ public final class PostCommitProcessing {
|
||||
/**
|
||||
* Notify the local part of L2 cache.
|
||||
*/
|
||||
void notifyLocalCache() {
|
||||
void notifyLocalCache(boolean viewInvalidation) {
|
||||
processTableEvents(event.getEventTables());
|
||||
cacheChanges = event.buildCacheChanges();
|
||||
cacheChanges = event.buildCacheChanges(viewInvalidation);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,7 +149,7 @@ public final class PostCommitProcessing {
|
||||
return new Runnable() {
|
||||
public void run() {
|
||||
if (cacheChanges != null) {
|
||||
cacheChanges.apply();
|
||||
manager.processViewInvalidation(cacheChanges.apply());
|
||||
}
|
||||
localPersistListenersNotify();
|
||||
notifyCluster();
|
||||
|
||||
@@ -27,6 +27,7 @@ import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
@@ -104,6 +105,8 @@ public class TransactionManager {
|
||||
*/
|
||||
private final ChangeLogListener changeLogListener;
|
||||
|
||||
private final boolean viewInvalidation;
|
||||
|
||||
/**
|
||||
* Create the TransactionManager
|
||||
*/
|
||||
@@ -113,6 +116,7 @@ public class TransactionManager {
|
||||
this.persistBatch = config.getPersistBatch();
|
||||
this.persistBatchOnCascade = config.appliedPersistBatchOnCascade();
|
||||
this.beanDescriptorManager = descMgr;
|
||||
this.viewInvalidation = descMgr.requiresViewEntityCacheInvalidation();
|
||||
this.changeLogPrepare = descMgr.getChangeLogPrepare();
|
||||
this.changeLogListener = descMgr.getChangeLogListener();
|
||||
this.clusterManager = clusterManager;
|
||||
@@ -398,7 +402,7 @@ public class TransactionManager {
|
||||
}
|
||||
|
||||
PostCommitProcessing postCommit = new PostCommitProcessing(clusterManager, this, transaction);
|
||||
postCommit.notifyLocalCache();
|
||||
postCommit.notifyLocalCache(viewInvalidation);
|
||||
backgroundExecutor.execute(postCommit.backgroundNotify());
|
||||
|
||||
for (TransactionEventListener listener : transactionEventListeners) {
|
||||
@@ -423,7 +427,7 @@ public class TransactionManager {
|
||||
event.add(tableEvents);
|
||||
|
||||
PostCommitProcessing postCommit = new PostCommitProcessing(clusterManager, this, event);
|
||||
postCommit.notifyLocalCache();
|
||||
postCommit.notifyLocalCache(viewInvalidation);
|
||||
backgroundExecutor.execute(postCommit.backgroundNotify());
|
||||
}
|
||||
|
||||
@@ -478,4 +482,13 @@ public class TransactionManager {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate the query caches for entities based on views.
|
||||
*/
|
||||
public void processViewInvalidation(Set<String> viewInvalidation) {
|
||||
if (!viewInvalidation.isEmpty()) {
|
||||
beanDescriptorManager.processViewInvalidation(viewInvalidation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user