mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
49
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4788dd4ec9 | ||
|
|
ca7e5b79b8 | ||
|
|
90dfbcd7b3 | ||
|
|
6b39bad4da | ||
|
|
25dea0b5b0 | ||
|
|
0069d2fabc | ||
|
|
98964638ba | ||
|
|
4573a995f1 | ||
|
|
4bc80cc724 | ||
|
|
a2972abe3e | ||
|
|
54eaf561f4 | ||
|
|
3134dd3165 | ||
|
|
8fc6abd480 | ||
|
|
f031bb72b6 | ||
|
|
42f0b419e8 | ||
|
|
7cbae758fc | ||
|
|
fb32cfa170 | ||
|
|
ffa8268125 | ||
|
|
eba1003c19 | ||
|
|
eca6383dba | ||
|
|
624d08f46c | ||
|
|
14749daf9b | ||
|
|
2817875706 | ||
|
|
3f8d705439 | ||
|
|
e9faef2692 | ||
|
|
d459807af7 | ||
|
|
10ad9ef096 | ||
|
|
11e3649b85 | ||
|
|
ddc00fedb6 | ||
|
|
0aa9208dcc | ||
|
|
e3b1996dc9 | ||
|
|
24ca744e06 | ||
|
|
7f9ce5bdd4 | ||
|
|
23e5de0819 | ||
|
|
c23508819f | ||
|
|
af030e3da4 | ||
|
|
d8e67d09a9 | ||
|
|
66c08e663e | ||
|
|
9a096e272e | ||
|
|
21e87e12cb | ||
|
|
7c6d7dd4d0 | ||
|
|
c5329e9db0 | ||
|
|
bccb9f8ddb | ||
|
|
c57af29fca | ||
|
|
37b6a321ab | ||
|
|
896f7d9ea8 | ||
|
|
141a0d9358 | ||
|
|
5a7f24da5d | ||
|
|
8ea86d6c6d |
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>11.15.11</version>
|
||||
<version>11.17.2</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>ebean</name>
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
|
||||
<tag>ebean-11.15.11</tag>
|
||||
<tag>ebean-11.17.2</tag>
|
||||
</scm>
|
||||
|
||||
<profiles>
|
||||
|
||||
@@ -167,6 +167,30 @@ public abstract class BeanRepository<I, T> extends BeanFinder<I, T> {
|
||||
return db().deletePermanent(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge this entity using the default merge options.
|
||||
* <p>
|
||||
* Ebean will detect if this is a new bean or a previously fetched bean and perform either an
|
||||
* insert or an update based on that.
|
||||
*
|
||||
* @see EbeanServer#merge(Object)
|
||||
*/
|
||||
public void merge(T bean) {
|
||||
db().merge(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge this entity using the specified merge options.
|
||||
* <p>
|
||||
* Ebean will detect if this is a new bean or a previously fetched bean and perform either an
|
||||
* insert or an update based on that.
|
||||
*
|
||||
* @see EbeanServer#merge(Object, MergeOptions)
|
||||
*/
|
||||
public void merge(T bean, MergeOptions options) {
|
||||
db().merge(bean, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes this entity from the database.
|
||||
*
|
||||
|
||||
@@ -638,6 +638,15 @@ public final class Ebean {
|
||||
serverMgr.getDefaultServer().updateAll(beans);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the bean using the default merge options.
|
||||
*
|
||||
* @param bean The bean to merge
|
||||
*/
|
||||
public static void merge(Object bean) {
|
||||
serverMgr.getDefaultServer().merge(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the bean using the given merge options.
|
||||
*
|
||||
|
||||
@@ -1628,6 +1628,13 @@ public interface EbeanServer {
|
||||
*/
|
||||
void updateAll(Collection<?> beans, Transaction transaction) throws OptimisticLockException;
|
||||
|
||||
/**
|
||||
* Merge the bean using the default merge options (no paths specified, default delete).
|
||||
*
|
||||
* @param bean The bean to merge
|
||||
*/
|
||||
void merge(Object bean);
|
||||
|
||||
/**
|
||||
* Merge the bean using the given merge options.
|
||||
*
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package io.ebean;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
/**
|
||||
* Class to determine the ebean version. (
|
||||
* @author Roland Praml, FOCONIS AG
|
||||
@@ -13,31 +13,32 @@ import org.slf4j.LoggerFactory;
|
||||
*/
|
||||
public class EbeanVersion {
|
||||
private EbeanVersion() {
|
||||
|
||||
|
||||
}
|
||||
private static final Logger logger = LoggerFactory.getLogger(EbeanVersion.class);
|
||||
|
||||
|
||||
private static String version = "unknown";
|
||||
static {
|
||||
try {
|
||||
Properties prop = new Properties();
|
||||
InputStream in = Ebean.class.getResourceAsStream("/META-INF/maven/io.ebean/ebean/pom.properties");
|
||||
if (in != null) {
|
||||
prop.load(in);
|
||||
in.close();
|
||||
version = prop.getProperty("version");
|
||||
try (InputStream in = Ebean.class.getResourceAsStream("/META-INF/maven/io.ebean/ebean/pom.properties")) {
|
||||
if (in != null) {
|
||||
prop.load(in);
|
||||
in.close();
|
||||
version = prop.getProperty("version");
|
||||
}
|
||||
}
|
||||
logger.info("ebean version: {}", version);
|
||||
} catch (IOException e) {
|
||||
logger.warn("Could not determine ebean version: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the ebean version (read from /META-INF/maven/io.ebean/ebean/pom.properties)
|
||||
*/
|
||||
public static String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -8,11 +8,20 @@ import java.util.Set;
|
||||
*/
|
||||
public class MergeOptionsBuilder {
|
||||
|
||||
private static final MOptions DEFAULT_OPTIONS = new MOptions();
|
||||
|
||||
private Set<String> paths = new LinkedHashSet<>();
|
||||
|
||||
private boolean clientGeneratedIds;
|
||||
|
||||
private boolean deletePermanent = true;
|
||||
private boolean deletePermanent;
|
||||
|
||||
/**
|
||||
* Return the default options.
|
||||
*/
|
||||
public static MergeOptions defaultOptions() {
|
||||
return DEFAULT_OPTIONS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a path that will included in the merge.
|
||||
@@ -47,7 +56,7 @@ public class MergeOptionsBuilder {
|
||||
/**
|
||||
* Build and return the MergeOptions instance.
|
||||
*/
|
||||
public MOptions build() {
|
||||
public MergeOptions build() {
|
||||
return new MOptions(paths, clientGeneratedIds, deletePermanent);
|
||||
}
|
||||
|
||||
@@ -57,6 +66,12 @@ public class MergeOptionsBuilder {
|
||||
private final boolean deletePermanent;
|
||||
private final Set<String> paths;
|
||||
|
||||
private MOptions(){
|
||||
this.clientGeneratedIds = false;
|
||||
this.paths = new LinkedHashSet<>();
|
||||
this.deletePermanent = false;
|
||||
}
|
||||
|
||||
private MOptions(Set<String> paths, boolean clientGeneratedIds, boolean deletePermanent) {
|
||||
this.paths = paths;
|
||||
this.clientGeneratedIds = clientGeneratedIds;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package io.ebean;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Used with SqlQuery to process potentially large queries reading directly from the JDBC ResultSet.
|
||||
* <p>
|
||||
* This provides a low level option that reads directly from the JDBC ResultSet.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* String sql = "select id, name, status from o_customer order by name desc";
|
||||
*
|
||||
* Ebean.createSqlQuery(sql)
|
||||
* .findEachRow((resultSet, rowNum) -> {
|
||||
*
|
||||
* // read directly from ResultSet
|
||||
*
|
||||
* long id = resultSet.getLong(1);
|
||||
* String name = resultSet.getString(2);
|
||||
*
|
||||
* // do something interesting with the data
|
||||
*
|
||||
* });
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface RowConsumer {
|
||||
|
||||
/**
|
||||
* Read the data from the ResultSet and process it.
|
||||
*
|
||||
* @param resultSet The JDBC ResultSet positioned to the current row
|
||||
* @param rowNum The number of the current row being mapped.
|
||||
*/
|
||||
void accept(ResultSet resultSet, int rowNum) throws SQLException;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package io.ebean;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Used with SqlQuery to map raw JDBC ResultSet to objects.
|
||||
* <p>
|
||||
* This provides a low level mapping option with direct use of JDBC ResultSet
|
||||
* with the option of having logic in the mapping. For example, only map some
|
||||
* columns depending on the values read from other columns.
|
||||
* </p>
|
||||
* <p>
|
||||
* For straight mapping into beans then DtoQuery would be the first choice as
|
||||
* it can automatically map the ResultSet into beans.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* //
|
||||
* // A mapper from ResultSet into our CustomerDto bean
|
||||
* //
|
||||
* class CustomerMapper implements RowMapper<CustomerDto> {
|
||||
*
|
||||
* @Override
|
||||
* public CustomerDto map(ResultSet rset, int rowNum) throws SQLException {
|
||||
*
|
||||
* long id = rset.getLong(1);
|
||||
* String name = rset.getString(2);
|
||||
* String status = rset.getString(3);
|
||||
*
|
||||
* return new CustomerDto(id, name, status);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*
|
||||
* //
|
||||
* // Then use the mapper
|
||||
* //
|
||||
*
|
||||
* String sql = "select id, name, status from o_customer where name = ?";
|
||||
*
|
||||
* CustomerDto rob = Ebean.createSqlQuery(sql)
|
||||
* .setParameter(1, "Rob")
|
||||
* .findOne(CUSTOMER_MAPPER);
|
||||
*
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param <T> The type the row data is mapped into.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface RowMapper<T> {
|
||||
|
||||
/**
|
||||
* Read the data from the ResultSet and map to the return type.
|
||||
*
|
||||
* @param resultSet The JDBC ResultSet positioned to the current row
|
||||
* @param rowNum The number of the current row being mapped.
|
||||
*/
|
||||
T map(ResultSet resultSet, int rowNum) throws SQLException;
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package io.ebean;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
@@ -77,12 +78,118 @@ public interface SqlQuery extends Serializable {
|
||||
@Nullable
|
||||
SqlRow findOne();
|
||||
|
||||
/**
|
||||
* Execute the query returning a single result using the mapper.
|
||||
*
|
||||
* @param mapper Used to map each ResultSet row into the result object.
|
||||
*/
|
||||
<T> T findOne(RowMapper<T> mapper);
|
||||
|
||||
/**
|
||||
* Execute the query returning a list using the mapper.
|
||||
*
|
||||
* @param mapper Used to map each ResultSet row into the result object.
|
||||
*/
|
||||
<T> List<T> findList(RowMapper<T> mapper);
|
||||
|
||||
/**
|
||||
* Execute the query reading each row from ResultSet using the RowConsumer.
|
||||
* <p>
|
||||
* This provides a low level option that reads directly from the JDBC ResultSet
|
||||
* and is good for processing very large results where (unlike findList) we don't
|
||||
* hold all the results in memory but instead can process row by row.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* String sql = "select id, name, status from customer order by name desc";
|
||||
*
|
||||
* Ebean.createSqlQuery(sql)
|
||||
* .findEachRow((resultSet, rowNum) -> {
|
||||
*
|
||||
* // read directly from ResultSet
|
||||
*
|
||||
* long id = resultSet.getLong(1);
|
||||
* String name = resultSet.getString(2);
|
||||
*
|
||||
* // do something interesting with the data
|
||||
*
|
||||
* });
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param consumer Used to read and process each ResultSet row.
|
||||
*/
|
||||
void findEachRow(RowConsumer consumer);
|
||||
|
||||
/**
|
||||
* Execute the query returning an optional row.
|
||||
*/
|
||||
@Nonnull
|
||||
Optional<SqlRow> findOneOrEmpty();
|
||||
|
||||
/**
|
||||
* Execute the query returning a single scalar attribute.
|
||||
* <pre>@{code
|
||||
*
|
||||
* String sql = "select max(unit_price) from o_order_detail where order_qty > ?";
|
||||
*
|
||||
* BigDecimal maxPrice = Ebean.createSqlQuery(sql)
|
||||
* .setParameter(1, 2)
|
||||
* .findSingleAttribute(BigDecimal.class);
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* The attributeType can be any scalar type that Ebean supports (includes javax time types, Joda types etc).
|
||||
* </p>
|
||||
*
|
||||
* @param attributeType The type of the returned value
|
||||
*/
|
||||
<T> T findSingleAttribute(Class<T> attributeType);
|
||||
|
||||
/**
|
||||
* Execute the query returning a single BigDecimal value.
|
||||
* <p>
|
||||
* This is an alias for <code>findSingleAttribute(BigDecimal.class)</code>
|
||||
* </p>
|
||||
*/
|
||||
BigDecimal findSingleDecimal();
|
||||
|
||||
/**
|
||||
* Execute the query returning a single Long value.
|
||||
* <p>
|
||||
* This is an alias for <code>findSingleAttribute(Long.class)</code>
|
||||
* </p>
|
||||
*/
|
||||
Long findSingleLong();
|
||||
|
||||
/**
|
||||
* Execute the query returning a list of scalar attribute values.
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* String sql =
|
||||
* " select (unit_price * order_qty) " +
|
||||
* " from o_order_detail " +
|
||||
* " where unit_price > ? " +
|
||||
* " order by (unit_price * order_qty) desc";
|
||||
*
|
||||
* //
|
||||
* List<BigDecimal> lineAmounts = Ebean.createSqlQuery(sql)
|
||||
* .setParameter(1, 3)
|
||||
* .findSingleAttributeList(BigDecimal.class);
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* The attributeType can be any scalar type that Ebean supports (includes javax time types, Joda types etc).
|
||||
* </p>
|
||||
*
|
||||
* @param attributeType The type of the returned value
|
||||
*/
|
||||
<T> List<T> findSingleAttributeList(Class<T> attributeType);
|
||||
|
||||
/**
|
||||
* The same as bind for named parameters.
|
||||
*/
|
||||
|
||||
@@ -19,15 +19,17 @@ package io.ebean;
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // example that uses 'named' parameters
|
||||
* String s = "UPDATE f_topic set post_count = :count where id = :id";
|
||||
* SqlUpdate update = Ebean.createSqlUpdate(s);
|
||||
* update.setParameter("id", 1);
|
||||
* update.setParameter("count", 50);
|
||||
* // example that uses 'named' parameters
|
||||
*
|
||||
* int modifiedCount = Ebean.execute(update);
|
||||
* String s = "UPDATE f_topic set post_count = :count where id = :id";
|
||||
*
|
||||
* String msg = "There were " + modifiedCount + " rows updated";
|
||||
* SqlUpdate update = Ebean.createSqlUpdate(s);
|
||||
* update.setParameter("id", 1);
|
||||
* update.setParameter("count", 50);
|
||||
*
|
||||
* int modifiedCount = update.execute();
|
||||
*
|
||||
* String msg = "There were " + modifiedCount + " rows updated";
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
@@ -57,6 +59,33 @@ package io.ebean;
|
||||
*
|
||||
* txn.commit();
|
||||
* }
|
||||
* }</pre>
|
||||
* <p>
|
||||
* An alternative to the batch mode on the transaction is to use addBatch() and executeBatch() like:
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* try (Transaction txn = Ebean.beginTransaction()) {
|
||||
*
|
||||
* insert.setNextParameter(10000);
|
||||
* insert.setNextParameter("hello");
|
||||
* insert.setNextParameter("rob");
|
||||
* insert.addBatch();
|
||||
*
|
||||
* insert.setNextParameter(10001);
|
||||
* insert.setNextParameter("goodbye");
|
||||
* insert.setNextParameter("rob");
|
||||
* insert.addBatch();
|
||||
*
|
||||
* insert.setNextParameter(10002);
|
||||
* insert.setNextParameter("chow");
|
||||
* insert.setNextParameter("bob");
|
||||
* insert.addBatch();
|
||||
*
|
||||
* int[] rows = insert.executeBatch();
|
||||
*
|
||||
* txn.commit();
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
@@ -83,6 +112,18 @@ public interface SqlUpdate {
|
||||
*/
|
||||
int execute();
|
||||
|
||||
/**
|
||||
* Execute when addBatch() has been used to batch multiple bind executions.
|
||||
*
|
||||
* @return The row counts for each of the batched statements.
|
||||
*/
|
||||
int[] executeBatch();
|
||||
|
||||
/**
|
||||
* Add the statement to batch processing to then later execute via executeBatch().
|
||||
*/
|
||||
void addBatch();
|
||||
|
||||
/**
|
||||
* Return the generated key value.
|
||||
*/
|
||||
|
||||
@@ -493,6 +493,11 @@ public class ServerConfig {
|
||||
*/
|
||||
private List<String> mappingLocations = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* When true we do not need explicit GeneratedValue mapping.
|
||||
*/
|
||||
private boolean idGeneratorAutomatic = true;
|
||||
|
||||
/**
|
||||
* Construct a Server Configuration for programmatically creating an EbeanServer.
|
||||
*/
|
||||
@@ -1967,7 +1972,7 @@ public class ServerConfig {
|
||||
* This is the same as serverConfig.getMigrationConfig().setRunMigration(). We have added this method here
|
||||
* as it is often the only thing we need to configure for migrations.
|
||||
*/
|
||||
public void setRunMigration(boolean runMigration){
|
||||
public void setRunMigration(boolean runMigration) {
|
||||
migrationConfig.setRunMigration(runMigration);
|
||||
}
|
||||
|
||||
@@ -2772,6 +2777,7 @@ public class ServerConfig {
|
||||
useJtaTransactionManager = p.getBoolean("useJtaTransactionManager", useJtaTransactionManager);
|
||||
useJavaxValidationNotNull = p.getBoolean("useJavaxValidationNotNull", useJavaxValidationNotNull);
|
||||
autoReadOnlyDataSource = p.getBoolean("autoReadOnlyDataSource", autoReadOnlyDataSource);
|
||||
idGeneratorAutomatic = p.getBoolean("idGeneratorAutomatic", idGeneratorAutomatic);
|
||||
|
||||
backgroundExecutorSchedulePoolSize = p.getInt("backgroundExecutorSchedulePoolSize", backgroundExecutorSchedulePoolSize);
|
||||
backgroundExecutorShutdownSecs = p.getInt("backgroundExecutorShutdownSecs", backgroundExecutorShutdownSecs);
|
||||
@@ -3109,6 +3115,23 @@ public class ServerConfig {
|
||||
this.mappingLocations = mappingLocations;
|
||||
}
|
||||
|
||||
/**
|
||||
* When false we need explicit <code>@GeneratedValue</code> mapping to assign
|
||||
* Identity or Sequence generated values. When true Id properties are automatically
|
||||
* assigned Identity or Sequence without the GeneratedValue mapping.
|
||||
*/
|
||||
public boolean isIdGeneratorAutomatic() {
|
||||
return idGeneratorAutomatic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to false such that Id properties require explicit <code>@GeneratedValue</code>
|
||||
* mapping before they are assigned Identity or Sequence generation based on platform.
|
||||
*/
|
||||
public void setIdGeneratorAutomatic(boolean idGeneratorAutomatic) {
|
||||
this.idGeneratorAutomatic = idGeneratorAutomatic;
|
||||
}
|
||||
|
||||
public enum UuidVersion {
|
||||
VERSION4,
|
||||
VERSION1,
|
||||
|
||||
@@ -18,22 +18,22 @@ import java.util.Set;
|
||||
*/
|
||||
class LoadContext {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(LoadContext.class);
|
||||
private static final Logger log = LoggerFactory.getLogger(LoadContext.class);
|
||||
|
||||
/**
|
||||
* Map we are loading the properties into.
|
||||
*/
|
||||
private final Map<String, String> map = new LinkedHashMap<>();
|
||||
private final Map<String, String> map = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* Names of resources/files that were loaded.
|
||||
*/
|
||||
private final Set<String> loadedResources = new LinkedHashSet<>();
|
||||
private final Set<String> loadedResources = new LinkedHashSet<>();
|
||||
|
||||
/**
|
||||
* Return the input stream (maybe null) for the given source.
|
||||
*/
|
||||
InputStream resource(String resourcePath, Loader.Source source) {
|
||||
InputStream resource(String resourcePath, Loader.Source source) {
|
||||
|
||||
InputStream is = null;
|
||||
if (source == Loader.Source.RESOURCE) {
|
||||
@@ -46,21 +46,21 @@ class LoadContext {
|
||||
if (file.exists()) {
|
||||
try {
|
||||
is = new FileInputStream(file);
|
||||
loadedResources.add("file:"+resourcePath);
|
||||
loadedResources.add("file:" + resourcePath);
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return is;
|
||||
}
|
||||
return is;
|
||||
}
|
||||
|
||||
private InputStream resourceStream(String resourcePath) {
|
||||
InputStream is = getClass().getResourceAsStream("/" + resourcePath);
|
||||
if (is == null) {
|
||||
// search the module path for top level resource
|
||||
is = ClassLoader.getSystemResourceAsStream(resourcePath);
|
||||
is = ClassLoader.getSystemResourceAsStream(resourcePath);
|
||||
}
|
||||
return is;
|
||||
}
|
||||
@@ -68,30 +68,30 @@ class LoadContext {
|
||||
/**
|
||||
* Add a property entry.
|
||||
*/
|
||||
void put(String key, String val) {
|
||||
if (val != null) {
|
||||
val = val.trim();
|
||||
void put(String key, String val) {
|
||||
if (val != null) {
|
||||
val = val.trim();
|
||||
}
|
||||
map.put(key, val);
|
||||
}
|
||||
map.put(key, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate all the expressions and return as a Properties object.
|
||||
*/
|
||||
Properties eval() {
|
||||
Properties eval() {
|
||||
|
||||
log.info("loaded properties from {}", loadedResources);
|
||||
log.info("loaded properties from {}", loadedResources);
|
||||
|
||||
Properties properties = new Properties();
|
||||
Properties properties = new Properties();
|
||||
|
||||
for (Map.Entry<String, String> entry : map.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = PropertyEval.eval(entry.getValue());
|
||||
properties.setProperty(key, value);
|
||||
}
|
||||
for (Map.Entry<String, String> entry : map.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = PropertyEval.eval(entry.getValue());
|
||||
properties.setProperty(key, value);
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the special properties that can point to an external properties source.
|
||||
|
||||
@@ -16,10 +16,10 @@ import java.util.Properties;
|
||||
*/
|
||||
class Loader {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(Loader.class);
|
||||
private static final Logger log = LoggerFactory.getLogger(Loader.class);
|
||||
|
||||
enum Source {
|
||||
RESOURCE,
|
||||
enum Source {
|
||||
RESOURCE,
|
||||
FILE
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ class Loader {
|
||||
|
||||
private YamlLoader yamlLoader;
|
||||
|
||||
Loader() {
|
||||
Loader() {
|
||||
String skipYaml = System.getProperty("ebeanSkipYaml");
|
||||
if (!"true".equals(skipYaml)) {
|
||||
try {
|
||||
@@ -44,19 +44,19 @@ class Loader {
|
||||
/**
|
||||
* Load the configuration with the expected ordering.
|
||||
*/
|
||||
void load() {
|
||||
void load() {
|
||||
|
||||
loadMain(Source.RESOURCE);
|
||||
// external file configuration overrides the resources configuration
|
||||
loadMain(Source.FILE);
|
||||
|
||||
loadViaSystemProperty();
|
||||
loadViaIndirection();
|
||||
loadViaSystemProperty();
|
||||
loadViaIndirection();
|
||||
|
||||
// test configuration (if found) overrides main configuration
|
||||
// test configuration (if found) overrides main configuration
|
||||
// we should only find these resources when running tests
|
||||
loadTest();
|
||||
}
|
||||
loadTest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load test configuration.
|
||||
@@ -89,7 +89,7 @@ class Loader {
|
||||
}
|
||||
|
||||
private void loadViaSystemProperty() {
|
||||
String fileName = System.getenv("EBEAN_PROPS_FILE");
|
||||
String fileName = System.getenv("EBEAN_PROPS_FILE");
|
||||
if (fileName == null) {
|
||||
fileName = System.getProperty("ebean.props.file");
|
||||
if (fileName != null) {
|
||||
@@ -112,59 +112,66 @@ class Loader {
|
||||
* Evaluate all the configuration entries and return as properties.
|
||||
*/
|
||||
Properties eval() {
|
||||
return loadContext.eval();
|
||||
}
|
||||
return loadContext.eval();
|
||||
}
|
||||
|
||||
void loadYaml(String resourcePath, Source source) {
|
||||
if (yamlLoader != null) {
|
||||
try {
|
||||
yamlLoader.load(resource(resourcePath, source));
|
||||
void loadYaml(String resourcePath, Source source) {
|
||||
if (yamlLoader != null) {
|
||||
try {
|
||||
try (InputStream is = resource(resourcePath, source)) {
|
||||
yamlLoader.load(is);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to read yml from:" + resourcePath, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void loadProperties(String resourcePath, Source source) {
|
||||
InputStream is = resource(resourcePath, source);
|
||||
if (is != null) {
|
||||
loadProperties(is);
|
||||
}
|
||||
}
|
||||
void loadProperties(String resourcePath, Source source) {
|
||||
try {
|
||||
try (InputStream is = resource(resourcePath, source)) {
|
||||
if (is != null) {
|
||||
loadProperties(is);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to read properties from:" + resourcePath, e);
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream resource(String resourcePath, Source source) {
|
||||
return loadContext.resource(resourcePath, source);
|
||||
}
|
||||
private InputStream resource(String resourcePath, Source source) {
|
||||
return loadContext.resource(resourcePath, source);
|
||||
}
|
||||
|
||||
private void loadProperties(InputStream is) {
|
||||
private void loadProperties(InputStream is) {
|
||||
|
||||
if (is != null) {
|
||||
try {
|
||||
Properties properties = new Properties();
|
||||
properties.load(is);
|
||||
put(properties);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to load properties?", e);
|
||||
} finally {
|
||||
close(is);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is != null) {
|
||||
try {
|
||||
Properties properties = new Properties();
|
||||
properties.load(is);
|
||||
put(properties);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to load properties?", e);
|
||||
} finally {
|
||||
close(is);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void put(Properties properties) {
|
||||
Enumeration<?> enumeration = properties.propertyNames();
|
||||
while (enumeration.hasMoreElements()) {
|
||||
String key = (String) enumeration.nextElement();
|
||||
String property = properties.getProperty(key);
|
||||
loadContext.put(key, property);
|
||||
}
|
||||
}
|
||||
private void put(Properties properties) {
|
||||
Enumeration<?> enumeration = properties.propertyNames();
|
||||
while (enumeration.hasMoreElements()) {
|
||||
String key = (String) enumeration.nextElement();
|
||||
String property = properties.getProperty(key);
|
||||
loadContext.put(key, property);
|
||||
}
|
||||
}
|
||||
|
||||
private void close(InputStream is) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
log.warn("Error closing input stream for properties", e);
|
||||
}
|
||||
}
|
||||
private void close(InputStream is) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
log.warn("Error closing input stream for properties", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import io.ebean.DtoQuery;
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.PersistenceContextScope;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.RowConsumer;
|
||||
import io.ebean.RowMapper;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.TxScope;
|
||||
import io.ebean.bean.BeanCollectionLoader;
|
||||
@@ -31,6 +33,11 @@ import java.util.function.Predicate;
|
||||
*/
|
||||
public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionLoader {
|
||||
|
||||
/**
|
||||
* Return the log manager.
|
||||
*/
|
||||
SpiLogManager log();
|
||||
|
||||
/**
|
||||
* Return the server extended Json context.
|
||||
*/
|
||||
@@ -229,6 +236,31 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
|
||||
*/
|
||||
void scopedTransactionExit(Object returnOrThrowable, int opCode);
|
||||
|
||||
/**
|
||||
* SqlQuery find single attribute.
|
||||
*/
|
||||
<T> T findSingleAttribute(SpiSqlQuery query, Class<T> cls);
|
||||
|
||||
/**
|
||||
* SqlQuery find single attribute list.
|
||||
*/
|
||||
<T> List<T> findSingleAttributeList(SpiSqlQuery query, Class<T> cls);
|
||||
|
||||
/**
|
||||
* SqlQuery find one with mapper.
|
||||
*/
|
||||
<T> T findOneMapper(SpiSqlQuery query, RowMapper<T> mapper);
|
||||
|
||||
/**
|
||||
* SqlQuery find list with mapper.
|
||||
*/
|
||||
<T> List<T> findListMapper(SpiSqlQuery query, RowMapper<T> mapper);
|
||||
|
||||
/**
|
||||
* SqlQuery find each with consumer.
|
||||
*/
|
||||
void findEachRow(SpiSqlQuery query, RowConsumer consumer);
|
||||
|
||||
/**
|
||||
* DTO findList query.
|
||||
*/
|
||||
@@ -268,4 +300,15 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
|
||||
* Return true if a row for the bean type and id exists.
|
||||
*/
|
||||
boolean exists(Class<?> beanType, Object beanId, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Add to JDBC batch for later execution.
|
||||
*/
|
||||
void addBatch(SpiSqlUpdate defaultSqlUpdate, SpiTransaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the batched statement.
|
||||
*/
|
||||
int[] executeBatch(SpiSqlUpdate defaultSqlUpdate, SpiTransaction transaction);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* Log manager for SQL, TXN and Summary logging.
|
||||
* <p>
|
||||
* In general at runtime this uses SLF4J Logger but this abstraction allows us to capture
|
||||
* the logged SQL during testing such that we can assert against the executed sql if desired.
|
||||
* </p>
|
||||
*/
|
||||
public interface SpiLogManager {
|
||||
|
||||
/**
|
||||
* Return the SQL logger.
|
||||
*/
|
||||
SpiLogger sql();
|
||||
|
||||
/**
|
||||
* Return the TXN logger.
|
||||
*/
|
||||
SpiLogger txn();
|
||||
|
||||
/**
|
||||
* Return the Summary logger.
|
||||
*/
|
||||
SpiLogger sum();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* Logger for SQL, TXN and Summary logging.
|
||||
* <p>
|
||||
* In general at runtime this uses SLF4J Logger but this abstraction allows us to capture
|
||||
* the logged SQL during testing such that we can assert against the executed sql if desired.
|
||||
* </p>
|
||||
*/
|
||||
public interface SpiLogger {
|
||||
|
||||
/**
|
||||
* Is debug logging enabled.
|
||||
*/
|
||||
boolean isDebug();
|
||||
|
||||
/**
|
||||
* Is trace logging enabled.
|
||||
*/
|
||||
boolean isTrace();
|
||||
|
||||
/**
|
||||
* Log a debug level message.
|
||||
*/
|
||||
void debug(String msg);
|
||||
|
||||
/**
|
||||
* Log a trace level message.
|
||||
*/
|
||||
void trace(String msg);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
/**
|
||||
* API for Plugins to define the logger for io.ebean.SQL, io.ebean.TXN and io.ebean.SUM.
|
||||
*/
|
||||
public interface SpiLoggerFactory {
|
||||
|
||||
/**
|
||||
* Create the logger given the name.
|
||||
*/
|
||||
SpiLogger create(String name);
|
||||
}
|
||||
@@ -644,9 +644,9 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
boolean isBeanCachePut();
|
||||
|
||||
/**
|
||||
* Return true if the bean cache is being explicitly loaded via RECACHE mode.
|
||||
* Return true if we must hit the DB (Cache reload or select for update).
|
||||
*/
|
||||
boolean isBeanCacheReload();
|
||||
boolean isForceHitDatabase();
|
||||
|
||||
/**
|
||||
* Return the cache mode for using the bean cache (Get and Put).
|
||||
|
||||
@@ -2,10 +2,10 @@ package io.ebeaninternal.dbmigration;
|
||||
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebeaninternal.dbmigration.model.CurrentModel;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.extraddl.model.ExtraDdlXmlReader;
|
||||
import io.ebean.migration.ddl.DdlRunner;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.dbmigration.model.CurrentModel;
|
||||
import io.ebeaninternal.extraddl.model.ExtraDdlXmlReader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -173,10 +173,11 @@ public class DdlGenerator {
|
||||
protected void runResourceScript(String sqlScript) throws IOException {
|
||||
|
||||
if (sqlScript != null) {
|
||||
InputStream is = getClassLoader().getResourceAsStream(sqlScript);
|
||||
if (is != null) {
|
||||
String content = readContent(new InputStreamReader(is));
|
||||
runScript(false, content, sqlScript);
|
||||
try (InputStream is = getClassLoader().getResourceAsStream(sqlScript)) {
|
||||
if (is != null) {
|
||||
String content = readContent(new InputStreamReader(is));
|
||||
runScript(false, content, sqlScript);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,12 +24,14 @@ public class MigrationXmlReader {
|
||||
*/
|
||||
public static Migration read(String resourcePath) {
|
||||
|
||||
InputStream is = MigrationXmlReader.class.getResourceAsStream(resourcePath);
|
||||
if (is == null) {
|
||||
throw new IllegalArgumentException("No resource found for path [" + resourcePath + "]");
|
||||
try (InputStream is = MigrationXmlReader.class.getResourceAsStream(resourcePath)) {
|
||||
if (is == null) {
|
||||
throw new IllegalArgumentException("No resource found for path [" + resourcePath + "]");
|
||||
}
|
||||
return read(is);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException("Failed to auto close resource " + resourcePath, e);
|
||||
}
|
||||
|
||||
return read(is);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+7
-4
@@ -5,6 +5,7 @@ import io.ebeaninternal.dbmigration.model.MTable;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.PropertyForeignKey;
|
||||
import io.ebeaninternal.server.deploy.TableJoin;
|
||||
import io.ebeaninternal.server.deploy.TableJoinColumn;
|
||||
|
||||
@@ -48,10 +49,12 @@ class ModelBuildIntersectionTable {
|
||||
|
||||
private void buildFkConstraints() {
|
||||
|
||||
ctx.fkeyBuilder(intersectionTable)
|
||||
.addForeignKey(manyProp.getBeanDescriptor(), intersectionTableJoin, true)
|
||||
.addForeignKey(manyProp.getTargetDescriptor(), tableJoin, false);
|
||||
|
||||
PropertyForeignKey foreignKey = manyProp.getForeignKey();
|
||||
if (foreignKey == null || !foreignKey.isNoConstraint()) {
|
||||
ctx.fkeyBuilder(intersectionTable)
|
||||
.addForeignKey(manyProp.getBeanDescriptor(), intersectionTableJoin, true)
|
||||
.addForeignKey(manyProp.getTargetDescriptor(), tableJoin, false);
|
||||
}
|
||||
intersectionTable.checkDuplicateForeignKeys();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import org.slf4j.LoggerFactory;
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
@@ -87,12 +88,15 @@ public class ExtraDdlXmlReader {
|
||||
*/
|
||||
public static ExtraDdl read(String resourcePath) {
|
||||
|
||||
InputStream is = ExtraDdlXmlReader.class.getResourceAsStream(resourcePath);
|
||||
if (is == null) {
|
||||
// we expect this and check for null
|
||||
return null;
|
||||
try (InputStream is = ExtraDdlXmlReader.class.getResourceAsStream(resourcePath)) {
|
||||
if (is == null) {
|
||||
// we expect this and check for null
|
||||
return null;
|
||||
}
|
||||
return read(is);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Error on auto close of " + resourcePath, e);
|
||||
}
|
||||
return read(is);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -144,7 +144,7 @@ public class BaseQueryTuner {
|
||||
default:
|
||||
// not using autoTune when explicitly loading the l2 bean cache
|
||||
// or when using Versions query
|
||||
return !query.isBeanCacheReload() && SpiQuery.TemporalMode.VERSIONS != query.getTemporalMode();
|
||||
return !query.isForceHitDatabase() && SpiQuery.TemporalMode.VERSIONS != query.getTemporalMode();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -106,11 +107,14 @@ public class DefaultAutoTuneService implements AutoTuneService {
|
||||
loadAutoTuneProfiling(AutoTuneXmlReader.read(file));
|
||||
} else {
|
||||
// look for autotune as a resource
|
||||
InputStream stream = getClass().getResourceAsStream("/" + tuningFile);
|
||||
if (stream != null) {
|
||||
loadAutoTuneProfiling(AutoTuneXmlReader.read(stream));
|
||||
} else {
|
||||
logger.warn("AutoTune file {} not found - no initial automatic query tuning", tuningFile);
|
||||
try (InputStream stream = getClass().getResourceAsStream("/" + tuningFile)) {
|
||||
if (stream != null) {
|
||||
loadAutoTuneProfiling(AutoTuneXmlReader.read(stream));
|
||||
} else {
|
||||
logger.warn("AutoTune file {} not found - no initial automatic query tuning", tuningFile);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Error on auto close of " + tuningFile, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,11 +170,7 @@ public abstract class AbstractSqlQueryRequest {
|
||||
}
|
||||
|
||||
if (isLogSql()) {
|
||||
String logSql = TrimLogSql.trim(sql);
|
||||
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
|
||||
logSql = Str.add(logSql, "; --bind(", bindLog, ")");
|
||||
}
|
||||
trans.logSql(logSql);
|
||||
trans.logSql(Str.add(TrimLogSql.trim(sql), "; --bind(", bindLog, ")"));
|
||||
}
|
||||
|
||||
setResultSet(pstmt.executeQuery(), null);
|
||||
|
||||
@@ -14,7 +14,7 @@ import io.ebean.config.dbplatform.sqlanywhere.SqlAnywherePlatform;
|
||||
import io.ebean.config.dbplatform.sqlite.SQLitePlatform;
|
||||
import io.ebean.config.dbplatform.sqlserver.SqlServer16Platform;
|
||||
import io.ebean.config.dbplatform.sqlserver.SqlServer17Platform;
|
||||
import io.ebean.migration.util.JdbcClose;
|
||||
import io.ebean.util.JdbcClose;
|
||||
import io.ebeaninternal.dbmigration.DbOffline;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -126,13 +126,7 @@ public class DatabasePlatformFactory {
|
||||
throw new PersistenceException(ex);
|
||||
|
||||
} finally {
|
||||
try {
|
||||
if (connection != null) {
|
||||
connection.close();
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logger.error(null, ex);
|
||||
}
|
||||
JdbcClose.close(connection);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import io.ebean.config.TenantMode;
|
||||
import io.ebean.config.UnderscoreNamingConvention;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.h2.H2Platform;
|
||||
import io.ebean.config.properties.PropertiesLoader;
|
||||
import io.ebean.service.SpiContainer;
|
||||
import io.ebeaninternal.api.SpiBackgroundExecutor;
|
||||
import io.ebeaninternal.api.SpiContainerBootup;
|
||||
@@ -374,25 +373,14 @@ public class DefaultContainer implements SpiContainer {
|
||||
throw new RuntimeException("DataSource not set?");
|
||||
}
|
||||
|
||||
Connection c = null;
|
||||
try {
|
||||
c = serverConfig.getDataSource().getConnection();
|
||||
if (!serverConfig.isAutoCommitMode() && c.getAutoCommit()) {
|
||||
try (Connection connection = serverConfig.getDataSource().getConnection()) {
|
||||
if (!serverConfig.isAutoCommitMode() && connection.getAutoCommit()) {
|
||||
logger.warn("DataSource [{}] has autoCommit defaulting to true!", serverConfig.getName());
|
||||
}
|
||||
return true;
|
||||
|
||||
} catch (SQLException ex) {
|
||||
throw new PersistenceException(ex);
|
||||
|
||||
} finally {
|
||||
if (c != null) {
|
||||
try {
|
||||
c.close();
|
||||
} catch (SQLException ex) {
|
||||
logger.error("Error closing connection", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,11 +13,14 @@ import io.ebean.FutureIds;
|
||||
import io.ebean.FutureList;
|
||||
import io.ebean.FutureRowCount;
|
||||
import io.ebean.MergeOptions;
|
||||
import io.ebean.MergeOptionsBuilder;
|
||||
import io.ebean.PagedList;
|
||||
import io.ebean.PersistenceContextScope;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.QueryIterator;
|
||||
import io.ebean.RowConsumer;
|
||||
import io.ebean.RowMapper;
|
||||
import io.ebean.SqlQuery;
|
||||
import io.ebean.SqlRow;
|
||||
import io.ebean.SqlUpdate;
|
||||
@@ -63,8 +66,11 @@ import io.ebeaninternal.api.SpiBackgroundExecutor;
|
||||
import io.ebeaninternal.api.SpiDtoQuery;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiJsonContext;
|
||||
import io.ebeaninternal.api.SpiLogManager;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiQuery.Type;
|
||||
import io.ebeaninternal.api.SpiSqlQuery;
|
||||
import io.ebeaninternal.api.SpiSqlUpdate;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.SpiTransactionManager;
|
||||
import io.ebeaninternal.api.TransactionEventTable;
|
||||
@@ -189,6 +195,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
private final CurrentTenantProvider currentTenantProvider;
|
||||
|
||||
private final SpiLogManager logManager;
|
||||
|
||||
/**
|
||||
* The default PersistenceContextScope used if it is not explicitly set on a query.
|
||||
*/
|
||||
@@ -229,6 +237,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
*/
|
||||
public DefaultServer(InternalConfiguration config, ServerCacheManager cache) {
|
||||
|
||||
this.logManager = config.getLogManager();
|
||||
this.dtoBeanManager = config.getDtoBeanManager();
|
||||
this.serverConfig = config.getServerConfig();
|
||||
this.objectGraphStats = new ConcurrentHashMap<>();
|
||||
@@ -315,6 +324,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiLogManager log() {
|
||||
return logManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCollectQueryOrigins() {
|
||||
return collectQueryOrigins;
|
||||
@@ -919,6 +933,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return new DefaultUpdateQuery<>(createQuery(beanType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void merge(Object bean) {
|
||||
merge(bean, MergeOptionsBuilder.defaultOptions(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void merge(Object bean, MergeOptions options) {
|
||||
merge(bean, options, null);
|
||||
@@ -1159,7 +1178,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
|
||||
spiQuery.setType(Type.BEAN);
|
||||
if (SpiQuery.Mode.NORMAL == spiQuery.getMode() && !spiQuery.isBeanCacheReload()) {
|
||||
if (SpiQuery.Mode.NORMAL == spiQuery.getMode() && !spiQuery.isForceHitDatabase()) {
|
||||
// See if we can skip doing the fetch completely by getting the bean from the
|
||||
// persistence context or the bean cache
|
||||
T bean = findIdCheckPersistenceContextAndCache(t, spiQuery, spiQuery.getId());
|
||||
@@ -1278,8 +1297,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
@Override
|
||||
public <T> int findCount(Query<T> query, Transaction t) {
|
||||
|
||||
SpiQuery<T> copy = ((SpiQuery<T>) query).copy();
|
||||
return findCountWithCopy(copy, t);
|
||||
SpiQuery<T> spiQuery = ((SpiQuery<T>) query);
|
||||
if (!spiQuery.isDistinct()) {
|
||||
spiQuery = spiQuery.copy();
|
||||
}
|
||||
return findCountWithCopy(spiQuery, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1566,6 +1588,41 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
private <P> P executeSqlQuery(Function<RelationalQueryRequest, P> fun, SpiSqlQuery query) {
|
||||
RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, null);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return fun.apply(request);
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findEachRow(SpiSqlQuery query, RowConsumer consumer) {
|
||||
executeSqlQuery((req) -> req.findEachRow(consumer), query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> findListMapper(SpiSqlQuery query, RowMapper<T> mapper) {
|
||||
return executeSqlQuery((req) -> req.findListMapper(mapper), query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T findOneMapper(SpiSqlQuery query, RowMapper<T> mapper) {
|
||||
return executeSqlQuery((req) -> req.findOneMapper(mapper), query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> findSingleAttributeList(SpiSqlQuery query, Class<T> cls) {
|
||||
return executeSqlQuery((req) -> req.findSingleAttributeList(cls), query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T findSingleAttribute(SpiSqlQuery query, Class<T> cls) {
|
||||
return executeSqlQuery((req) -> req.findSingleAttribute(cls), query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void findDtoEach(SpiDtoQuery<T> query, Consumer<T> consumer) {
|
||||
DtoQueryRequest<T> request = new DtoQueryRequest<>(this, dtoQueryEngine, query);
|
||||
@@ -1961,6 +2018,16 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return persister.executeSqlUpdate(updSql, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBatch(SpiSqlUpdate sqlUpdate, SpiTransaction transaction) {
|
||||
persister.addBatch(sqlUpdate, transaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] executeBatch(SpiSqlUpdate sqlUpdate, SpiTransaction transaction) {
|
||||
return persister.executeBatch(sqlUpdate, transaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the updateSql.
|
||||
*/
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebean.Update;
|
||||
import io.ebeaninternal.api.BindParams;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiSqlUpdate;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@@ -26,7 +27,7 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
|
||||
|
||||
private static final long serialVersionUID = -6493829438421253102L;
|
||||
|
||||
private transient final EbeanServer server;
|
||||
private transient final SpiEbeanServer server;
|
||||
|
||||
/**
|
||||
* The parameters used to bind to the sql.
|
||||
@@ -69,6 +70,16 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
|
||||
|
||||
private Object generatedKey;
|
||||
|
||||
/**
|
||||
* Set when batching explicitly used.
|
||||
*/
|
||||
private boolean batched;
|
||||
|
||||
/**
|
||||
* Transaction used for addBatch() executeBatch() processing.
|
||||
*/
|
||||
private transient SpiTransaction transaction;
|
||||
|
||||
/**
|
||||
* Create with server sql and bindParams object.
|
||||
* <p>
|
||||
@@ -76,7 +87,7 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
|
||||
* same time.
|
||||
* </p>
|
||||
*/
|
||||
public DefaultSqlUpdate(EbeanServer server, String sql, BindParams bindParams) {
|
||||
public DefaultSqlUpdate(SpiEbeanServer server, String sql, BindParams bindParams) {
|
||||
this.server = server;
|
||||
this.sql = sql;
|
||||
this.bindParams = bindParams;
|
||||
@@ -86,7 +97,7 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
|
||||
* Create with a specific server. This means you can use the
|
||||
* SqlUpdate.execute() method.
|
||||
*/
|
||||
public DefaultSqlUpdate(EbeanServer server, String sql) {
|
||||
public DefaultSqlUpdate(SpiEbeanServer server, String sql) {
|
||||
this(server, sql, new BindParams());
|
||||
}
|
||||
|
||||
@@ -111,6 +122,10 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
|
||||
@Override
|
||||
public int execute() {
|
||||
if (server != null) {
|
||||
if (batched) {
|
||||
server.executeBatch(this, transaction);
|
||||
return -1;
|
||||
}
|
||||
return server.execute(this);
|
||||
} else {
|
||||
// Hopefully this doesn't catch anyone out...
|
||||
@@ -118,6 +133,34 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] executeBatch() {
|
||||
if (server == null) {
|
||||
throw new IllegalStateException("No EbeanServer set?");
|
||||
}
|
||||
if (!batched) {
|
||||
throw new IllegalStateException("No prior addBatch() called?");
|
||||
}
|
||||
return server.executeBatch(this, transaction);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addBatch() {
|
||||
if (server == null) {
|
||||
throw new IllegalStateException("No EbeanServer set?");
|
||||
}
|
||||
if (transaction == null) {
|
||||
transaction = server.currentServerTransaction();
|
||||
if (transaction == null) {
|
||||
throw new IllegalStateException("No current transaction? Must have a transaction to use addBatch()");
|
||||
}
|
||||
}
|
||||
|
||||
batched = true;
|
||||
server.addBatch(this, transaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGeneratedKey() {
|
||||
return generatedKey;
|
||||
|
||||
@@ -20,6 +20,9 @@ import io.ebean.plugin.SpiServer;
|
||||
import io.ebeaninternal.api.SpiBackgroundExecutor;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiJsonContext;
|
||||
import io.ebeaninternal.api.SpiLogManager;
|
||||
import io.ebeaninternal.api.SpiLogger;
|
||||
import io.ebeaninternal.api.SpiLoggerFactory;
|
||||
import io.ebeaninternal.api.SpiProfileHandler;
|
||||
import io.ebeaninternal.dbmigration.DbOffline;
|
||||
import io.ebeaninternal.server.autotune.AutoTuneService;
|
||||
@@ -44,6 +47,8 @@ import io.ebeaninternal.server.dto.DtoBeanManager;
|
||||
import io.ebeaninternal.server.expression.DefaultExpressionFactory;
|
||||
import io.ebeaninternal.server.expression.platform.DbExpressionHandler;
|
||||
import io.ebeaninternal.server.expression.platform.DbExpressionHandlerFactory;
|
||||
import io.ebeaninternal.server.logger.DLogManager;
|
||||
import io.ebeaninternal.server.logger.DLoggerFactory;
|
||||
import io.ebeaninternal.server.persist.Binder;
|
||||
import io.ebeaninternal.server.persist.DefaultPersister;
|
||||
import io.ebeaninternal.server.persist.platform.MultiValueBind;
|
||||
@@ -134,16 +139,19 @@ public class InternalConfiguration {
|
||||
|
||||
private final MultiValueBind multiValueBind;
|
||||
|
||||
private final SpiLogManager logManager;
|
||||
|
||||
public InternalConfiguration(ClusterManager clusterManager,
|
||||
SpiCacheManager cacheManager, SpiBackgroundExecutor backgroundExecutor,
|
||||
ServerConfig serverConfig, BootupClasses bootupClasses) {
|
||||
|
||||
this.serverConfig = serverConfig;
|
||||
this.logManager = initLogManager();
|
||||
this.docStoreFactory = initDocStoreFactory(serverConfig.service(DocStoreFactory.class));
|
||||
this.jsonFactory = serverConfig.getJsonFactory();
|
||||
this.clusterManager = clusterManager;
|
||||
this.backgroundExecutor = backgroundExecutor;
|
||||
this.cacheManager = cacheManager;
|
||||
this.serverConfig = serverConfig;
|
||||
this.bootupClasses = bootupClasses;
|
||||
|
||||
this.databasePlatform = serverConfig.getDatabasePlatform();
|
||||
@@ -169,6 +177,20 @@ public class InternalConfiguration {
|
||||
this.cQueryEngine = new CQueryEngine(serverConfig, databasePlatform, binder, asOfTableMapping, draftTableMap);
|
||||
}
|
||||
|
||||
private SpiLogManager initLogManager() {
|
||||
|
||||
// allow plugin - i.e. capture executed SQL for testing/asserts
|
||||
SpiLoggerFactory loggerFactory = serverConfig.service(SpiLoggerFactory.class);
|
||||
if (loggerFactory == null) {
|
||||
loggerFactory = new DLoggerFactory();
|
||||
}
|
||||
|
||||
SpiLogger sql = loggerFactory.create("io.ebean.SQL");
|
||||
SpiLogger sum = loggerFactory.create("io.ebean.SUM");
|
||||
SpiLogger txn = loggerFactory.create("io.ebean.TXN");
|
||||
return new DLogManager(sql, sum, txn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return the ExpressionFactory based on configuration and database platform.
|
||||
*/
|
||||
@@ -262,9 +284,9 @@ public class InternalConfiguration {
|
||||
|
||||
DbHistorySupport historySupport = databasePlatform.getHistorySupport();
|
||||
if (historySupport == null) {
|
||||
return new Binder(typeManager, 0, false, jsonHandler, dataTimeZone, multiValueBind);
|
||||
return new Binder(typeManager, logManager, 0, false, jsonHandler, dataTimeZone, multiValueBind);
|
||||
}
|
||||
return new Binder(typeManager, historySupport.getBindCount(), historySupport.isStandardsBased(), jsonHandler, dataTimeZone, multiValueBind);
|
||||
return new Binder(typeManager, logManager, historySupport.getBindCount(), historySupport.isStandardsBased(), jsonHandler, dataTimeZone, multiValueBind);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -378,7 +400,7 @@ public class InternalConfiguration {
|
||||
|
||||
TransactionManagerOptions options =
|
||||
new TransactionManagerOptions(notifyL2CacheInForeground, serverConfig, scopeManager, clusterManager, backgroundExecutor,
|
||||
indexUpdateProcessor, beanDescriptorManager, dataSource(), profileHandler());
|
||||
indexUpdateProcessor, beanDescriptorManager, dataSource(), profileHandler(), logManager);
|
||||
|
||||
if (serverConfig.isExplicitTransactionBeginMode()) {
|
||||
return new ExplicitTransactionManager(options);
|
||||
@@ -511,4 +533,8 @@ public class InternalConfiguration {
|
||||
public DtoBeanManager getDtoBeanManager() {
|
||||
return dtoBeanManager;
|
||||
}
|
||||
|
||||
public SpiLogManager getLogManager() {
|
||||
return logManager;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,8 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
|
||||
private BeanPropertyAssocMany<?> manyProperty;
|
||||
|
||||
private boolean inlineCountDistinct;
|
||||
|
||||
/**
|
||||
* Create the InternalQueryRequest.
|
||||
*/
|
||||
@@ -742,4 +744,12 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
public void slowQueryCheck(long executionTimeMicros, int rowCount) {
|
||||
ebeanServer.slowQueryCheck(executionTimeMicros, rowCount, query);
|
||||
}
|
||||
|
||||
public void setInlineCountDistinct() {
|
||||
inlineCountDistinct = true;
|
||||
}
|
||||
|
||||
public boolean isInlineCountDistinct() {
|
||||
return inlineCountDistinct;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiSqlUpdate;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
@@ -27,15 +26,17 @@ public final class PersistRequestUpdateSql extends PersistRequest {
|
||||
|
||||
private String description;
|
||||
|
||||
private boolean addBatch;
|
||||
|
||||
/**
|
||||
* Create.
|
||||
*/
|
||||
public PersistRequestUpdateSql(SpiEbeanServer server, SqlUpdate sqlUpdate,
|
||||
public PersistRequestUpdateSql(SpiEbeanServer server, SpiSqlUpdate sqlUpdate,
|
||||
SpiTransaction t, PersistExecute persistExecute) {
|
||||
|
||||
super(server, t, persistExecute, sqlUpdate.getLabel());
|
||||
this.type = Type.UPDATESQL;
|
||||
this.updateSql = (SpiSqlUpdate) sqlUpdate;
|
||||
this.updateSql = sqlUpdate;
|
||||
updateSql.reset();
|
||||
}
|
||||
|
||||
@@ -44,11 +45,24 @@ public final class PersistRequestUpdateSql extends PersistRequest {
|
||||
profileBase(EVT_UPDATESQL, offset, (short)0, flushCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add this statement to JDBC batch for later execution.
|
||||
*/
|
||||
public int addBatch() {
|
||||
this.addBatch = true;
|
||||
return executeOrQueue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeNow() {
|
||||
return persistExecute.executeSqlUpdate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBatchThisRequest() {
|
||||
return addBatch || super.isBatchThisRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeOrQueue() {
|
||||
return executeStatement();
|
||||
|
||||
@@ -8,6 +8,7 @@ import io.ebean.Transaction;
|
||||
import io.ebean.Update;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.api.SpiSqlUpdate;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
@@ -91,4 +92,14 @@ public interface Persister {
|
||||
* Visit the metrics.
|
||||
*/
|
||||
void visitMetrics(MetricVisitor visitor);
|
||||
|
||||
/**
|
||||
* Add the statement to JDBC batch for later execution via executeBatch.
|
||||
*/
|
||||
void addBatch(SpiSqlUpdate sqlUpdate, SpiTransaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the associated batched statement.
|
||||
*/
|
||||
int[] executeBatch(SpiSqlUpdate sqlUpdate, SpiTransaction transaction);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
|
||||
import io.ebean.RowConsumer;
|
||||
import io.ebean.RowMapper;
|
||||
import io.ebean.SqlRow;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
|
||||
@@ -30,6 +32,31 @@ public interface RelationalQueryEngine {
|
||||
*/
|
||||
void findEach(RelationalQueryRequest request, Predicate<SqlRow> consumer);
|
||||
|
||||
/**
|
||||
* Find single attribute.
|
||||
*/
|
||||
<T> T findSingleAttribute(RelationalQueryRequest request, Class<T> cls);
|
||||
|
||||
/**
|
||||
* Find single attribute list.
|
||||
*/
|
||||
<T> List<T> findSingleAttributeList(RelationalQueryRequest request, Class<T> cls);
|
||||
|
||||
/**
|
||||
* Find one via mapper.
|
||||
*/
|
||||
<T> T findOneMapper(RelationalQueryRequest request, RowMapper<T> mapper);
|
||||
|
||||
/**
|
||||
* Find list via mapper.
|
||||
*/
|
||||
<T> List<T> findListMapper(RelationalQueryRequest request, RowMapper<T> mapper);
|
||||
|
||||
/**
|
||||
* Find each via raw consumer.
|
||||
*/
|
||||
void findEachRow(RelationalQueryRequest request, RowConsumer mapper);
|
||||
|
||||
/**
|
||||
* Collect SQL query execution statistics.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.RowConsumer;
|
||||
import io.ebean.RowMapper;
|
||||
import io.ebean.SqlQuery;
|
||||
import io.ebean.SqlRow;
|
||||
import io.ebean.Transaction;
|
||||
@@ -53,6 +55,27 @@ public final class RelationalQueryRequest extends AbstractSqlQueryRequest {
|
||||
}
|
||||
}
|
||||
|
||||
boolean findEachRow(RowConsumer mapper) {
|
||||
queryEngine.findEachRow(this, mapper);
|
||||
return true;
|
||||
}
|
||||
|
||||
<T> List<T> findListMapper(RowMapper<T> mapper) {
|
||||
return queryEngine.findListMapper(this, mapper);
|
||||
}
|
||||
|
||||
<T> T findOneMapper(RowMapper<T> mapper) {
|
||||
return queryEngine.findOneMapper(this, mapper);
|
||||
}
|
||||
|
||||
public <T> List<T> findSingleAttributeList(Class<T> cls) {
|
||||
return queryEngine.findSingleAttributeList(this, cls);
|
||||
}
|
||||
|
||||
public <T> T findSingleAttribute(Class<T> cls) {
|
||||
return queryEngine.findSingleAttribute(this, cls);
|
||||
}
|
||||
|
||||
public void findEach(Consumer<SqlRow> consumer) {
|
||||
queryEngine.findEach(this, consumer);
|
||||
}
|
||||
@@ -104,4 +127,35 @@ public final class RelationalQueryRequest extends AbstractSqlQueryRequest {
|
||||
}
|
||||
}
|
||||
|
||||
public ResultSet getResultSet() {
|
||||
return resultSet;
|
||||
}
|
||||
|
||||
public void incrementRows() {
|
||||
rows++;
|
||||
}
|
||||
|
||||
public <T> List<T> mapList(RowMapper<T> mapper) throws SQLException {
|
||||
|
||||
List<T> list = new ArrayList<>();
|
||||
while (next()) {
|
||||
list.add(mapper.map(resultSet, rows++));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public <T> T mapOne(RowMapper<T> mapper) throws SQLException {
|
||||
if (!next()) {
|
||||
return null;
|
||||
} else {
|
||||
return mapper.map(resultSet, rows++);
|
||||
}
|
||||
}
|
||||
|
||||
public void mapEach(RowConsumer consumer) throws SQLException {
|
||||
while (next()) {
|
||||
consumer.accept(resultSet, rows++);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -59,11 +59,8 @@ class ManifestReader {
|
||||
try {
|
||||
Enumeration<URL> resources = classLoader.getResources(resourcePath);
|
||||
while (resources.hasMoreElements()) {
|
||||
InputStream is = resources.nextElement().openStream();
|
||||
try {
|
||||
try (InputStream is = resources.nextElement().openStream()) {
|
||||
read(new Manifest(is));
|
||||
} finally {
|
||||
close(is);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
@@ -72,14 +69,6 @@ class ManifestReader {
|
||||
return packageSet;
|
||||
}
|
||||
|
||||
private void close(InputStream is) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
logger.warn("Error closing manifest InputStream", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the entity packages from the manifest.
|
||||
*/
|
||||
|
||||
@@ -152,6 +152,11 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
*/
|
||||
private final IdType idType;
|
||||
|
||||
/**
|
||||
* Set when Id property is marked with GeneratedValue annotation.
|
||||
*/
|
||||
private final boolean idGeneratedValue;
|
||||
|
||||
private final boolean idTypePlatformDefault;
|
||||
|
||||
private final PlatformIdGenerator idGenerator;
|
||||
@@ -450,6 +455,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
|
||||
this.defaultSelectClause = deploy.getDefaultSelectClause();
|
||||
this.idType = deploy.getIdType();
|
||||
this.idGeneratedValue = deploy.isIdGeneratedValue();
|
||||
this.idTypePlatformDefault = deploy.isIdTypePlatformDefault();
|
||||
this.idGenerator = deploy.getIdGenerator();
|
||||
this.sequenceName = deploy.getSequenceName();
|
||||
@@ -631,9 +637,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
*/
|
||||
public void setEbeanServer(SpiEbeanServer ebeanServer) {
|
||||
this.ebeanServer = ebeanServer;
|
||||
for (BeanPropertyAssocMany<?> aPropertiesMany : propertiesMany) {
|
||||
for (BeanPropertyAssocMany<?> assocMany : propertiesMany) {
|
||||
// used for creating lazy loading lists etc
|
||||
aPropertiesMany.setLoader(ebeanServer);
|
||||
assocMany.setEbeanServer(ebeanServer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1922,6 +1928,20 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return createEntityBean();
|
||||
}
|
||||
|
||||
/**
|
||||
* We actually need to do a query because we don't know the type without the discriminator
|
||||
* value, just select the id property and discriminator column (auto added)
|
||||
*/
|
||||
private T findReferenceBean(Object id, PersistenceContext pc) {
|
||||
DefaultOrmQuery<T> query = new DefaultOrmQuery<>(this, ebeanServer, ebeanServer.getExpressionFactory());
|
||||
query.setPersistenceContext(pc);
|
||||
return query
|
||||
// .select(getIdProperty().getName())
|
||||
// we do not select the id because we
|
||||
// probably have to load the entire bean
|
||||
.setId(id).findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a reference bean based on the id.
|
||||
*/
|
||||
@@ -1941,6 +1961,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (inheritInfo != null && !inheritInfo.isConcrete()) {
|
||||
return findReferenceBean(id, pc);
|
||||
}
|
||||
|
||||
EntityBean eb = createEntityBean();
|
||||
id = convertSetId(id, eb);
|
||||
|
||||
@@ -1974,10 +1998,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
|
||||
try {
|
||||
if (inheritInfo != null && !inheritInfo.isConcrete()) {
|
||||
// we actually need to do a query because we don't know the type without the discriminator
|
||||
// value, just select the id property and discriminator column (auto added)
|
||||
DefaultOrmQuery<T> query = new DefaultOrmQuery<>(this, ebeanServer, ebeanServer.getExpressionFactory());
|
||||
return query.select(getIdProperty().getName()).setId(id).findOne();
|
||||
return findReferenceBean(id, pc);
|
||||
}
|
||||
|
||||
EntityBean eb = createEntityBean();
|
||||
@@ -2984,6 +3005,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return idType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the Id value is marked as a <code>@GeneratedValue</code>.
|
||||
*/
|
||||
public boolean isIdGeneratedValue() {
|
||||
return idGeneratedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the identity is the platform default (not explicitly set).
|
||||
*/
|
||||
|
||||
@@ -1369,9 +1369,15 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
desc.setIdType(IdType.EXTERNAL);
|
||||
return;
|
||||
}
|
||||
// use the default. IDENTITY or SEQUENCE.
|
||||
desc.setIdType(dbIdentity.getIdType());
|
||||
desc.setIdTypePlatformDefault();
|
||||
if (desc.isIdGeneratedValue() || serverConfig.isIdGeneratorAutomatic()) {
|
||||
// use IDENTITY or SEQUENCE based on platform
|
||||
desc.setIdType(dbIdentity.getIdType());
|
||||
desc.setIdTypePlatformDefault();
|
||||
} else {
|
||||
// externally/application supplied Id values
|
||||
desc.setIdType(IdType.EXTERNAL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (desc.getBaseTable() == null) {
|
||||
@@ -1387,21 +1393,23 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
return;
|
||||
}
|
||||
|
||||
String seqName = desc.getIdGeneratorName();
|
||||
if (seqName != null) {
|
||||
logger.debug("explicit sequence {} on {}", seqName, desc.getFullName());
|
||||
} else {
|
||||
String primaryKeyColumn = desc.getSinglePrimaryKeyColumn();
|
||||
// use namingConvention to define sequence name
|
||||
seqName = namingConvention.getSequenceName(desc.getBaseTable(), primaryKeyColumn);
|
||||
}
|
||||
if (IdType.SEQUENCE == desc.getIdType()) {
|
||||
String seqName = desc.getIdGeneratorName();
|
||||
if (seqName != null) {
|
||||
logger.debug("explicit sequence {} on {}", seqName, desc.getFullName());
|
||||
} else {
|
||||
String primaryKeyColumn = desc.getSinglePrimaryKeyColumn();
|
||||
// use namingConvention to define sequence name
|
||||
seqName = namingConvention.getSequenceName(desc.getBaseTable(), primaryKeyColumn);
|
||||
}
|
||||
|
||||
if (databasePlatform.isSequenceBatchMode()) {
|
||||
// use sequence next step 1 as we are going to batch fetch them instead
|
||||
desc.setSequenceAllocationSize(1);
|
||||
if (databasePlatform.isSequenceBatchMode()) {
|
||||
// use sequence next step 1 as we are going to batch fetch them instead
|
||||
desc.setSequenceAllocationSize(1);
|
||||
}
|
||||
int stepSize = desc.getSequenceAllocationSize();
|
||||
desc.setIdGenerator(createSequenceIdGenerator(seqName, stepSize));
|
||||
}
|
||||
int stepSize = desc.getSequenceAllocationSize();
|
||||
desc.setIdGenerator(createSequenceIdGenerator(seqName, stepSize));
|
||||
}
|
||||
|
||||
private PlatformIdGenerator createSequenceIdGenerator(String seqName, int stepSize) {
|
||||
|
||||
@@ -78,6 +78,11 @@ public final class BeanFkeyProperty implements ElPropertyValue {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object localEncrypt(Object value) {
|
||||
throw new IllegalArgumentException("Should not get here?");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsFormulaWithJoin() {
|
||||
return false;
|
||||
|
||||
@@ -28,6 +28,7 @@ import io.ebeaninternal.server.query.STreeProperty;
|
||||
import io.ebeaninternal.server.query.SqlBeanLoad;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
import io.ebeaninternal.server.type.DataBind;
|
||||
import io.ebeaninternal.server.type.LocalEncryptedType;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
import io.ebeaninternal.server.type.ScalarTypeBoolean;
|
||||
import io.ebeaninternal.server.type.ScalarTypeEnum;
|
||||
@@ -1246,6 +1247,11 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
return dbBind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object localEncrypt(Object value) {
|
||||
return ((LocalEncryptedType)scalarType).localEncrypt(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if DB encrypted.
|
||||
*/
|
||||
|
||||
@@ -65,6 +65,8 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
|
||||
*/
|
||||
final TableJoin tableJoin;
|
||||
|
||||
final PropertyForeignKey foreignKey;
|
||||
|
||||
/**
|
||||
* The type of the joined bean.
|
||||
*/
|
||||
@@ -90,6 +92,7 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
|
||||
*/
|
||||
public BeanPropertyAssoc(BeanDescriptor<?> descriptor, DeployBeanPropertyAssoc<T> deploy) {
|
||||
super(descriptor, deploy);
|
||||
this.foreignKey = deploy.getForeignKey();
|
||||
this.extraWhere = InternString.intern(deploy.getExtraWhere());
|
||||
this.beanTable = deploy.getBeanTable();
|
||||
this.mappedBy = InternString.intern(deploy.getMappedBy());
|
||||
@@ -128,6 +131,13 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STree
|
||||
return fetchPreference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the extra configuration for the foreign key.
|
||||
*/
|
||||
public PropertyForeignKey getForeignKey() {
|
||||
return foreignKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ElPropertyValue for a *ToOne or *ToMany.
|
||||
*/
|
||||
|
||||
@@ -7,10 +7,10 @@ import io.ebean.Transaction;
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.BeanCollection.ModifyListenMode;
|
||||
import io.ebean.bean.BeanCollectionAdd;
|
||||
import io.ebean.bean.BeanCollectionLoader;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.text.PathProperties;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
@@ -50,6 +50,8 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
private final String intersectionPublishTable;
|
||||
private final String intersectionDraftTable;
|
||||
|
||||
private IntersectionTable intersectionTable;
|
||||
|
||||
/**
|
||||
* For ManyToMany this is the Inverse join used to build reference queries.
|
||||
*/
|
||||
@@ -352,10 +354,30 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
* Set the lazy load server to help create reference collections (that lazy
|
||||
* load on demand).
|
||||
*/
|
||||
public void setLoader(BeanCollectionLoader loader) {
|
||||
public void setEbeanServer(SpiEbeanServer server) {
|
||||
if (help != null) {
|
||||
help.setLoader(loader);
|
||||
help.setLoader(server);
|
||||
}
|
||||
if (manyToMany) {
|
||||
intersectionTable = initIntersectionTable();
|
||||
}
|
||||
}
|
||||
|
||||
private IntersectionTable initIntersectionTable() {
|
||||
|
||||
IntersectionBuilder row = new IntersectionBuilder(intersectionPublishTable, intersectionDraftTable);
|
||||
for (ExportedProperty exportedProperty : exportedProperties) {
|
||||
row.addColumn(exportedProperty.getForeignDbColumn());
|
||||
}
|
||||
importedId.buildImport(row);
|
||||
return row.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the intersection table helper.
|
||||
*/
|
||||
public IntersectionTable intersectionTable() {
|
||||
return intersectionTable;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -755,6 +777,20 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
return intersectionDraftTable != null && !intersectionDraftTable.equals(intersectionPublishTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind the two side of a many to many to the given SqlUpdate.
|
||||
*/
|
||||
public void intersectionBind(SqlUpdate sql, EntityBean parentBean, EntityBean other) {
|
||||
if (embeddedExportedProperties) {
|
||||
BeanProperty idProp = descriptor.getIdProperty();
|
||||
parentBean = (EntityBean) idProp.getValue(parentBean);
|
||||
}
|
||||
for (ExportedProperty exportedProperty : exportedProperties) {
|
||||
sql.setNextParameter(exportedProperty.getValue(parentBean));
|
||||
}
|
||||
importedId.bindImport(sql, other);
|
||||
}
|
||||
|
||||
private void buildExport(IntersectionRow row, EntityBean parentBean) {
|
||||
|
||||
if (embeddedExportedProperties) {
|
||||
|
||||
@@ -46,15 +46,16 @@ class BeanPropertyAssocManyJsonHelp {
|
||||
if (JsonToken.VALUE_NULL == event) {
|
||||
return;
|
||||
}
|
||||
if (JsonToken.START_ARRAY != event) {
|
||||
throw new JsonParseException(parser, "Unexpected token " + event + " - expecting start_array ");
|
||||
}
|
||||
|
||||
if (many.isTransient()) {
|
||||
jsonReadTransientUsingObjectMapper(readJson, parentBean);
|
||||
return;
|
||||
}
|
||||
|
||||
if (JsonToken.START_ARRAY != event) {
|
||||
throw new JsonParseException(parser, "Unexpected token " + event + " - expecting start_array");
|
||||
}
|
||||
|
||||
many.setValue(parentBean, many.jsonReadCollection(readJson, parentBean));
|
||||
}
|
||||
|
||||
|
||||
@@ -44,8 +44,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
|
||||
|
||||
private final boolean primaryKeyExport;
|
||||
|
||||
private final PropertyForeignKey foreignKey;
|
||||
|
||||
private AssocOneHelp localHelp;
|
||||
|
||||
protected final BeanProperty[] embeddedProps;
|
||||
@@ -73,8 +71,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
|
||||
DeployBeanPropertyAssocOne<T> deploy) {
|
||||
|
||||
super(descriptor, deploy);
|
||||
|
||||
foreignKey = deploy.getForeignKey();
|
||||
primaryKeyExport = deploy.isPrimaryKeyExport();
|
||||
oneToOne = deploy.isOneToOne();
|
||||
oneToOneExported = deploy.isOneToOneExported();
|
||||
@@ -323,10 +319,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
|
||||
}
|
||||
}
|
||||
|
||||
public PropertyForeignKey getForeignKey() {
|
||||
return foreignKey;
|
||||
}
|
||||
|
||||
public boolean hasForeignKey() {
|
||||
return foreignKey == null || !foreignKey.isNoConstraint();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Helper to build IntersectionTable.
|
||||
*/
|
||||
public class IntersectionBuilder {
|
||||
|
||||
private final String publishTable;
|
||||
private final String draftTable;
|
||||
|
||||
private final List<String> columns = new ArrayList<>();
|
||||
|
||||
IntersectionBuilder(String publishTable, String draftTable) {
|
||||
this.publishTable = publishTable;
|
||||
this.draftTable = draftTable;
|
||||
}
|
||||
|
||||
public void addColumn(String column) {
|
||||
columns.add(column);
|
||||
}
|
||||
|
||||
public IntersectionTable build() {
|
||||
|
||||
String insertSql = insertSql(publishTable);
|
||||
String deleteSql = deleteSql(publishTable);
|
||||
|
||||
String draftInsertSql;
|
||||
String draftDeleteSql;
|
||||
if (publishTable.equals(draftTable)) {
|
||||
draftInsertSql = insertSql;
|
||||
draftDeleteSql = deleteSql;
|
||||
} else {
|
||||
draftInsertSql = insertSql(draftTable);
|
||||
draftDeleteSql = deleteSql(draftTable);
|
||||
}
|
||||
|
||||
return new IntersectionTable(insertSql, deleteSql, draftInsertSql, draftDeleteSql);
|
||||
}
|
||||
|
||||
private String insertSql(String tableName) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("insert into ").append(tableName).append(" (");
|
||||
|
||||
int count = 0;
|
||||
for (String column : columns) {
|
||||
if (count++ > 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append(column);
|
||||
}
|
||||
sb.append(") values (");
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (i > 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append("?");
|
||||
}
|
||||
sb.append(")");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String deleteSql(String tableName) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("delete from ").append(tableName);
|
||||
sb.append(" where ");
|
||||
|
||||
int count = 0;
|
||||
for (String column : columns) {
|
||||
if (count++ > 0) {
|
||||
sb.append(" and ");
|
||||
}
|
||||
sb.append(column);
|
||||
sb.append(" = ?");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebeaninternal.api.BindParams;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
import io.ebeaninternal.server.expression.DefaultExpressionRequest;
|
||||
import io.ebeaninternal.server.expression.IdInExpression;
|
||||
@@ -22,12 +22,12 @@ public class IntersectionRow {
|
||||
private List<Object> excludeIds;
|
||||
private BeanDescriptor<?> excludeDescriptor;
|
||||
|
||||
public IntersectionRow(String tableName, BeanDescriptor<?> targetDescriptor) {
|
||||
IntersectionRow(String tableName, BeanDescriptor<?> targetDescriptor) {
|
||||
this.tableName = tableName;
|
||||
this.targetDescriptor = targetDescriptor;
|
||||
}
|
||||
|
||||
public IntersectionRow(String tableName) {
|
||||
IntersectionRow(String tableName) {
|
||||
this.tableName = tableName;
|
||||
this.targetDescriptor = null;
|
||||
}
|
||||
@@ -35,7 +35,7 @@ public class IntersectionRow {
|
||||
/**
|
||||
* Set Id's to exclude. This is for deleting non-attached detail Id's.
|
||||
*/
|
||||
public void setExcludeIds(List<Object> excludeIds, BeanDescriptor<?> excludeDescriptor) {
|
||||
void setExcludeIds(List<Object> excludeIds, BeanDescriptor<?> excludeDescriptor) {
|
||||
this.excludeIds = excludeIds;
|
||||
this.excludeDescriptor = excludeDescriptor;
|
||||
}
|
||||
@@ -44,7 +44,7 @@ public class IntersectionRow {
|
||||
values.put(key, value);
|
||||
}
|
||||
|
||||
public SqlUpdate createInsert(EbeanServer server) {
|
||||
public SqlUpdate createInsert(SpiEbeanServer server) {
|
||||
|
||||
BindParams bindParams = new BindParams();
|
||||
|
||||
@@ -72,7 +72,7 @@ public class IntersectionRow {
|
||||
return new DefaultSqlUpdate(server, sb.toString(), bindParams);
|
||||
}
|
||||
|
||||
public SqlUpdate createDelete(EbeanServer server, boolean softDelete) {
|
||||
public SqlUpdate createDelete(SpiEbeanServer server, boolean softDelete) {
|
||||
|
||||
BindParams bindParams = new BindParams();
|
||||
|
||||
@@ -107,7 +107,7 @@ public class IntersectionRow {
|
||||
return new DefaultSqlUpdate(server, sb.toString(), bindParams);
|
||||
}
|
||||
|
||||
public SqlUpdate createDeleteChildren(EbeanServer server) {
|
||||
public SqlUpdate createDeleteChildren(SpiEbeanServer server) {
|
||||
|
||||
BindParams bindParams = new BindParams();
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.SqlUpdate;
|
||||
|
||||
public class IntersectionTable {
|
||||
|
||||
private final String insertSql;
|
||||
private final String deleteSql;
|
||||
private final String draftInsertSql;
|
||||
private final String draftDeleteSql;
|
||||
|
||||
IntersectionTable(String insertSql, String deleteSql, String draftInsertSql, String draftDeleteSql) {
|
||||
this.insertSql = insertSql;
|
||||
this.deleteSql = deleteSql;
|
||||
this.draftInsertSql = draftInsertSql;
|
||||
this.draftDeleteSql = draftDeleteSql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a SqlUpdate for inserting into the intersection table.
|
||||
*/
|
||||
public SqlUpdate insert(EbeanServer server, boolean draft) {
|
||||
return server.createSqlUpdate(draft ? draftInsertSql : insertSql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a SqlUpdate for deleting from the intersection table.
|
||||
*/
|
||||
public SqlUpdate delete(EbeanServer server, boolean draft) {
|
||||
return server.createSqlUpdate(draft ? draftDeleteSql : deleteSql);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import io.ebean.SqlUpdate;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.DbSqlContext;
|
||||
import io.ebeaninternal.server.deploy.IntersectionBuilder;
|
||||
import io.ebeaninternal.server.deploy.IntersectionRow;
|
||||
import io.ebeaninternal.server.persist.dml.GenerateDmlRequest;
|
||||
import io.ebeaninternal.server.persist.dmlbind.BindableRequest;
|
||||
@@ -66,4 +67,14 @@ public interface ImportedId {
|
||||
* Return the set importedId clause.
|
||||
*/
|
||||
String importedIdClause();
|
||||
|
||||
/**
|
||||
* Add DB columns to the intersection builder.
|
||||
*/
|
||||
void buildImport(IntersectionBuilder row);
|
||||
|
||||
/**
|
||||
* Bind values to the intersection SqlUpdate.
|
||||
*/
|
||||
void bindImport(SqlUpdate sql, EntityBean other);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssoc;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
import io.ebeaninternal.server.deploy.DbSqlContext;
|
||||
import io.ebeaninternal.server.deploy.IntersectionBuilder;
|
||||
import io.ebeaninternal.server.deploy.IntersectionRow;
|
||||
import io.ebeaninternal.server.persist.dml.GenerateDmlRequest;
|
||||
import io.ebeaninternal.server.persist.dmlbind.BindableRequest;
|
||||
@@ -130,15 +131,31 @@ public class ImportedIdEmbedded implements ImportedId {
|
||||
|
||||
EntityBean embeddedId = (EntityBean) foreignAssocOne.getValue(other);
|
||||
if (embeddedId == null) {
|
||||
String msg = "Foreign Key value null?";
|
||||
throw new PersistenceException(msg);
|
||||
throw new PersistenceException("Foreign Key value null?");
|
||||
}
|
||||
|
||||
for (ImportedIdSimple anImported : imported) {
|
||||
Object scalarValue = anImported.foreignProperty.getValue(embeddedId);
|
||||
row.put(anImported.localDbColumn, scalarValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildImport(IntersectionBuilder row) {
|
||||
for (ImportedIdSimple importedScalar : imported) {
|
||||
row.addColumn(importedScalar.localDbColumn);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindImport(SqlUpdate sql, EntityBean other) {
|
||||
EntityBean embeddedId = (EntityBean) foreignAssocOne.getValue(other);
|
||||
if (embeddedId == null) {
|
||||
throw new PersistenceException("Foreign Key value null?");
|
||||
}
|
||||
for (ImportedIdSimple anImported : imported) {
|
||||
Object scalarValue = anImported.foreignProperty.getValue(embeddedId);
|
||||
sql.setNextParameter(scalarValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,6 +7,7 @@ import io.ebeaninternal.server.deploy.BeanFkeyProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssoc;
|
||||
import io.ebeaninternal.server.deploy.DbSqlContext;
|
||||
import io.ebeaninternal.server.deploy.IntersectionBuilder;
|
||||
import io.ebeaninternal.server.deploy.IntersectionRow;
|
||||
import io.ebeaninternal.server.persist.dml.GenerateDmlRequest;
|
||||
import io.ebeaninternal.server.persist.dmlbind.BindableRequest;
|
||||
@@ -98,6 +99,20 @@ public final class ImportedIdSimple implements ImportedId, Comparable<ImportedId
|
||||
return foreignProperty.getValue(bean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildImport(IntersectionBuilder row) {
|
||||
row.addColumn(localDbColumn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindImport(SqlUpdate sql, EntityBean other) {
|
||||
Object value = getIdValue(other);
|
||||
if (value == null) {
|
||||
throw new PersistenceException("Foreign Key value null?");
|
||||
}
|
||||
sql.setNextParameter(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildImport(IntersectionRow row, EntityBean other) {
|
||||
|
||||
|
||||
@@ -106,6 +106,11 @@ public class DeployBeanDescriptor<T> {
|
||||
|
||||
private PlatformIdGenerator idGenerator;
|
||||
|
||||
/**
|
||||
* Set true when explicit auto generated Id.
|
||||
*/
|
||||
private boolean idGeneratedValue;
|
||||
|
||||
/**
|
||||
* The database sequence name (optional).
|
||||
*/
|
||||
@@ -848,6 +853,20 @@ public class DeployBeanDescriptor<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true for automatic Id generation strategy.
|
||||
*/
|
||||
public boolean isIdGeneratedValue() {
|
||||
return idGeneratedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set when GeneratedValue explicitly mapped on Id property.
|
||||
*/
|
||||
public void setIdGeneratedValue() {
|
||||
this.idGeneratedValue = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign the standard UUID generator.
|
||||
*/
|
||||
|
||||
@@ -647,11 +647,35 @@ public class DeployBeanProperty {
|
||||
*/
|
||||
public String parseAggregation() {
|
||||
if (aggregation != null) {
|
||||
int pos = aggregation.indexOf('(');
|
||||
if (pos > -1) {
|
||||
// check for recursive property name and formula
|
||||
String maybePropertyName = aggregation.substring(pos + 1, aggregation.length() - 1);
|
||||
if (name.equals(maybePropertyName)) {
|
||||
// e.g. bean property cost mapped to sum(cost)
|
||||
return aggregationJoin(pos, dbColumn);
|
||||
} else {
|
||||
DeployBeanProperty other = desc.getBeanProperty(maybePropertyName);
|
||||
if (other != null) {
|
||||
// e.g. bean property maxKms mapped to sum(totalKms) where totalKms is another property
|
||||
return aggregationJoin(pos, other.getDbColumnRaw());
|
||||
}
|
||||
}
|
||||
}
|
||||
aggregationParsed = desc.parse(aggregation);
|
||||
}
|
||||
return aggregationParsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple aggregation parsing like sum(someProperty)
|
||||
*/
|
||||
private String aggregationJoin(int pos, String dbColumn) {
|
||||
String p0 = aggregation.substring(0, pos + 1);
|
||||
aggregationParsed = p0 + "${ta}." + dbColumn + aggregation.substring(aggregation.length() - 1);
|
||||
return aggregationParsed;
|
||||
}
|
||||
|
||||
public void setAggregation(String aggregation) {
|
||||
this.aggregation = aggregation;
|
||||
this.dbRead = true;
|
||||
@@ -702,6 +726,13 @@ public class DeployBeanProperty {
|
||||
return dbColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DB column without any aggregation parsing.
|
||||
*/
|
||||
private String getDbColumnRaw() {
|
||||
return dbColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the database column name this is mapped to.
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.deploy.meta;
|
||||
|
||||
import io.ebeaninternal.server.deploy.BeanCascadeInfo;
|
||||
import io.ebeaninternal.server.deploy.BeanTable;
|
||||
import io.ebeaninternal.server.deploy.PropertyForeignKey;
|
||||
|
||||
/**
|
||||
* Abstract base for properties mapped to an associated bean, list, set or map.
|
||||
@@ -16,7 +17,7 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
|
||||
/**
|
||||
* Persist settings.
|
||||
*/
|
||||
protected final BeanCascadeInfo cascadeInfo = new BeanCascadeInfo();
|
||||
final BeanCascadeInfo cascadeInfo = new BeanCascadeInfo();
|
||||
|
||||
/**
|
||||
* The join table information.
|
||||
@@ -42,6 +43,8 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
|
||||
|
||||
private int fetchPreference = 1000;
|
||||
|
||||
private PropertyForeignKey foreignKey;
|
||||
|
||||
/**
|
||||
* Construct the property.
|
||||
*/
|
||||
@@ -117,6 +120,13 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
|
||||
return cascadeInfo;
|
||||
}
|
||||
|
||||
public void setForeignKey(PropertyForeignKey foreignKey) {
|
||||
this.foreignKey = foreignKey;
|
||||
}
|
||||
|
||||
public PropertyForeignKey getForeignKey() {
|
||||
return foreignKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mappedBy deployment attribute.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package io.ebeaninternal.server.deploy.meta;
|
||||
|
||||
import io.ebeaninternal.server.deploy.PropertyForeignKey;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
@@ -22,8 +21,6 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
|
||||
|
||||
private String columnPrefix;
|
||||
|
||||
private PropertyForeignKey foreignKey;
|
||||
|
||||
private boolean orphanRemoval;
|
||||
|
||||
/**
|
||||
@@ -148,14 +145,6 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public void setForeignKey(PropertyForeignKey foreignKey) {
|
||||
this.foreignKey = foreignKey;
|
||||
}
|
||||
|
||||
public PropertyForeignKey getForeignKey() {
|
||||
return foreignKey;
|
||||
}
|
||||
|
||||
public void setOrphanRemoval(boolean orphanRemoval) {
|
||||
this.orphanRemoval = orphanRemoval;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.deploy.parse;
|
||||
|
||||
import io.ebean.annotation.DbForeignKey;
|
||||
import io.ebean.annotation.FetchPreference;
|
||||
import io.ebean.annotation.HistoryExclude;
|
||||
import io.ebean.annotation.PrivateOwned;
|
||||
@@ -12,6 +13,7 @@ import io.ebean.util.StringHelper;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanTable;
|
||||
import io.ebeaninternal.server.deploy.PropertyForeignKey;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
|
||||
@@ -95,6 +97,12 @@ class AnnotationAssocManys extends AnnotationParser {
|
||||
readElementCollection(prop, elementCollection);
|
||||
}
|
||||
|
||||
// for ManyToMany typically to disable foreign keys from intersection table
|
||||
DbForeignKey dbForeignKey = get(prop, DbForeignKey.class);
|
||||
if (dbForeignKey != null){
|
||||
prop.setForeignKey(new PropertyForeignKey(dbForeignKey));
|
||||
}
|
||||
|
||||
if (get(prop, HistoryExclude.class) != null) {
|
||||
prop.setExcludedFromHistory();
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ public class AnnotationAssocOnes extends AnnotationParser {
|
||||
}
|
||||
|
||||
private String errorMsgMissingBeanTable(Class<?> type, String from) {
|
||||
return "Error with association to [" + type + "] from [" + from + "]. Is " + type + " registered?";
|
||||
return "Error with association to [" + type + "] from [" + from + "]. Is " + type + " registered? Does it have the @Entity annotation?";
|
||||
}
|
||||
|
||||
private BeanTable beanTable(DeployBeanPropertyAssoc<?> prop) {
|
||||
|
||||
@@ -322,7 +322,7 @@ public class AnnotationFields extends AnnotationParser {
|
||||
prop.setDbLength(length.value());
|
||||
}
|
||||
|
||||
io.ebean.annotation.NotNull nonNull = get(prop, io.ebean.annotation.NotNull.class);
|
||||
io.ebean.annotation.NotNull nonNull = get(prop, io.ebean.annotation.NotNull.class);
|
||||
if (nonNull != null) {
|
||||
prop.setNullable(false);
|
||||
}
|
||||
@@ -389,7 +389,7 @@ public class AnnotationFields extends AnnotationParser {
|
||||
|
||||
Set<DbMigration> dbMigration = getAll(prop, DbMigration.class);
|
||||
dbMigration.forEach(ann -> prop.addDbMigrationInfo(
|
||||
new DbMigrationInfo(ann.preAdd(), ann.postAdd(), ann.preAlter(), ann.postAlter(), ann.platforms())));
|
||||
new DbMigrationInfo(ann.preAdd(), ann.postAdd(), ann.preAlter(), ann.postAlter(), ann.platforms())));
|
||||
}
|
||||
|
||||
private void addIndex(DeployBeanProperty prop, Index index) {
|
||||
@@ -515,6 +515,7 @@ public class AnnotationFields extends AnnotationParser {
|
||||
|
||||
private void readGenValue(GeneratedValue gen, DeployBeanProperty prop) {
|
||||
|
||||
descriptor.setIdGeneratedValue();
|
||||
String genName = gen.generator();
|
||||
|
||||
SequenceGenerator sequenceGenerator = find(prop, SequenceGenerator.class);
|
||||
|
||||
@@ -174,6 +174,11 @@ public class ElPropertyChain implements ElPropertyValue {
|
||||
return lastElPropertyValue.isLocalEncrypted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object localEncrypt(Object value) {
|
||||
return lastElPropertyValue.localEncrypt(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAssocIsEmpty(SpiExpressionRequest request, String path) {
|
||||
return lastElPropertyValue.getAssocIsEmpty(request, path);
|
||||
|
||||
@@ -52,6 +52,11 @@ public interface ElPropertyValue extends ElPropertyDeploy, ExpressionPath {
|
||||
*/
|
||||
boolean isDbEncrypted();
|
||||
|
||||
/**
|
||||
* Encrypt the input value return the encrypted value.
|
||||
*/
|
||||
Object localEncrypt(Object value);
|
||||
|
||||
/**
|
||||
* Return the value ensuring objects prior to the top scalar property are
|
||||
* automatically populated.
|
||||
|
||||
@@ -84,10 +84,11 @@ public class SimpleExpression extends AbstractValueExpression {
|
||||
// bind the key as well as the value
|
||||
String encryptKey = prop.getBeanProperty().getEncryptKey().getStringValue();
|
||||
request.addBindEncryptKey(encryptKey);
|
||||
} else if (prop.isLocalEncrypted()) {
|
||||
Object bindVal = prop.localEncrypt(value());
|
||||
request.addBindEncryptKey(bindVal);
|
||||
return;
|
||||
}
|
||||
//else if (prop.isLocalEncrypted()) {
|
||||
// not supporting this for equals (but probably could)
|
||||
// prop.getBeanProperty().getScalarType();
|
||||
}
|
||||
|
||||
request.addBindValue(value());
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package io.ebeaninternal.server.logger;
|
||||
|
||||
import io.ebeaninternal.api.SpiLogManager;
|
||||
import io.ebeaninternal.api.SpiLogger;
|
||||
|
||||
public class DLogManager implements SpiLogManager {
|
||||
|
||||
private final SpiLogger sql;
|
||||
private final SpiLogger summary;
|
||||
private final SpiLogger txn;
|
||||
|
||||
public DLogManager(SpiLogger sql, SpiLogger summary, SpiLogger txn) {
|
||||
this.sql = sql;
|
||||
this.summary = summary;
|
||||
this.txn = txn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiLogger txn() {
|
||||
return txn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiLogger sql() {
|
||||
return sql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiLogger sum() {
|
||||
return summary;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package io.ebeaninternal.server.logger;
|
||||
|
||||
import io.ebeaninternal.api.SpiLogger;
|
||||
import io.ebeaninternal.api.SpiLoggerFactory;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Default SpiLoggerFactory implementation.
|
||||
*/
|
||||
public class DLoggerFactory implements SpiLoggerFactory {
|
||||
|
||||
/**
|
||||
* Just use a standard slf4j Logger.
|
||||
*/
|
||||
@Override
|
||||
public SpiLogger create(String name) {
|
||||
return new DSpiLogger(LoggerFactory.getLogger(name));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package io.ebeaninternal.server.logger;
|
||||
|
||||
import io.ebeaninternal.api.SpiLogger;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class DSpiLogger implements SpiLogger {
|
||||
|
||||
private final Logger logger;
|
||||
|
||||
public DSpiLogger(Logger logger) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebug() {
|
||||
return logger.isDebugEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTrace() {
|
||||
return logger.isTraceEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String msg) {
|
||||
logger.debug(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(String msg) {
|
||||
logger.trace(msg);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
@@ -357,4 +358,11 @@ public final class BatchControl {
|
||||
private BatchedBeanHolder[] getBeanHolderArray() {
|
||||
return beanHoldMap.values().toArray(new BatchedBeanHolder[beanHoldMap.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a batched statement.
|
||||
*/
|
||||
public int[] execute(String key, boolean getGeneratedKeys) throws SQLException {
|
||||
return pstmtHolder.execute(key, getGeneratedKeys);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ public class BatchedPstmt implements SpiProfileTransactionEvent {
|
||||
|
||||
private long profileStart;
|
||||
|
||||
private int[] results;
|
||||
|
||||
/**
|
||||
* Create with a given statement.
|
||||
*/
|
||||
@@ -116,7 +118,7 @@ public class BatchedPstmt implements SpiProfileTransactionEvent {
|
||||
|
||||
private void executeAndCheckRowCounts() throws SQLException {
|
||||
|
||||
int[] results = pstmt.executeBatch();
|
||||
results = pstmt.executeBatch();
|
||||
if (results.length != list.size()) {
|
||||
String s = "results array error " + results.length + " " + list.size();
|
||||
throw new SQLException(s);
|
||||
@@ -140,4 +142,10 @@ public class BatchedPstmt implements SpiProfileTransactionEvent {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the execution results (row counts).
|
||||
*/
|
||||
public int[] getResults() {
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.persist;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -73,6 +74,19 @@ public class BatchedPstmtHolder {
|
||||
return stmtMap.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute one of the batched statements returning the row counts.
|
||||
*/
|
||||
public int[] execute(String key, boolean getGeneratedKeys) throws SQLException {
|
||||
|
||||
BatchedPstmt batchedPstmt = stmtMap.remove(key);
|
||||
if (batchedPstmt == null) {
|
||||
throw new PersistenceException("No batched statement found for key " + key);
|
||||
}
|
||||
batchedPstmt.executeBatch(getGeneratedKeys);
|
||||
return batchedPstmt.getResults();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all batched PreparedStatements.
|
||||
*
|
||||
|
||||
@@ -2,12 +2,14 @@ package io.ebeaninternal.server.persist;
|
||||
|
||||
import io.ebean.config.dbplatform.DbPlatformType;
|
||||
import io.ebeaninternal.api.BindParams;
|
||||
import io.ebeaninternal.api.SpiLogManager;
|
||||
import io.ebeaninternal.server.core.Message;
|
||||
import io.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
import io.ebeaninternal.server.expression.platform.DbExpressionHandler;
|
||||
import io.ebeaninternal.server.persist.platform.MultiValueBind;
|
||||
import io.ebeaninternal.server.transaction.TransactionManager;
|
||||
import io.ebeaninternal.server.type.DataBind;
|
||||
import io.ebeaninternal.server.type.DataReader;
|
||||
import io.ebeaninternal.server.type.RsetDataReader;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
import io.ebeaninternal.server.type.TypeManager;
|
||||
import org.slf4j.Logger;
|
||||
@@ -18,6 +20,7 @@ import java.math.BigDecimal;
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.ArrayList;
|
||||
@@ -48,7 +51,7 @@ public class Binder {
|
||||
/**
|
||||
* Set the PreparedStatement with which to bind variables to.
|
||||
*/
|
||||
public Binder(TypeManager typeManager, int asOfBindCount, boolean asOfStandardsBased,
|
||||
public Binder(TypeManager typeManager, SpiLogManager logManager, int asOfBindCount, boolean asOfStandardsBased,
|
||||
DbExpressionHandler dbExpressionHandler, DataTimeZone dataTimeZone, MultiValueBind multiValueBind) {
|
||||
|
||||
this.typeManager = typeManager;
|
||||
@@ -57,12 +60,7 @@ public class Binder {
|
||||
this.dbExpressionHandler = dbExpressionHandler;
|
||||
this.dataTimeZone = dataTimeZone;
|
||||
this.multiValueBind = multiValueBind;
|
||||
this.enableBindLog = enableBindLog();
|
||||
}
|
||||
|
||||
private boolean enableBindLog() {
|
||||
return TransactionManager.SQL_LOGGER.isDebugEnabled()
|
||||
|| TransactionManager.SUM_LOGGER.isDebugEnabled();
|
||||
this.enableBindLog = logManager.sql().isDebug();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -201,7 +199,7 @@ public class Binder {
|
||||
}
|
||||
}
|
||||
|
||||
private ScalarType<?> getScalarType(Class<?> clazz) {
|
||||
public ScalarType<?> getScalarType(Class<?> clazz) {
|
||||
ScalarType<?> type = typeManager.getScalarType(clazz);
|
||||
if (type == null) {
|
||||
throw new PersistenceException("No ScalarType registered for " + clazz);
|
||||
@@ -462,4 +460,8 @@ public class Binder {
|
||||
public DataBind dataBind(PreparedStatement stmt, Connection connection) {
|
||||
return new DataBind(dataTimeZone, stmt, connection);
|
||||
}
|
||||
|
||||
public DataReader createDataReader(ResultSet resultSet) {
|
||||
return new RsetDataReader(dataTimeZone, resultSet);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.event.BeanPersistController;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiSqlUpdate;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.SpiUpdate;
|
||||
import io.ebeaninternal.server.core.Message;
|
||||
@@ -34,6 +35,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -124,13 +126,28 @@ public final class DefaultPersister implements Persister {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBatch(SpiSqlUpdate sqlUpdate, SpiTransaction transaction) {
|
||||
new PersistRequestUpdateSql(server, sqlUpdate, transaction, persistExecute).addBatch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] executeBatch(SpiSqlUpdate sqlUpdate, SpiTransaction transaction) {
|
||||
BatchControl batchControl = transaction.getBatchControl();
|
||||
try {
|
||||
return batchControl.execute(sqlUpdate.getSql(), sqlUpdate.isGetGeneratedKeys());
|
||||
} catch (SQLException e) {
|
||||
throw new PersistenceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the updateSql.
|
||||
*/
|
||||
@Override
|
||||
public int executeSqlUpdate(SqlUpdate updSql, Transaction t) {
|
||||
|
||||
return executeOrQueue(new PersistRequestUpdateSql(server, updSql, (SpiTransaction) t, persistExecute));
|
||||
return executeOrQueue(new PersistRequestUpdateSql(server, (SpiSqlUpdate) updSql, (SpiTransaction) t, persistExecute));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,6 +26,14 @@ class MergeContext {
|
||||
this.clientGeneratedIds = clientGeneratedIds;
|
||||
}
|
||||
|
||||
public SpiEbeanServer getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
public SpiTransaction getTransaction() {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add to the list of beans to delete.
|
||||
*/
|
||||
|
||||
@@ -26,7 +26,7 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
class MergeHandler {
|
||||
|
||||
private final Pattern PATH_SPLIT = Pattern.compile("\\.");
|
||||
private static final Pattern PATH_SPLIT = Pattern.compile("\\.");
|
||||
|
||||
private final SpiEbeanServer server;
|
||||
private final BeanDescriptor<?> desc;
|
||||
@@ -51,7 +51,7 @@ class MergeHandler {
|
||||
List<EntityBean> merge() {
|
||||
|
||||
Set<String> paths = options.paths();
|
||||
if (paths.isEmpty() && !options.isClientGeneratedIds()) {
|
||||
if (desc.isIdGeneratedValue() && paths.isEmpty() && !options.isClientGeneratedIds()) {
|
||||
// just do a single insert or update based on Id value present
|
||||
Object id = desc.getId(bean);
|
||||
if (id != null) {
|
||||
@@ -139,7 +139,12 @@ class MergeHandler {
|
||||
throw new PersistenceException("merge path [" + path + "] is not a ToMany or ToOne property of " + targetDesc.getFullName());
|
||||
}
|
||||
if (prop instanceof BeanPropertyAssocMany<?>) {
|
||||
return new MergeNodeAssocMany(fullPath, (BeanPropertyAssocMany<?>) prop);
|
||||
BeanPropertyAssocMany<?> assocMany = (BeanPropertyAssocMany<?>) prop;
|
||||
if (assocMany.isManyToMany()) {
|
||||
return new MergeNodeAssocManyToMany(fullPath, assocMany);
|
||||
} else {
|
||||
return new MergeNodeAssocOneToMany(fullPath, assocMany);
|
||||
}
|
||||
} else {
|
||||
return new MergeNodeAssocOne(fullPath, (BeanPropertyAssocOne<?>) prop);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssoc;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -50,6 +52,22 @@ abstract class MergeNode {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the outline beans as a map keyed by Id values.
|
||||
*/
|
||||
Map<Object, EntityBean> toMap(Collection outlines) {
|
||||
|
||||
Map<Object, EntityBean> outlineMap = new HashMap<>();
|
||||
if (outlines != null) {
|
||||
for (Object out : outlines) {
|
||||
EntityBean outlineBean = (EntityBean) out;
|
||||
Object outlineId = targetDescriptor.getId(outlineBean);
|
||||
outlineMap.put(outlineId, outlineBean);
|
||||
}
|
||||
}
|
||||
return outlineMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add to the query to fetch the Ids values for the foreign keys basically.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package io.ebeaninternal.server.persist;
|
||||
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.IntersectionTable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Node for processing merge on ManyToMany properties.
|
||||
*/
|
||||
class MergeNodeAssocManyToMany extends MergeNode {
|
||||
|
||||
private final BeanPropertyAssocMany<?> many;
|
||||
|
||||
MergeNodeAssocManyToMany(String fullPath, BeanPropertyAssocMany<?> property) {
|
||||
super(fullPath, property);
|
||||
this.many = property;
|
||||
}
|
||||
|
||||
public void merge(MergeRequest request) {
|
||||
|
||||
EntityBean parentBean = request.getBean();
|
||||
|
||||
Collection beans = many.getRawCollection(parentBean);
|
||||
Collection outlines = many.getRawCollection(request.getOutline());
|
||||
|
||||
Map<Object, EntityBean> outlineIds = toMap(outlines);
|
||||
|
||||
List<EntityBean> additions = new ArrayList<>();
|
||||
if (beans != null) {
|
||||
for (Object bean : beans) {
|
||||
EntityBean entityBean = (EntityBean) bean;
|
||||
Object beanId = targetDescriptor.getId(entityBean);
|
||||
if (beanId != null) {
|
||||
if (outlineIds.remove(beanId) == null) {
|
||||
additions.add(entityBean);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// any remaining are considered deletes
|
||||
List<EntityBean> deletions = new ArrayList<>(outlineIds.values());
|
||||
|
||||
SpiEbeanServer server = request.getServer();
|
||||
SpiTransaction transaction = request.getTransaction();
|
||||
|
||||
IntersectionTable intersectionTable = many.intersectionTable();
|
||||
|
||||
if (!deletions.isEmpty()) {
|
||||
transaction.flushBatch();
|
||||
|
||||
SqlUpdate delete = intersectionTable.delete(server, false);
|
||||
for (EntityBean deletion : deletions) {
|
||||
many.intersectionBind(delete, parentBean, deletion);
|
||||
delete.addBatch();
|
||||
}
|
||||
delete.execute();
|
||||
}
|
||||
|
||||
if (!additions.isEmpty()) {
|
||||
transaction.flushBatch();
|
||||
|
||||
SqlUpdate insert = intersectionTable.insert(server, false);
|
||||
for (EntityBean addition : additions) {
|
||||
many.intersectionBind(insert, parentBean, addition);
|
||||
insert.addBatch();
|
||||
}
|
||||
insert.execute();
|
||||
}
|
||||
|
||||
many.resetMany(parentBean);
|
||||
}
|
||||
|
||||
}
|
||||
+3
-10
@@ -10,11 +10,11 @@ import java.util.Map;
|
||||
/**
|
||||
* Node for processing merge on ToMany properties.
|
||||
*/
|
||||
class MergeNodeAssocMany extends MergeNode {
|
||||
class MergeNodeAssocOneToMany extends MergeNode {
|
||||
|
||||
private final BeanPropertyAssocMany<?> many;
|
||||
|
||||
MergeNodeAssocMany(String fullPath, BeanPropertyAssocMany<?> property) {
|
||||
MergeNodeAssocOneToMany(String fullPath, BeanPropertyAssocMany<?> property) {
|
||||
super(fullPath, property);
|
||||
this.many = property;
|
||||
}
|
||||
@@ -24,14 +24,7 @@ class MergeNodeAssocMany extends MergeNode {
|
||||
Collection beans = many.getRawCollection(request.getBean());
|
||||
Collection outlines = many.getRawCollection(request.getOutline());
|
||||
|
||||
Map<Object, EntityBean> outlineIds = new HashMap<>();
|
||||
if (outlines != null) {
|
||||
for (Object outline : outlines) {
|
||||
EntityBean outlineBean = (EntityBean) outline;
|
||||
Object outlineId = targetDescriptor.getId(outlineBean);
|
||||
outlineIds.put(outlineId, outlineBean);
|
||||
}
|
||||
}
|
||||
Map<Object, EntityBean> outlineIds = toMap(outlines);
|
||||
|
||||
if (beans != null) {
|
||||
for (Object bean : beans) {
|
||||
@@ -1,6 +1,8 @@
|
||||
package io.ebeaninternal.server.persist;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
|
||||
/**
|
||||
* Request object used for processing the merge.
|
||||
@@ -18,6 +20,20 @@ class MergeRequest {
|
||||
this.outline = outline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the associated server.
|
||||
*/
|
||||
SpiEbeanServer getServer() {
|
||||
return context.getServer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the associated transaction.
|
||||
*/
|
||||
public SpiTransaction getTransaction() {
|
||||
return context.getTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a sub request with the given beans (to cascade the processing).
|
||||
*/
|
||||
|
||||
@@ -154,9 +154,7 @@ public abstract class DmlHandler implements PersistHandler, BindableRequest {
|
||||
*/
|
||||
protected void logSql(String sql) {
|
||||
if (logLevelSql) {
|
||||
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
|
||||
sql = Str.add(sql, "; --bind(", bindLog.toString(), ")");
|
||||
}
|
||||
sql = Str.add(sql, "; --bind(", bindLog.toString(), ")");
|
||||
transaction.logSql(sql);
|
||||
}
|
||||
}
|
||||
@@ -187,7 +185,10 @@ public abstract class DmlHandler implements PersistHandler, BindableRequest {
|
||||
@Override
|
||||
public void bindNoLog(Object value, int sqlType, String logPlaceHolder) throws SQLException {
|
||||
if (logLevelSql) {
|
||||
bindLog.append(logPlaceHolder).append(" ");
|
||||
if (bindLog.length() > 0) {
|
||||
bindLog.append(",");
|
||||
}
|
||||
bindLog.append(logPlaceHolder);
|
||||
}
|
||||
dataBind.setObject(value, sqlType);
|
||||
}
|
||||
|
||||
@@ -241,10 +241,14 @@ class CQueryBuilder {
|
||||
|
||||
ManyWhereJoins manyWhereJoins = query.getManyWhereJoins();
|
||||
|
||||
if (manyWhereJoins.isFormulaWithJoin()) {
|
||||
query.select(manyWhereJoins.getFormulaProperties());
|
||||
} else {
|
||||
query.setSelectId();
|
||||
boolean countDistinct = query.isDistinct();
|
||||
if (!countDistinct) {
|
||||
// minimise select clause for standard count
|
||||
if (manyWhereJoins.isFormulaWithJoin()) {
|
||||
query.select(manyWhereJoins.getFormulaProperties());
|
||||
} else {
|
||||
query.setSelectId();
|
||||
}
|
||||
}
|
||||
|
||||
CQueryPredicates predicates = new CQueryPredicates(binder, request);
|
||||
@@ -263,23 +267,35 @@ class CQueryBuilder {
|
||||
}
|
||||
|
||||
boolean hasMany = sqlTree.hasMany();
|
||||
String sqlSelect = "select count(*)";
|
||||
if (hasMany) {
|
||||
// need to count distinct id's ...
|
||||
query.setSqlDistinct(true);
|
||||
sqlSelect = null;
|
||||
|
||||
String sqlSelect = null;
|
||||
if (countDistinct) {
|
||||
if (sqlTree.isSingleProperty()) {
|
||||
request.setInlineCountDistinct();
|
||||
}
|
||||
} else {
|
||||
if (hasMany) {
|
||||
// need to count distinct id's ...
|
||||
query.setSqlDistinct(true);
|
||||
} else {
|
||||
sqlSelect = "select count(*)";
|
||||
}
|
||||
}
|
||||
|
||||
SqlLimitResponse s = buildSql(sqlSelect, request, predicates, sqlTree);
|
||||
String sql = s.getSql();
|
||||
if (hasMany || query.isRawSql()) {
|
||||
int pos = sql.lastIndexOf(" order by "); // remove order by - mssql does not accept order by in subqueries
|
||||
if (pos != -1) {
|
||||
sql = sql.substring(0, pos);
|
||||
}
|
||||
sql = "select count(*) from ( " + sql + ")";
|
||||
if (selectCountWithAlias) {
|
||||
sql += " as c";
|
||||
|
||||
if (!request.isInlineCountDistinct()) {
|
||||
if (countDistinct) {
|
||||
sql = wrapSelectCount(sql);
|
||||
|
||||
} else if (hasMany || query.isRawSql()) {
|
||||
// remove order by - mssql does not accept order by in subqueries
|
||||
int pos = sql.lastIndexOf(" order by ");
|
||||
if (pos != -1) {
|
||||
sql = sql.substring(0, pos);
|
||||
}
|
||||
sql = wrapSelectCount(sql);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,6 +306,14 @@ class CQueryBuilder {
|
||||
return new CQueryRowCount(queryPlan, request, predicates);
|
||||
}
|
||||
|
||||
private String wrapSelectCount(String sql) {
|
||||
sql = "select count(*) from ( " + sql + ")";
|
||||
if (selectCountWithAlias) {
|
||||
sql += " as c";
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SQL Select statement as a String. Converts logical property
|
||||
* names to physical deployment column names.
|
||||
@@ -536,6 +560,9 @@ class CQueryBuilder {
|
||||
if (!useSqlLimiter) {
|
||||
sb.append("select ");
|
||||
if (query.isDistinctQuery()) {
|
||||
if (request.isInlineCountDistinct()) {
|
||||
sb.append("count(");
|
||||
}
|
||||
sb.append("distinct ");
|
||||
String distinctOn = select.getDistinctOn();
|
||||
if (distinctOn != null) {
|
||||
@@ -551,6 +578,9 @@ class CQueryBuilder {
|
||||
} else {
|
||||
sb.append(select.getSelectSql());
|
||||
}
|
||||
if (request.isInlineCountDistinct()) {
|
||||
sb.append(")");
|
||||
}
|
||||
if (query.isDistinctQuery() && dbOrderBy != null && !query.isSingleAttribute()) {
|
||||
// add the orderBy columns to the select clause (due to distinct)
|
||||
sb.append(", ").append(DbOrderByTrim.trim(dbOrderBy));
|
||||
|
||||
@@ -19,7 +19,6 @@ import io.ebeaninternal.server.core.SpiResultSet;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.lib.util.Str;
|
||||
import io.ebeaninternal.server.persist.Binder;
|
||||
import io.ebeaninternal.server.transaction.TransactionManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -82,9 +81,7 @@ public class CQueryEngine {
|
||||
|
||||
if (request.isLogSql()) {
|
||||
String logSql = query.getGeneratedSql();
|
||||
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
|
||||
logSql = Str.add(logSql, "; --bind(", query.getBindLog(), ") rows:", String.valueOf(rows));
|
||||
}
|
||||
logSql = Str.add(logSql, "; --bind(", query.getBindLog(), ") rows:", String.valueOf(rows));
|
||||
request.logSql(logSql);
|
||||
}
|
||||
|
||||
@@ -151,11 +148,7 @@ public class CQueryEngine {
|
||||
}
|
||||
|
||||
private <T> void logGeneratedSql(OrmQueryRequest<T> request, String sql, String bindLog) {
|
||||
String logSql = sql;
|
||||
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
|
||||
logSql = Str.add(logSql, "; --bind(", bindLog, ")");
|
||||
}
|
||||
request.logSql(logSql);
|
||||
request.logSql(Str.add(sql, "; --bind(", bindLog, ")"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -458,9 +451,7 @@ public class CQueryEngine {
|
||||
private void logSql(CQuery<?> query) {
|
||||
|
||||
String sql = query.getGeneratedSql();
|
||||
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
|
||||
sql = Str.add(sql, "; --bind(", query.getBindLog(), ")");
|
||||
}
|
||||
sql = Str.add(sql, "; --bind(", query.getBindLog(), ")");
|
||||
query.getTransaction().logSql(sql);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.RowConsumer;
|
||||
import io.ebean.RowMapper;
|
||||
import io.ebean.SqlRow;
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
@@ -10,6 +12,7 @@ import io.ebeaninternal.server.core.Message;
|
||||
import io.ebeaninternal.server.core.RelationalQueryEngine;
|
||||
import io.ebeaninternal.server.core.RelationalQueryRequest;
|
||||
import io.ebeaninternal.server.persist.Binder;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.SQLException;
|
||||
@@ -91,6 +94,109 @@ public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T findOneMapper(RelationalQueryRequest request, RowMapper<T> mapper) {
|
||||
try {
|
||||
request.executeSql(binder, SpiQuery.Type.BEAN);
|
||||
T value = request.mapOne(mapper);
|
||||
request.logSummary();
|
||||
return value;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(Message.msg("fetch.error", e.getMessage(), request.getSql()), e);
|
||||
|
||||
} finally {
|
||||
request.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> findListMapper(RelationalQueryRequest request, RowMapper<T> mapper) {
|
||||
try {
|
||||
request.executeSql(binder, SpiQuery.Type.LIST);
|
||||
List<T> list = request.mapList(mapper);
|
||||
request.logSummary();
|
||||
return list;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(Message.msg("fetch.error", e.getMessage(), request.getSql()), e);
|
||||
|
||||
} finally {
|
||||
request.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findEachRow(RelationalQueryRequest request, RowConsumer consumer) {
|
||||
try {
|
||||
request.executeSql(binder, SpiQuery.Type.LIST);
|
||||
request.mapEach(consumer);
|
||||
request.logSummary();
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(Message.msg("fetch.error", e.getMessage(), request.getSql()), e);
|
||||
|
||||
} finally {
|
||||
request.close();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> List<T> findSingleAttributeList(RelationalQueryRequest request, Class<T> cls) {
|
||||
ScalarType<T> scalarType = (ScalarType<T>) binder.getScalarType(cls);
|
||||
return findScalarList(request, scalarType);
|
||||
}
|
||||
|
||||
private <T> List<T> findScalarList(RelationalQueryRequest request, ScalarType<T> scalarType) {
|
||||
try {
|
||||
request.executeSql(binder, SpiQuery.Type.ATTRIBUTE);
|
||||
List<T> list = new ArrayList<>();
|
||||
while (request.next()) {
|
||||
request.incrementRows();
|
||||
list.add(scalarType.read(binder.createDataReader(request.getResultSet())));
|
||||
}
|
||||
|
||||
request.logSummary();
|
||||
return list;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(Message.msg("fetch.error", e.getMessage(), request.getSql()), e);
|
||||
|
||||
} finally {
|
||||
request.close();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T findSingleAttribute(RelationalQueryRequest request, Class<T> cls) {
|
||||
|
||||
ScalarType<T> scalarType = (ScalarType<T>) binder.getScalarType(cls);
|
||||
return findScalar(request, scalarType);
|
||||
}
|
||||
|
||||
private <T> T findScalar(RelationalQueryRequest request, ScalarType<T> scalarType) {
|
||||
try {
|
||||
request.executeSql(binder, SpiQuery.Type.ATTRIBUTE);
|
||||
|
||||
T value = null;
|
||||
if (request.next()) {
|
||||
request.incrementRows();
|
||||
value = scalarType.read(binder.createDataReader(request.getResultSet()));
|
||||
}
|
||||
|
||||
request.logSummary();
|
||||
return value;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(Message.msg("fetch.error", e.getMessage(), request.getSql()), e);
|
||||
|
||||
} finally {
|
||||
request.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SqlRow> findList(RelationalQueryRequest request) {
|
||||
|
||||
|
||||
@@ -159,4 +159,8 @@ class SqlTree {
|
||||
boolean hasMany() {
|
||||
return manyProperty != null || rootNode.hasMany();
|
||||
}
|
||||
|
||||
boolean isSingleProperty() {
|
||||
return rootNode.isSingleProperty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,4 +83,8 @@ interface SqlTreeNode {
|
||||
*/
|
||||
ScalarType<?> getSingleAttributeScalarType();
|
||||
|
||||
/**
|
||||
* Return true if the query is known to only have a single property selected.
|
||||
*/
|
||||
boolean isSingleProperty();
|
||||
}
|
||||
|
||||
@@ -138,6 +138,11 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleProperty() {
|
||||
return properties != null && properties.length == 1 && children.length == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScalarType<?> getSingleAttributeScalarType() {
|
||||
if (properties == null || properties.length == 0) {
|
||||
|
||||
@@ -39,6 +39,11 @@ class SqlTreeNodeExtraJoin implements SqlTreeNode {
|
||||
this.manyJoin = assocBeanProperty instanceof STreePropertyAssocMany;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleProperty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildRawSqlSelectChain(List<String> selectChain) {
|
||||
// nothing to add
|
||||
|
||||
@@ -36,6 +36,11 @@ class SqlTreeNodeManyWhereJoin implements SqlTreeNode {
|
||||
this.parentPrefix = split[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleProperty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScalarType<?> getSingleAttributeScalarType() {
|
||||
throw new IllegalStateException("No expected");
|
||||
|
||||
@@ -1276,8 +1276,8 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBeanCacheReload() {
|
||||
return CacheMode.PUT == useBeanCache;
|
||||
public boolean isForceHitDatabase() {
|
||||
return forUpdate != null || CacheMode.PUT == useBeanCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package io.ebeaninternal.server.querydefn;
|
||||
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.RowConsumer;
|
||||
import io.ebean.RowMapper;
|
||||
import io.ebean.SqlRow;
|
||||
import io.ebeaninternal.api.BindParams;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiSqlQuery;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
@@ -17,7 +20,7 @@ public class DefaultRelationalQuery implements SpiSqlQuery {
|
||||
|
||||
private static final long serialVersionUID = -1098305779779591068L;
|
||||
|
||||
private final transient EbeanServer server;
|
||||
private final transient SpiEbeanServer server;
|
||||
|
||||
private String label;
|
||||
|
||||
@@ -39,16 +42,11 @@ public class DefaultRelationalQuery implements SpiSqlQuery {
|
||||
/**
|
||||
* Additional supply a query detail object.
|
||||
*/
|
||||
public DefaultRelationalQuery(EbeanServer server, String query) {
|
||||
public DefaultRelationalQuery(SpiEbeanServer server, String query) {
|
||||
this.server = server;
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
public DefaultRelationalQuery setQuery(String query) {
|
||||
this.query = query;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findEach(Consumer<SqlRow> consumer) {
|
||||
server.findEach(this, consumer, null);
|
||||
@@ -64,6 +62,41 @@ public class DefaultRelationalQuery implements SpiSqlQuery {
|
||||
return server.findList(this, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal findSingleDecimal() {
|
||||
return server.findSingleAttribute(this, BigDecimal.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long findSingleLong() {
|
||||
return server.findSingleAttribute(this, Long.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T findSingleAttribute(Class<T> cls) {
|
||||
return server.findSingleAttribute(this, cls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> findSingleAttributeList(Class<T> cls) {
|
||||
return server.findSingleAttributeList(this, cls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T findOne(RowMapper<T> mapper) {
|
||||
return server.findOneMapper(this, mapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> findList(RowMapper<T> mapper) {
|
||||
return server.findListMapper(this, mapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findEachRow(RowConsumer consumer) {
|
||||
server.findEachRow(this, consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlRow findOne() {
|
||||
return server.findOne(this, null);
|
||||
|
||||
@@ -43,6 +43,9 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
|
||||
|
||||
private final TransactionManager manager;
|
||||
|
||||
private final boolean logSql;
|
||||
private final boolean logSummary;
|
||||
|
||||
/**
|
||||
* The status of the transaction.
|
||||
*/
|
||||
@@ -70,6 +73,8 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
|
||||
*/
|
||||
ImplicitReadOnlyTransaction(TransactionManager manager, Connection connection) {
|
||||
this.manager = manager;
|
||||
this.logSql = manager.isLogSql();
|
||||
this.logSummary = manager.isLogSummary();
|
||||
this.active = true;
|
||||
this.connection = connection;
|
||||
this.persistenceContext = new DefaultPersistenceContext();
|
||||
@@ -441,22 +446,22 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
|
||||
|
||||
@Override
|
||||
public boolean isLogSql() {
|
||||
return TransactionManager.SQL_LOGGER.isDebugEnabled();
|
||||
return logSql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLogSummary() {
|
||||
return TransactionManager.SUM_LOGGER.isDebugEnabled();
|
||||
return logSummary;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logSql(String msg) {
|
||||
TransactionManager.SQL_LOGGER.debug(msg);
|
||||
manager.log().sql().debug(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logSummary(String msg) {
|
||||
TransactionManager.SUM_LOGGER.debug(msg);
|
||||
manager.log().sum().debug(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -55,6 +55,9 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
*/
|
||||
protected final String id;
|
||||
|
||||
private final boolean logSql;
|
||||
private final boolean logSummary;
|
||||
|
||||
/**
|
||||
* The user defined label to group execution statistics.
|
||||
*/
|
||||
@@ -200,11 +203,15 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
this.startNanos = System.nanoTime();
|
||||
|
||||
if (manager == null) {
|
||||
this.logSql = false;
|
||||
this.logSummary = false;
|
||||
this.skipCacheAfterWrite = true;
|
||||
this.batchMode = PersistBatch.NONE;
|
||||
this.batchOnCascadeMode = PersistBatch.NONE;
|
||||
this.onQueryOnly = OnQueryOnly.ROLLBACK;
|
||||
} else {
|
||||
this.logSql = manager.isLogSql();
|
||||
this.logSummary = manager.isLogSummary();
|
||||
this.skipCacheAfterWrite = manager.isSkipCacheAfterWrite();
|
||||
this.batchMode = manager.getPersistBatch();
|
||||
this.batchOnCascadeMode = manager.getPersistBatchOnCascade();
|
||||
@@ -861,22 +868,22 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
|
||||
@Override
|
||||
public boolean isLogSql() {
|
||||
return TransactionManager.SQL_LOGGER.isDebugEnabled();
|
||||
return logSql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLogSummary() {
|
||||
return TransactionManager.SUM_LOGGER.isDebugEnabled();
|
||||
return logSummary;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logSql(String msg) {
|
||||
TransactionManager.SQL_LOGGER.debug(Str.add(logPrefix, msg));
|
||||
manager.log().sql().debug(Str.add(logPrefix, msg));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logSummary(String msg) {
|
||||
TransactionManager.SUM_LOGGER.debug(Str.add(logPrefix, msg));
|
||||
manager.log().sum().debug(Str.add(logPrefix, msg));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,6 +21,7 @@ class SavepointTransaction extends SpiTransactionProxy {
|
||||
private final Savepoint savepoint;
|
||||
private final Connection connection;
|
||||
private final String logPrefix;
|
||||
private final String spPrefix;
|
||||
|
||||
private boolean rollbackOnly;
|
||||
private int state;
|
||||
@@ -31,7 +32,8 @@ class SavepointTransaction extends SpiTransactionProxy {
|
||||
this.connection = transaction.getInternalConnection();
|
||||
this.savepoint = connection.setSavepoint();
|
||||
int savepointId = savepoint.getSavepointId();
|
||||
this.logPrefix = "txn[" + transaction.getId() + "-sp" + savepointId + "] ";
|
||||
this.spPrefix = "sp[" + savepointId + "] ";
|
||||
this.logPrefix = transaction.getLogPrefix() + spPrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -41,12 +43,12 @@ class SavepointTransaction extends SpiTransactionProxy {
|
||||
|
||||
@Override
|
||||
public void logSql(String msg) {
|
||||
TransactionManager.SQL_LOGGER.debug(Str.add(logPrefix, msg));
|
||||
transaction.logSql(Str.add(spPrefix, msg));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logSummary(String msg) {
|
||||
TransactionManager.SUM_LOGGER.debug(Str.add(logPrefix, msg));
|
||||
transaction.logSummary(Str.add(spPrefix, msg));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,7 +2,6 @@ package io.ebeaninternal.server.transaction;
|
||||
|
||||
import io.ebean.util.JdbcClose;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.Connection;
|
||||
@@ -14,8 +13,6 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
*/
|
||||
abstract class TransactionFactory {
|
||||
|
||||
static final Logger TXN_LOGGER = TransactionManager.TXN_LOGGER;
|
||||
|
||||
final AtomicLong counter = new AtomicLong(1000);
|
||||
|
||||
final TransactionManager manager;
|
||||
@@ -51,8 +48,8 @@ abstract class TransactionFactory {
|
||||
}
|
||||
}
|
||||
|
||||
if (explicit && TXN_LOGGER.isTraceEnabled()) {
|
||||
TXN_LOGGER.trace(t.getLogPrefix() + "Begin");
|
||||
if (explicit && manager.log().txn().isTrace()) {
|
||||
manager.log().txn().trace(t.getLogPrefix() + "Begin");
|
||||
}
|
||||
|
||||
return t;
|
||||
|
||||
@@ -15,6 +15,8 @@ import io.ebean.meta.MetricType;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.api.ScopeTrans;
|
||||
import io.ebeaninternal.api.ScopedTransaction;
|
||||
import io.ebeaninternal.api.SpiLogManager;
|
||||
import io.ebeaninternal.api.SpiLogger;
|
||||
import io.ebeaninternal.api.SpiProfileHandler;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.SpiTransactionManager;
|
||||
@@ -53,12 +55,6 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
|
||||
public static final Logger clusterLogger = LoggerFactory.getLogger("io.ebean.Cluster");
|
||||
|
||||
public static final Logger SQL_LOGGER = LoggerFactory.getLogger("io.ebean.SQL");
|
||||
|
||||
public static final Logger SUM_LOGGER = LoggerFactory.getLogger("io.ebean.SUM");
|
||||
|
||||
public static final Logger TXN_LOGGER = LoggerFactory.getLogger("io.ebean.TXN");
|
||||
|
||||
protected final BeanDescriptorManager beanDescriptorManager;
|
||||
|
||||
/**
|
||||
@@ -128,6 +124,9 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
|
||||
private final TransactionFactory transactionFactory;
|
||||
|
||||
private final SpiLogManager logManager;
|
||||
private final SpiLogger txnLogger;
|
||||
|
||||
private final DatabasePlatform databasePlatform;
|
||||
|
||||
private final SpiProfileHandler profileHandler;
|
||||
@@ -142,6 +141,8 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
*/
|
||||
public TransactionManager(TransactionManagerOptions options) {
|
||||
|
||||
this.logManager = options.logManager;
|
||||
this.txnLogger = logManager.txn();
|
||||
this.databasePlatform = options.config.getDatabasePlatform();
|
||||
this.skipCacheAfterWrite = options.config.isSkipCacheAfterWrite();
|
||||
this.notifyL2CacheInForeground = options.notifyL2CacheInForeground;
|
||||
@@ -355,12 +356,12 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
public void notifyOfRollback(SpiTransaction transaction, Throwable cause) {
|
||||
|
||||
try {
|
||||
if (TXN_LOGGER.isDebugEnabled()) {
|
||||
if (txnLogger.isDebug()) {
|
||||
String msg = transaction.getLogPrefix() + "Rollback";
|
||||
if (cause != null) {
|
||||
msg += " error: " + formatThrowable(cause);
|
||||
}
|
||||
TXN_LOGGER.debug(msg);
|
||||
txnLogger.debug(msg);
|
||||
}
|
||||
|
||||
} catch (Exception ex) {
|
||||
@@ -374,8 +375,8 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
public void notifyOfQueryOnly(SpiTransaction transaction) {
|
||||
|
||||
// Nothing that interesting here
|
||||
if (TXN_LOGGER.isTraceEnabled()) {
|
||||
TXN_LOGGER.trace(transaction.getLogPrefix() + "Commit - query only");
|
||||
if (txnLogger.isTrace()) {
|
||||
txnLogger.trace(transaction.getLogPrefix() + "Commit - query only");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,8 +410,8 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
public void notifyOfCommit(SpiTransaction transaction) {
|
||||
|
||||
try {
|
||||
if (TXN_LOGGER.isDebugEnabled()) {
|
||||
TXN_LOGGER.debug(transaction.getLogPrefix() + "Commit");
|
||||
if (txnLogger.isDebug()) {
|
||||
txnLogger.debug(transaction.getLogPrefix() + "Commit");
|
||||
}
|
||||
|
||||
PostCommitProcessing postCommit = new PostCommitProcessing(clusterManager, this, transaction);
|
||||
@@ -702,4 +703,16 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
throw new RuntimeException("Should never get here?");
|
||||
}
|
||||
}
|
||||
|
||||
public SpiLogManager log() {
|
||||
return logManager;
|
||||
}
|
||||
|
||||
public boolean isLogSql() {
|
||||
return logManager.sql().isDebug();
|
||||
}
|
||||
|
||||
public boolean isLogSummary() {
|
||||
return logManager.sum().isDebug();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.transaction;
|
||||
|
||||
import io.ebean.BackgroundExecutor;
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebeaninternal.api.SpiLogManager;
|
||||
import io.ebeaninternal.api.SpiProfileHandler;
|
||||
import io.ebeaninternal.server.cluster.ClusterManager;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
@@ -22,10 +23,11 @@ public class TransactionManagerOptions {
|
||||
final DataSourceSupplier dataSourceSupplier;
|
||||
final SpiProfileHandler profileHandler;
|
||||
final TransactionScopeManager scopeManager;
|
||||
final SpiLogManager logManager;
|
||||
|
||||
public TransactionManagerOptions(boolean notifyL2CacheInForeground, ServerConfig config, TransactionScopeManager scopeManager, ClusterManager clusterManager,
|
||||
BackgroundExecutor backgroundExecutor, DocStoreUpdateProcessor docStoreUpdateProcessor,
|
||||
BeanDescriptorManager descMgr, DataSourceSupplier dataSourceSupplier, SpiProfileHandler profileHandler) {
|
||||
BeanDescriptorManager descMgr, DataSourceSupplier dataSourceSupplier, SpiProfileHandler profileHandler, SpiLogManager logManager) {
|
||||
|
||||
this.notifyL2CacheInForeground = notifyL2CacheInForeground;
|
||||
this.config = config;
|
||||
@@ -36,6 +38,7 @@ public class TransactionManagerOptions {
|
||||
this.descMgr = descMgr;
|
||||
this.dataSourceSupplier = dataSourceSupplier;
|
||||
this.profileHandler = profileHandler;
|
||||
this.logManager = logManager;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
/**
|
||||
* Scalar type that wraps a local/client side encrypted value.
|
||||
*/
|
||||
public interface LocalEncryptedType {
|
||||
|
||||
/**
|
||||
* Encrypt and return the un-encrypted value.
|
||||
*/
|
||||
Object localEncrypt(Object value);
|
||||
}
|
||||
@@ -32,7 +32,6 @@ public class ScalarTypeBigDecimal extends ScalarTypeBase<BigDecimal> {
|
||||
|
||||
@Override
|
||||
public BigDecimal read(DataReader dataReader) throws SQLException {
|
||||
|
||||
return dataReader.getBigDecimal();
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class ScalarTypeEncryptedWrapper<T> implements ScalarType<T> {
|
||||
public class ScalarTypeEncryptedWrapper<T> implements ScalarType<T>, LocalEncryptedType {
|
||||
|
||||
private final ScalarType<T> wrapped;
|
||||
|
||||
@@ -23,6 +23,12 @@ public class ScalarTypeEncryptedWrapper<T> implements ScalarType<T> {
|
||||
this.dataEncryptSupport = dataEncryptSupport;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object localEncrypt(Object value) {
|
||||
String formatValue = wrapped.format(value);
|
||||
return dataEncryptSupport.encryptObject(formatValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long asVersion(T value) {
|
||||
throw new RuntimeException("not supported");
|
||||
|
||||
@@ -4,7 +4,6 @@ import io.ebean.config.dbplatform.DbPlatformType;
|
||||
import io.ebean.text.TextException;
|
||||
import io.ebean.text.json.EJson;
|
||||
import io.ebeaninternal.json.ModifyAwareOwner;
|
||||
import io.ebeaninternal.util.EncodeUtil;
|
||||
import io.ebeanservice.docstore.api.mapping.DocPropertyType;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
@@ -15,6 +14,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.Map;
|
||||
@@ -104,7 +104,7 @@ public abstract class ScalarTypeJsonMap extends ScalarTypeBase<Map> {
|
||||
b.setNull(Types.BLOB);
|
||||
} else {
|
||||
String rawJson = formatValue(value);
|
||||
b.setBytes(EncodeUtil.utf8ToBytes(rawJson));
|
||||
b.setBytes(rawJson.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import io.ebean.text.TextException;
|
||||
import io.ebeaninternal.util.EncodeUtil;
|
||||
import io.ebeanservice.docstore.api.mapping.DocPropertyType;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
@@ -14,6 +13,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
|
||||
@@ -82,7 +82,7 @@ public abstract class ScalarTypeJsonNode extends ScalarTypeBase<JsonNode> {
|
||||
dataBind.setNull(Types.BLOB);
|
||||
} else {
|
||||
String rawJson = formatValue(value);
|
||||
dataBind.setBlob(EncodeUtil.utf8ToBytes(rawJson));
|
||||
dataBind.setBlob(rawJson.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import io.ebean.config.Encryptor;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Simple AES based encryption and decryption.
|
||||
@@ -32,15 +32,6 @@ public class SimpleAesEncryptor implements Encryptor {
|
||||
return key;
|
||||
}
|
||||
|
||||
private byte[] getKeyBytes(String skey) {
|
||||
|
||||
try {
|
||||
return skey.getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private IvParameterSpec getIvParameterSpec(String initialVector) {
|
||||
return new IvParameterSpec(initialVector.getBytes());
|
||||
}
|
||||
@@ -56,7 +47,7 @@ public class SimpleAesEncryptor implements Encryptor {
|
||||
|
||||
try {
|
||||
|
||||
byte[] keyBytes = getKeyBytes(key);
|
||||
byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
|
||||
IvParameterSpec iv = getIvParameterSpec(key);
|
||||
|
||||
SecretKeySpec sks = new SecretKeySpec(keyBytes, "AES");
|
||||
@@ -81,7 +72,7 @@ public class SimpleAesEncryptor implements Encryptor {
|
||||
String key = paddKey(encryptKey);
|
||||
|
||||
try {
|
||||
byte[] keyBytes = getKeyBytes(key);
|
||||
byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
|
||||
IvParameterSpec iv = getIvParameterSpec(key);
|
||||
|
||||
SecretKeySpec sks = new SecretKeySpec(keyBytes, "AES");
|
||||
@@ -103,12 +94,7 @@ public class SimpleAesEncryptor implements Encryptor {
|
||||
}
|
||||
|
||||
byte[] bytes = decrypt(data, key);
|
||||
try {
|
||||
return new String(bytes, "UTF-8");
|
||||
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return new String(bytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -117,13 +103,8 @@ public class SimpleAesEncryptor implements Encryptor {
|
||||
if (valueFormatValue == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
byte[] d = valueFormatValue.getBytes("UTF-8");
|
||||
return encrypt(d, key);
|
||||
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
byte[] d = valueFormatValue.getBytes(StandardCharsets.UTF_8);
|
||||
return encrypt(d, key);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.util;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
|
||||
public class Md5 {
|
||||
@@ -11,7 +12,7 @@ public class Md5 {
|
||||
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
byte[] digest = md.digest(content.getBytes("UTF-8"));
|
||||
byte[] digest = md.digest(content.getBytes(StandardCharsets.UTF_8));
|
||||
return digestToHex(digest);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("MD5 hashing failed", e);
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
package io.ebeaninternal.util;
|
||||
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
|
||||
|
||||
/**
|
||||
* Utilities for encoding and decoding strings.
|
||||
*/
|
||||
public final class EncodeUtil {
|
||||
|
||||
private EncodeUtil() {
|
||||
/* no instances */
|
||||
}
|
||||
|
||||
/**
|
||||
* URL-encodes the specified UTF-8 string.
|
||||
*/
|
||||
public static String urlEncode(String string) {
|
||||
if (string == null) return null;
|
||||
try {
|
||||
return URLEncoder.encode(string, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new IllegalStateException("Support for UTF-8 is mandated by the Java spec", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* URL-decodes the specified string as a UTF-8 string.
|
||||
*/
|
||||
public static String urlDecode(String string) {
|
||||
if (string == null) return null;
|
||||
try {
|
||||
return URLDecoder.decode(string, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new IllegalStateException("Support for UTF-8 is mandated by the Java spec", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bytes corresponding to the specified ASCII string.
|
||||
*/
|
||||
public static byte[] asciiToBytes(String string) {
|
||||
if (string == null) return null;
|
||||
try {
|
||||
return string.getBytes("US-ASCII");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new IllegalStateException("Support for US-ASCII is mandated by the Java spec", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ASCII string corresponding to the specified bytes.
|
||||
*/
|
||||
public static String bytesToAscii(byte[] data) {
|
||||
try {
|
||||
return new String(data, "US-ASCII");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new IllegalStateException("Support for US-ASCII is mandated by the Java spec", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bytes corresponding to the specified UTF-8 string.
|
||||
*/
|
||||
public static byte[] utf8ToBytes(String string) {
|
||||
if (string == null) return null;
|
||||
try {
|
||||
return string.getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new IllegalStateException("Support for UTF-8 is mandated by the Java spec", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the UTF-8 string corresponding to the specified bytes.
|
||||
*/
|
||||
public static String bytesToUtf8(byte[] data) {
|
||||
try {
|
||||
return new String(data, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new IllegalStateException("Support for UTF-8 is mandated by the Java spec", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the UTF-8 string corresponding to the specified bytes.
|
||||
*/
|
||||
public static String decodeBytes(byte[] data, String encoding) {
|
||||
try {
|
||||
return new String(data, encoding);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException("Error decoding bytes with " + encoding, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
package io.ebeaninternal.util;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
|
||||
/**
|
||||
* Utilities for IO.
|
||||
*/
|
||||
public class IOUtils {
|
||||
|
||||
/**
|
||||
* Reads the entire contents of the specified input stream and returns them
|
||||
* as a byte array.
|
||||
*/
|
||||
public static byte[] read(InputStream in) throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
pump(in, buffer);
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the entire contents of the specified input stream and returns them
|
||||
* as an ASCII string.
|
||||
*/
|
||||
public static String readAscii(InputStream in) throws IOException {
|
||||
return EncodeUtil.bytesToAscii(read(in));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the entire contents of the specified input stream and returns them
|
||||
* as UTF-8 string.
|
||||
*/
|
||||
public static String readUtf8(InputStream in) throws IOException {
|
||||
return EncodeUtil.bytesToUtf8(read(in));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the entire contents of the specified input stream and returns them
|
||||
* as a string using the encoding supplied.
|
||||
*/
|
||||
public static String readEncoded(InputStream in, String encoding) throws IOException {
|
||||
return EncodeUtil.decodeBytes(read(in), encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the entire contents from the reader returning as a String.
|
||||
*/
|
||||
public static String read(Reader reader) throws IOException {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
try {
|
||||
char[] buffer = new char[4096];
|
||||
for (; ; ) {
|
||||
int len = reader.read(buffer);
|
||||
if (len < 0) {
|
||||
break;
|
||||
}
|
||||
sb.append(buffer, 0, len);
|
||||
}
|
||||
} finally {
|
||||
reader.close();
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads data from the specified input stream and copies it to the specified
|
||||
* output stream, until the input stream is at EOF. Both streams are then
|
||||
* closed.
|
||||
*
|
||||
* @throws IOException if the input or output stream is <code>null</code>
|
||||
*/
|
||||
public static void pump(InputStream in, OutputStream out) throws IOException {
|
||||
|
||||
if (in == null) throw new IOException("Input stream is null");
|
||||
if (out == null) throw new IOException("Output stream is null");
|
||||
|
||||
try {
|
||||
try {
|
||||
byte[] buffer = new byte[4096];
|
||||
for (; ; ) {
|
||||
int bytes = in.read(buffer);
|
||||
if (bytes < 0) {
|
||||
break;
|
||||
}
|
||||
out.write(buffer, 0, bytes);
|
||||
}
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
} finally {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user