mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-11-14 06:45:21 +00:00
Merge remote-tracking branch 'upstream/master'
# Conflicts: # ebean-bom/pom.xml # ebean-core/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java # ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/InsertMeta.java # ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNode.java # ebean-core/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeBean.java # ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DdlPlugin.java # ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java # ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/MysqlGenerateMigrationTest.java # ebean-test/pom.xml # pom.xml # tests/pom.xml # tests/test-java16/pom.xml # tests/test-kotlin/pom.xml
This commit is contained in:
commit
5762a5bb9c
100 files changed
+1344
-747
No files matched your search
@@ -13,6 +13,7 @@ import io.ebean.config.dbplatform.mysql.MySql55Platform;
|
||||
import io.ebean.config.dbplatform.mysql.MySqlPlatform;
|
||||
import io.ebean.config.dbplatform.nuodb.NuoDbPlatform;
|
||||
import io.ebean.config.dbplatform.oracle.Oracle11Platform;
|
||||
import io.ebean.config.dbplatform.oracle.Oracle12Platform;
|
||||
import io.ebean.config.dbplatform.oracle.OraclePlatform;
|
||||
import io.ebean.config.dbplatform.postgres.Postgres9Platform;
|
||||
import io.ebean.config.dbplatform.postgres.PostgresPlatform;
|
||||
@@ -25,11 +26,7 @@ import io.ebeaninternal.api.DbOffline;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.*;
|
||||
|
||||
/**
|
||||
* Create a DatabasePlatform from the configuration.
|
||||
@@ -87,6 +84,9 @@ public class DatabasePlatformFactory {
|
||||
if (dbName.equals("oracle11") || dbName.equals("oracle10") || dbName.equals("oracle9")) {
|
||||
return new Oracle11Platform();
|
||||
}
|
||||
if (dbName.equals("oracle12")) {
|
||||
return new Oracle12Platform();
|
||||
}
|
||||
if (dbName.equals("oracle")) {
|
||||
return new OraclePlatform();
|
||||
}
|
||||
@@ -173,7 +173,13 @@ public class DatabasePlatformFactory {
|
||||
}
|
||||
|
||||
private DatabasePlatform oracleVersion(int majorVersion) {
|
||||
return majorVersion < 12 ? new Oracle11Platform() : new OraclePlatform();
|
||||
if (majorVersion < 12) {
|
||||
return new Oracle11Platform();
|
||||
}
|
||||
if (majorVersion < 13) {
|
||||
return new Oracle12Platform();
|
||||
}
|
||||
return new OraclePlatform();
|
||||
}
|
||||
|
||||
private DatabasePlatform mysqlVersion(int majorVersion, int minorVersion) {
|
||||
|
||||
@@ -226,7 +226,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
*/
|
||||
@Override
|
||||
public void addBeanToCollectionWithCreate(EntityBean parentBean, EntityBean detailBean, boolean withCheck) {
|
||||
BeanCollection<?> bc = (BeanCollection<?>) super.getValue(parentBean);
|
||||
BeanCollection<?> bc = beanCollection(parentBean);
|
||||
if (bc == null) {
|
||||
bc = help.createEmpty(parentBean);
|
||||
setValue(parentBean, bc);
|
||||
@@ -234,6 +234,15 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
help.add(bc, detailBean, withCheck);
|
||||
}
|
||||
|
||||
private BeanCollection<?> beanCollection(EntityBean parentBean) {
|
||||
try {
|
||||
return (BeanCollection<?>) super.getValue(parentBean);
|
||||
} catch (ClassCastException e) {
|
||||
// fetching element collection, ok for now
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is considered 'empty' from a save perspective.
|
||||
*/
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.ebeaninternal.server.deploy.InheritInfo;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
|
||||
import javax.persistence.DiscriminatorColumn;
|
||||
import javax.persistence.DiscriminatorType;
|
||||
import javax.persistence.DiscriminatorValue;
|
||||
import javax.persistence.Inheritance;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -105,8 +106,11 @@ public final class DeployInherit {
|
||||
if (da != null) {
|
||||
// lowercase the discriminator column for RawSql and JSON
|
||||
info.setColumnName(da.name().toLowerCase());
|
||||
info.setColumnType(da.discriminatorType());
|
||||
info.setColumnLength(da.length());
|
||||
DiscriminatorType discriminatorType = da.discriminatorType();
|
||||
info.setColumnType(discriminatorType);
|
||||
if (discriminatorType == DiscriminatorType.STRING) {
|
||||
info.setColumnLength(da.length());
|
||||
}
|
||||
info.setColumnDefn(da.columnDefinition());
|
||||
}
|
||||
if (!info.isAbstract()) {
|
||||
|
||||
+1
-4
@@ -10,20 +10,17 @@ public final class DbExpressionHandlerFactory {
|
||||
* Create and return the appropriate platform specific handing of expressions.
|
||||
*/
|
||||
public static DbExpressionHandler from(DatabasePlatform databasePlatform) {
|
||||
Platform platform = databasePlatform.getPlatform();
|
||||
Platform platform = databasePlatform.getPlatform().base();
|
||||
switch (platform) {
|
||||
case H2:
|
||||
return new H2DbExpression();
|
||||
case POSTGRES:
|
||||
case POSTGRES9:
|
||||
return new PostgresDbExpression();
|
||||
case MARIADB:
|
||||
return new MariaDbExpression();
|
||||
case MYSQL55:
|
||||
case MYSQL:
|
||||
return new MySqlDbExpression();
|
||||
case ORACLE:
|
||||
case ORACLE11:
|
||||
return new OracleDbExpression();
|
||||
case DB2:
|
||||
return new Db2DbExpression();
|
||||
|
||||
+2
-1
@@ -10,6 +10,7 @@ final class H2DbExpression extends BasicDbExpression {
|
||||
|
||||
@Override
|
||||
public void bitwise(SpiExpressionRequest request, String propName, BitwiseOp operator, long flags, String compare, long match) {
|
||||
bitwiseFunction(request, propName, operator, compare);
|
||||
String funcName = functionName(operator);
|
||||
request.append(funcName).append("(").append(propName).append(", cast(? as long)) ").append(compare).append(" cast(? as long)");
|
||||
}
|
||||
}
|
||||
@@ -13,11 +13,8 @@ import io.ebeaninternal.server.persist.dmlbind.BindableList;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import static io.ebean.annotation.Platform.MARIADB;
|
||||
import static io.ebean.annotation.Platform.MYSQL;
|
||||
|
||||
/**
|
||||
* Meta data for insert handler. The meta data is for a particular bean type. It
|
||||
* Metadata for insert handler. The metadata is for a particular bean type. It
|
||||
* is considered immutable and is thread safe.
|
||||
*/
|
||||
final class InsertMeta {
|
||||
@@ -153,31 +150,25 @@ final class InsertMeta {
|
||||
private String genSql(boolean nullId, String table, boolean draftTable) {
|
||||
GenerateDmlRequest request = new GenerateDmlRequest();
|
||||
request.setInsertSetMode();
|
||||
|
||||
request.append("insert into ").append(table);
|
||||
if (nullId && noColumnsForInsert(draftTable)) {
|
||||
return request.append(defaultValues()).toString();
|
||||
}
|
||||
|
||||
request.append(" (");
|
||||
if (!nullId) {
|
||||
id.dmlAppend(request);
|
||||
}
|
||||
|
||||
if (shadowFKey != null) {
|
||||
shadowFKey.dmlAppend(request);
|
||||
}
|
||||
|
||||
if (discriminator != null) {
|
||||
discriminator.dmlAppend(request);
|
||||
}
|
||||
|
||||
if (draftTable) {
|
||||
all.dmlAppend(request);
|
||||
} else {
|
||||
allExcludeDraftOnly.dmlAppend(request);
|
||||
}
|
||||
|
||||
request.append(") values (");
|
||||
request.append(request.getInsertBindBuffer());
|
||||
request.append(")");
|
||||
@@ -186,13 +177,14 @@ final class InsertMeta {
|
||||
|
||||
private String defaultValues() {
|
||||
switch (platform.base()) {
|
||||
case MYSQL:
|
||||
case MARIADB:
|
||||
return " values (default)";
|
||||
case DB2:
|
||||
return " (" + id.getIdentityColumn() + ") values (default)";
|
||||
default:
|
||||
return " default values";
|
||||
case MYSQL:
|
||||
case MARIADB:
|
||||
case ORACLE:
|
||||
return " values (default)";
|
||||
case DB2:
|
||||
return " (" + id.getIdentityColumn() + ") values (default)";
|
||||
default:
|
||||
return " default values";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -193,7 +193,7 @@ public final class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfi
|
||||
// set the generated sql back to the query
|
||||
// so its available to the user...
|
||||
query.setGeneratedSql(queryPlan.getSql());
|
||||
SqlTree sqlTree = queryPlan.getSqlTree();
|
||||
SqlTreePlan sqlTree = queryPlan.getSqlTree();
|
||||
this.rootNode = sqlTree.getRootNode();
|
||||
this.manyProperty = sqlTree.getManyProperty();
|
||||
this.sql = queryPlan.getSql();
|
||||
|
||||
@@ -110,7 +110,7 @@ final class CQueryBuilder {
|
||||
sql = buildUpdateSql(request, rootTableAlias, predicates, sqlTree);
|
||||
}
|
||||
// cache the query plan
|
||||
queryPlan = new CQueryPlan(request, sql, sqlTree, predicates.getLogWhereSql());
|
||||
queryPlan = new CQueryPlan(request, sql, sqlTree.plan(), predicates.getLogWhereSql());
|
||||
request.putQueryPlan(queryPlan);
|
||||
return new CQueryUpdate(request, predicates, queryPlan);
|
||||
}
|
||||
@@ -197,7 +197,7 @@ final class CQueryBuilder {
|
||||
SqlTree sqlTree = createSqlTree(request, predicates);
|
||||
SqlLimitResponse s = buildSql(null, request, predicates, sqlTree);
|
||||
|
||||
queryPlan = new CQueryPlan(request, s.getSql(), sqlTree, predicates.getLogWhereSql());
|
||||
queryPlan = new CQueryPlan(request, s.getSql(), sqlTree.plan(), predicates.getLogWhereSql());
|
||||
request.putQueryPlan(queryPlan);
|
||||
return new CQueryFetchSingleAttribute(request, predicates, queryPlan, query.isCountDistinct());
|
||||
}
|
||||
@@ -288,7 +288,7 @@ final class CQueryBuilder {
|
||||
}
|
||||
}
|
||||
// cache the query plan
|
||||
queryPlan = new CQueryPlan(request, sql, sqlTree, predicates.getLogWhereSql());
|
||||
queryPlan = new CQueryPlan(request, sql, sqlTree.plan(), predicates.getLogWhereSql());
|
||||
request.putQueryPlan(queryPlan);
|
||||
return new CQueryRowCount(queryPlan, request, predicates);
|
||||
}
|
||||
@@ -343,7 +343,7 @@ final class CQueryBuilder {
|
||||
if (rawSql) {
|
||||
queryPlan = new CQueryPlanRawSql(request, res, sqlTree, predicates.getLogWhereSql());
|
||||
} else {
|
||||
queryPlan = new CQueryPlan(request, res, sqlTree, false, predicates.getLogWhereSql());
|
||||
queryPlan = new CQueryPlan(request, res, sqlTree.plan(), false, predicates.getLogWhereSql());
|
||||
}
|
||||
BeanDescriptor<T> desc = request.descriptor();
|
||||
if (desc.isReadAuditing()) {
|
||||
|
||||
@@ -55,7 +55,7 @@ public class CQueryPlan implements SpiQueryPlan {
|
||||
private final String sql;
|
||||
private final String hash;
|
||||
private final String logWhereSql;
|
||||
private final SqlTree sqlTree;
|
||||
private final SqlTreePlan sqlTree;
|
||||
|
||||
/**
|
||||
* Encrypted properties required additional binding.
|
||||
@@ -76,7 +76,7 @@ public class CQueryPlan implements SpiQueryPlan {
|
||||
/**
|
||||
* Create a query plan based on a OrmQueryRequest.
|
||||
*/
|
||||
CQueryPlan(OrmQueryRequest<?> request, SqlLimitResponse sqlRes, SqlTree sqlTree, boolean rawSql, String logWhereSql) {
|
||||
CQueryPlan(OrmQueryRequest<?> request, SqlLimitResponse sqlRes, SqlTreePlan sqlTree, boolean rawSql, String logWhereSql) {
|
||||
this.server = request.server();
|
||||
this.dataTimeZone = server.dataTimeZone();
|
||||
this.beanType = request.descriptor().type();
|
||||
@@ -101,7 +101,7 @@ public class CQueryPlan implements SpiQueryPlan {
|
||||
/**
|
||||
* Create a query plan for a raw sql query.
|
||||
*/
|
||||
CQueryPlan(OrmQueryRequest<?> request, String sql, SqlTree sqlTree, String logWhereSql) {
|
||||
CQueryPlan(OrmQueryRequest<?> request, String sql, SqlTreePlan sqlTree, String logWhereSql) {
|
||||
this.server = request.server();
|
||||
this.dataTimeZone = server.dataTimeZone();
|
||||
this.beanType = request.descriptor().type();
|
||||
@@ -249,7 +249,7 @@ public class CQueryPlan implements SpiQueryPlan {
|
||||
return rawSql ? planKey.getPartialKey() + "_" + hash : planKey.getPartialKey();
|
||||
}
|
||||
|
||||
final SqlTree getSqlTree() {
|
||||
final SqlTreePlan getSqlTree() {
|
||||
return sqlTree;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ final class CQueryPlanRawSql extends CQueryPlan {
|
||||
private final int[] rsetIndexPositions;
|
||||
|
||||
CQueryPlanRawSql(OrmQueryRequest<?> request, SqlLimitResponse sqlRes, SqlTree sqlTree, String logWhereSql) {
|
||||
super(request, sqlRes, sqlTree, true, logWhereSql);
|
||||
super(request, sqlRes, sqlTree.plan(), true, logWhereSql);
|
||||
this.rsetIndexPositions = createIndexPositions(request, sqlTree);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,10 @@ final class SqlTree {
|
||||
this.noJoins = !includeJoins;
|
||||
}
|
||||
|
||||
SqlTreePlan plan() {
|
||||
return new SqlTreePlan(rootNode.createLoad(), manyProperty, encryptedProps, dependentTables());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the query mandates SQL Distinct due to ToMany inclusion.
|
||||
*/
|
||||
@@ -123,10 +127,6 @@ final class SqlTree {
|
||||
return inheritanceWhereSql;
|
||||
}
|
||||
|
||||
SqlTreeRoot getRootNode() {
|
||||
return (SqlTreeRoot)rootNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the property that is associated with the many. There can only be one
|
||||
* per SqlSelect. This can be null.
|
||||
@@ -135,10 +135,6 @@ final class SqlTree {
|
||||
return manyProperty;
|
||||
}
|
||||
|
||||
STreeProperty[] getEncryptedProps() {
|
||||
return encryptedProps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the query has a many join.
|
||||
*/
|
||||
@@ -153,7 +149,7 @@ final class SqlTree {
|
||||
/**
|
||||
* Return the tables that are joined in this query.
|
||||
*/
|
||||
Set<String> dependentTables() {
|
||||
private Set<String> dependentTables() {
|
||||
Set<String> tables = new LinkedHashSet<>();
|
||||
rootNode.dependentTables(tables);
|
||||
return tables;
|
||||
|
||||
@@ -35,7 +35,6 @@ public final class SqlTreeBuilder {
|
||||
private final CQueryPredicates predicates;
|
||||
private final boolean subQuery;
|
||||
private final boolean distinctOnPlatform;
|
||||
private final boolean distinctNoLobs;
|
||||
/**
|
||||
* Property if resultSet contains master and detail rows.
|
||||
*/
|
||||
@@ -66,7 +65,6 @@ public final class SqlTreeBuilder {
|
||||
this.query = null;
|
||||
this.subQuery = false;
|
||||
this.distinctOnPlatform = false;
|
||||
this.distinctNoLobs = false;
|
||||
this.queryDetail = queryDetail;
|
||||
this.predicates = predicates;
|
||||
this.temporalMode = SpiQuery.TemporalMode.CURRENT;
|
||||
@@ -99,7 +97,6 @@ public final class SqlTreeBuilder {
|
||||
this.predicates = predicates;
|
||||
this.alias = new SqlTreeAlias(request.baseTableAlias(), temporalMode);
|
||||
this.distinctOnPlatform = builder.isPlatformDistinctOn();
|
||||
this.distinctNoLobs = builder.isPlatformDistinctNoLobs();
|
||||
String fromForUpdate = builder.fromForUpdate(query);
|
||||
CQueryHistorySupport historySupport = builder.getHistorySupport(query);
|
||||
CQueryDraftSupport draftSupport = builder.getDraftSupport(query);
|
||||
@@ -269,9 +266,6 @@ public final class SqlTreeBuilder {
|
||||
if (joinList != null) {
|
||||
joinList.add(selectNode);
|
||||
}
|
||||
if (sqlDistinct && distinctNoLobs) {
|
||||
selectNode.unselectLobs();
|
||||
}
|
||||
return selectNode;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.core.type.ScalarDataReader;
|
||||
import io.ebeaninternal.server.deploy.DbReadContext;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Tree node that loads an entity bean type.
|
||||
*/
|
||||
interface SqlTreeLoad {
|
||||
|
||||
/**
|
||||
* Load the appropriate information from the SqlSelectReader.
|
||||
* <p>
|
||||
* At a high level this actually controls the reading of the data from the
|
||||
* jdbc resultSet and putting it into the bean etc.
|
||||
* </p>
|
||||
*/
|
||||
EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean contextBean) throws SQLException;
|
||||
|
||||
/**
|
||||
* Return the reader for the single attribute query.
|
||||
*/
|
||||
ScalarDataReader<?> getSingleAttributeReader();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,373 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.EntityBeanIntercept;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.core.type.ScalarDataReader;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiQuery.Mode;
|
||||
import io.ebeaninternal.server.deploy.DbReadContext;
|
||||
import io.ebeaninternal.server.deploy.InheritInfo;
|
||||
import io.ebeaninternal.server.deploy.id.IdBinder;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Normal bean included in the query.
|
||||
*/
|
||||
class SqlTreeLoadBean implements SqlTreeLoad {
|
||||
|
||||
final STreeType desc;
|
||||
final IdBinder idBinder;
|
||||
final SqlTreeLoad[] children;
|
||||
private final boolean partialObject;
|
||||
private final STreeProperty[] properties;
|
||||
private final STreePropertyAssoc nodeBeanProp;
|
||||
final boolean readId;
|
||||
private final boolean readIdNormal;
|
||||
private final boolean disableLazyLoad;
|
||||
private final InheritInfo inheritInfo;
|
||||
final String prefix;
|
||||
private final Map<String, String> pathMap;
|
||||
final STreePropertyAssocMany lazyLoadParent;
|
||||
private final SpiQuery.TemporalMode temporalMode;
|
||||
private final boolean temporalVersions;
|
||||
final IdBinder lazyLoadParentIdBinder;
|
||||
|
||||
SqlTreeLoadBean(SqlTreeNodeBean node) {
|
||||
this.lazyLoadParent = node.lazyLoadParent;
|
||||
this.lazyLoadParentIdBinder = node.lazyLoadParentIdBinder;
|
||||
this.prefix = node.prefix;
|
||||
this.desc = node.desc;
|
||||
this.inheritInfo = desc.inheritInfo();
|
||||
this.idBinder = desc.idBinder();
|
||||
this.temporalMode = node.temporalMode;
|
||||
this.temporalVersions = node.temporalVersions;
|
||||
this.nodeBeanProp = node.nodeBeanProp;
|
||||
this.readId = node.readId;//!aggregationRoot && withId && desc.hasId();
|
||||
this.readIdNormal = readId && !temporalVersions;
|
||||
this.disableLazyLoad = node.disableLazyLoad;// disableLazyLoad || !readIdNormal || desc.isRawSqlBased();
|
||||
this.partialObject = node.partialObject;//props.isPartialObject();
|
||||
this.properties = node.properties;//props.getProps();
|
||||
this.pathMap = node.pathMap;//createPathMap(prefix, desc);
|
||||
this.children = node.createLoadChildren();
|
||||
}
|
||||
|
||||
boolean isRoot() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ScalarDataReader<?> getSingleAttributeReader() {
|
||||
if (properties == null || properties.length == 0) {
|
||||
// if we have no property ask first children (in a distinct select with join)
|
||||
if (children.length == 0) {
|
||||
// expected to be a findIds query
|
||||
return desc.idBinder().getBeanProperty();
|
||||
}
|
||||
return children[0].getSingleAttributeReader();
|
||||
}
|
||||
if (properties[0] instanceof STreePropertyAssocOne) {
|
||||
STreePropertyAssocOne assocOne = (STreePropertyAssocOne)properties[0];
|
||||
if (assocOne.isAssocId()) {
|
||||
return assocOne.idReader();
|
||||
}
|
||||
}
|
||||
return properties[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Load that takes into account inheritance.
|
||||
*/
|
||||
private final class LoadInherit extends Load {
|
||||
|
||||
private LoadInherit(DbReadContext ctx, EntityBean parentBean) {
|
||||
super(ctx, parentBean);
|
||||
}
|
||||
|
||||
@Override
|
||||
void initBeanType() throws SQLException {
|
||||
InheritInfo localInfo = readId ? inheritInfo.readType(ctx) : desc.inheritInfo();
|
||||
if (localInfo == null) {
|
||||
// the bean must be null
|
||||
localIdBinder = idBinder;
|
||||
localDesc = desc;
|
||||
} else {
|
||||
localBean = localInfo.createEntityBean();
|
||||
localType = localInfo.getType();
|
||||
localIdBinder = localInfo.getIdBinder();
|
||||
localDesc = localInfo.desc();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void loadProperties() {
|
||||
// take account of inheritance
|
||||
for (STreeProperty property : properties) {
|
||||
localDesc.inheritanceLoad(sqlBeanLoad, property, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a bean instance.
|
||||
*/
|
||||
class Load {
|
||||
|
||||
final DbReadContext ctx;
|
||||
final EntityBean parentBean;
|
||||
|
||||
Object lazyLoadParentId;
|
||||
Class<?> localType;
|
||||
STreeType localDesc;
|
||||
IdBinder localIdBinder;
|
||||
EntityBean localBean;
|
||||
|
||||
Mode queryMode;
|
||||
PersistenceContext persistenceContext;
|
||||
Object id;
|
||||
EntityBean contextBean;
|
||||
SqlBeanLoad sqlBeanLoad;
|
||||
boolean lazyLoadMany;
|
||||
|
||||
private Load(DbReadContext ctx, EntityBean parentBean) {
|
||||
this.ctx = ctx;
|
||||
this.parentBean = parentBean;
|
||||
}
|
||||
|
||||
private void initLazyParent() throws SQLException {
|
||||
if (lazyLoadParentIdBinder != null) {
|
||||
lazyLoadParentId = lazyLoadParentIdBinder.read(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void initBeanType() throws SQLException {
|
||||
localDesc = desc;
|
||||
localBean = desc.createEntityBean();
|
||||
localIdBinder = idBinder;
|
||||
}
|
||||
|
||||
private void initPersistenceContext() {
|
||||
queryMode = ctx.getQueryMode();
|
||||
persistenceContext = (!readIdNormal) ? null : ctx.getPersistenceContext();
|
||||
}
|
||||
|
||||
private void readId() throws SQLException {
|
||||
if (readId) {
|
||||
id = localIdBinder.readSet(ctx, localBean);
|
||||
if (id == null) {
|
||||
readIdNullBean();
|
||||
} else if (!temporalVersions) {
|
||||
readIdBean();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readIdBean() {
|
||||
// check the PersistenceContext to see if the bean already exists
|
||||
contextBean = (EntityBean) localDesc.contextPutIfAbsent(persistenceContext, id, localBean);
|
||||
if (contextBean == null) {
|
||||
// bean just added to the persistenceContext
|
||||
contextBean = localBean;
|
||||
} else {
|
||||
// bean already exists in persistenceContext
|
||||
|
||||
if (queryMode.isLoadContextBean()) {
|
||||
// if explicitly set loadContextBean to true, then reload
|
||||
localBean = contextBean;
|
||||
} else if (!contextBean._ebean_getIntercept().isFullyLoadedBean()) {
|
||||
// reload if contextBean is partial object
|
||||
localBean = contextBean;
|
||||
// and switch to lazyLoad query mode in order not to overwrite
|
||||
// existing properties in SqlBeanLoad::load
|
||||
queryMode = Mode.LAZYLOAD_BEAN;
|
||||
} else {
|
||||
// ignore the DB data...
|
||||
localBean = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readIdNullBean() {
|
||||
// bean must be null...
|
||||
localBean = null;
|
||||
// ... but there may exist as reference bean in parent which has to be marked as deleted.
|
||||
if (parentBean != null && nodeBeanProp instanceof STreePropertyAssocOne) {
|
||||
contextBean = ((STreePropertyAssocOne)nodeBeanProp).getValueAsEntityBean(parentBean);
|
||||
if (contextBean != null) {
|
||||
desc.markAsDeleted(contextBean);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initSqlLoadBean() {
|
||||
ctx.setCurrentPrefix(prefix, pathMap);
|
||||
ctx.propagateState(localBean);
|
||||
sqlBeanLoad = new SqlBeanLoad(ctx, localType, localBean, queryMode);
|
||||
}
|
||||
|
||||
void loadProperties() {
|
||||
for (STreeProperty property : properties) {
|
||||
property.load(sqlBeanLoad);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadChildren() throws SQLException {
|
||||
if (localBean == null && queryMode == Mode.LAZYLOAD_MANY) {
|
||||
// batch lazy load many into existing contextBean
|
||||
localBean = contextBean;
|
||||
lazyLoadMany = true;
|
||||
}
|
||||
for (SqlTreeLoad child : children) {
|
||||
child.load(ctx, localBean, contextBean);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isLazyLoadManyRoot() {
|
||||
return queryMode == Mode.LAZYLOAD_MANY && isRoot();
|
||||
}
|
||||
|
||||
private EntityBean getContextBean() {
|
||||
return contextBean;
|
||||
}
|
||||
|
||||
private void postLoad() {
|
||||
if (!lazyLoadMany && localBean != null) {
|
||||
ctx.setCurrentPrefix(prefix, pathMap);
|
||||
if (readIdNormal) {
|
||||
createListProxies();
|
||||
}
|
||||
if (temporalMode == SpiQuery.TemporalMode.DRAFT) {
|
||||
localDesc.setDraft(localBean);
|
||||
}
|
||||
localDesc.postLoad(localBean);
|
||||
|
||||
EntityBeanIntercept ebi = localBean._ebean_getIntercept();
|
||||
ebi.setPersistenceContext(persistenceContext);
|
||||
if (Mode.LAZYLOAD_BEAN == queryMode) {
|
||||
// Lazy Load does not reset the dirty state
|
||||
ebi.setLoadedLazy();
|
||||
} else if (readId) {
|
||||
// normal bean loading
|
||||
ebi.setLoaded();
|
||||
}
|
||||
|
||||
if (disableLazyLoad) {
|
||||
// bean does not have an Id or is SqlSelect based
|
||||
ebi.setDisableLazyLoad(true);
|
||||
} else if (partialObject) {
|
||||
if (readId) {
|
||||
// register for lazy loading
|
||||
ctx.register(null, ebi);
|
||||
}
|
||||
} else {
|
||||
ebi.setFullyLoadedBean(true);
|
||||
}
|
||||
|
||||
if (ctx.isAutoTuneProfiling() && !disableLazyLoad) {
|
||||
// collect autoTune profiling for this bean...
|
||||
ctx.profileBean(ebi, prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create lazy loading proxies for the Many's except for the one that is
|
||||
* included in the actual query.
|
||||
*/
|
||||
private void createListProxies() {
|
||||
STreePropertyAssocMany fetchedMany = ctx.getManyProperty();
|
||||
boolean forceNewReference = queryMode == Mode.REFRESH_BEAN;
|
||||
for (STreePropertyAssocMany many : localDesc.propsMany()) {
|
||||
if (many != fetchedMany) {
|
||||
// create a proxy for the many (deferred fetching)
|
||||
BeanCollection<?> ref = many.createReference(localBean, forceNewReference);
|
||||
if (ref != null) {
|
||||
if (disableLazyLoad) {
|
||||
ref.setDisableLazyLoad(true);
|
||||
}
|
||||
if (!ref.isRegisteredWithLoadContext()) {
|
||||
ctx.register(many.asMany(), ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setBeanToParent() {
|
||||
if (parentBean != null) {
|
||||
// set this back to the parentBean
|
||||
nodeBeanProp.setValue(parentBean, contextBean);
|
||||
}
|
||||
}
|
||||
|
||||
private EntityBean complete() {
|
||||
if (!readIdNormal) {
|
||||
// a bean with no Id (never found in context)
|
||||
if (lazyLoadParentId != null) {
|
||||
ctx.setLazyLoadedChildBean(localBean, lazyLoadParentId);
|
||||
}
|
||||
return localBean;
|
||||
} else {
|
||||
if (lazyLoadParentId != null) {
|
||||
ctx.setLazyLoadedChildBean(contextBean, lazyLoadParentId);
|
||||
}
|
||||
return contextBean;
|
||||
}
|
||||
}
|
||||
|
||||
private void initialise() throws SQLException {
|
||||
initLazyParent();
|
||||
initBeanType();
|
||||
initPersistenceContext();
|
||||
readId();
|
||||
initSqlLoadBean();
|
||||
loadProperties();
|
||||
loadChildren();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the load returning the loaded bean.
|
||||
*/
|
||||
final EntityBean perform() throws SQLException {
|
||||
initialise();
|
||||
if (isLazyLoadManyRoot()) {
|
||||
return getContextBean();
|
||||
}
|
||||
postLoad();
|
||||
setBeanToParent();
|
||||
return complete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this bean was already in the context. If already in the
|
||||
* context we need to check if it is already contained in the collection.
|
||||
*/
|
||||
final boolean isContextBean() {
|
||||
return localBean == null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* read the properties from the resultSet.
|
||||
*/
|
||||
@Override
|
||||
public EntityBean load(DbReadContext ctx, EntityBean parentBean, EntityBean contextParent) throws SQLException {
|
||||
return createLoad(ctx, parentBean).perform();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the loader with or without inheritance.
|
||||
*/
|
||||
final Load createLoad(DbReadContext ctx, EntityBean parentBean) {
|
||||
return (inheritInfo != null) ? new LoadInherit(ctx, parentBean) : new Load(ctx, parentBean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SqlTreeLoadBean: " + desc;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.deploy.DbReadContext;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
final class SqlTreeLoadManyRoot extends SqlTreeLoadBean {
|
||||
|
||||
private final STreePropertyAssocMany manyProp;
|
||||
|
||||
SqlTreeLoadManyRoot(SqlTreeNodeManyRoot node) {
|
||||
super(node);
|
||||
this.manyProp = node.manyProp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityBean load(DbReadContext cquery, EntityBean parentBean, EntityBean contextParent) throws SQLException {
|
||||
// pass in null for parentBean because added to a collection rather than set to the parentBean
|
||||
Load load = createLoad(cquery, null);
|
||||
EntityBean detailBean = load.perform();
|
||||
if (contextParent != null) {
|
||||
// Add to the collection and initialise collection if needed
|
||||
// A null detailBean may initialise an empty collection
|
||||
// Check for bean existing in collection based on load.isContextBean()
|
||||
manyProp.addBeanToCollectionWithCreate(contextParent, detailBean, load.isContextBean());
|
||||
}
|
||||
return detailBean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.Version;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.deploy.DbReadContext;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
|
||||
/**
|
||||
* Represents the root node of the Sql Tree.
|
||||
*/
|
||||
final class SqlTreeLoadRoot extends SqlTreeLoadBean implements SqlTreeRoot {
|
||||
|
||||
SqlTreeLoadRoot(SqlTreeNodeBean node) {
|
||||
super(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isRoot() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityBean load(DbReadContext ctx) throws SQLException {
|
||||
return load(ctx, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the version bean.
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Version<T> loadVersion(DbReadContext ctx) throws SQLException {
|
||||
// read the sys period lower and upper bounds
|
||||
// these are always the first 2 columns in the resultSet
|
||||
Timestamp start = ctx.getDataReader().getTimestamp();
|
||||
Timestamp end = ctx.getDataReader().getTimestamp();
|
||||
T bean = (T) load(ctx, null, null);
|
||||
return new Version<>(bean, start, end);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,13 +1,8 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.Version;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.core.type.ScalarDataReader;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.deploy.DbReadContext;
|
||||
import io.ebeaninternal.server.deploy.DbSqlContext;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -64,25 +59,11 @@ interface SqlTreeNode {
|
||||
*/
|
||||
void addSoftDeletePredicate(SpiQuery<?> query);
|
||||
|
||||
/**
|
||||
* Load the appropriate information from the SqlSelectReader.
|
||||
* <p>
|
||||
* At a high level this actually controls the reading of the data from the
|
||||
* jdbc resultSet and putting it into the bean etc.
|
||||
* </p>
|
||||
*/
|
||||
EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean contextBean) throws SQLException;
|
||||
|
||||
/**
|
||||
* Return true if the query has a many join.
|
||||
*/
|
||||
boolean hasMany();
|
||||
|
||||
/**
|
||||
* Return the reader for the single attribute query.
|
||||
*/
|
||||
ScalarDataReader<?> getSingleAttributeReader();
|
||||
|
||||
/**
|
||||
* Return true if the query is known to only have a single property selected.
|
||||
*/
|
||||
@@ -94,7 +75,7 @@ interface SqlTreeNode {
|
||||
void dependentTables(Set<String> tables);
|
||||
|
||||
/**
|
||||
* Unselect lobs (for distinct queries on DB2 and Oracle).
|
||||
* Create the loader for this node.
|
||||
*/
|
||||
default void unselectLobs() {};
|
||||
SqlTreeLoad createLoad();
|
||||
}
|
||||
@@ -1,33 +1,20 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.EntityBeanIntercept;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.ScalarDataReader;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiQuery.Mode;
|
||||
import io.ebeaninternal.server.deploy.DbReadContext;
|
||||
import io.ebeaninternal.server.deploy.DbSqlContext;
|
||||
import io.ebeaninternal.server.deploy.InheritInfo;
|
||||
import io.ebeaninternal.server.deploy.TableJoin;
|
||||
import io.ebeaninternal.server.deploy.id.IdBinder;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Normal bean included in the query.
|
||||
*/
|
||||
class SqlTreeNodeBean implements SqlTreeNode {
|
||||
|
||||
private static final SqlTreeLoad[] NO_LOAD_CHILDREN = new SqlTreeLoad[0];
|
||||
private static final SqlTreeNode[] NO_CHILDREN = new SqlTreeNode[0];
|
||||
|
||||
final STreeType desc;
|
||||
@@ -39,33 +26,33 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
/**
|
||||
* Set to true if this is a partial object fetch.
|
||||
*/
|
||||
private boolean partialObject;
|
||||
private STreeProperty[] properties;
|
||||
final boolean partialObject;
|
||||
final STreeProperty[] properties;
|
||||
/**
|
||||
* Extra where clause added by Where annotation on associated many.
|
||||
*/
|
||||
private final String extraWhere;
|
||||
private final STreePropertyAssoc nodeBeanProp;
|
||||
final String extraWhere;
|
||||
final STreePropertyAssoc nodeBeanProp;
|
||||
/**
|
||||
* False if report bean and has no id property.
|
||||
*/
|
||||
final boolean readId;
|
||||
private final boolean readIdNormal;
|
||||
private final boolean disableLazyLoad;
|
||||
private final InheritInfo inheritInfo;
|
||||
final boolean readIdNormal;
|
||||
final boolean disableLazyLoad;
|
||||
final InheritInfo inheritInfo;
|
||||
final String prefix;
|
||||
private final Map<String, String> pathMap;
|
||||
final Map<String, String> pathMap;
|
||||
final STreePropertyAssocMany lazyLoadParent;
|
||||
private final SpiQuery.TemporalMode temporalMode;
|
||||
private final boolean temporalVersions;
|
||||
private final IdBinder lazyLoadParentIdBinder;
|
||||
final SpiQuery.TemporalMode temporalMode;
|
||||
final boolean temporalVersions;
|
||||
final IdBinder lazyLoadParentIdBinder;
|
||||
String baseTableAlias;
|
||||
/**
|
||||
* Table alias set if this bean node includes a join to a intersection
|
||||
* table and that table has history support.
|
||||
*/
|
||||
private boolean intersectionAsOfTableAlias;
|
||||
private final boolean aggregation;
|
||||
boolean intersectionAsOfTableAlias;
|
||||
final boolean aggregation;
|
||||
|
||||
/**
|
||||
* Construct for leaf node.
|
||||
@@ -111,8 +98,23 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
pathMap = createPathMap(prefix, desc);
|
||||
}
|
||||
|
||||
boolean isRoot() {
|
||||
return false;
|
||||
@Override
|
||||
public SqlTreeLoad createLoad() {
|
||||
return new SqlTreeLoadBean(this);
|
||||
}
|
||||
|
||||
protected SqlTreeLoad[] createLoadChildren() {
|
||||
if (children.length == 0) {
|
||||
return NO_LOAD_CHILDREN;
|
||||
}
|
||||
List<SqlTreeLoad> loadChildren = new ArrayList<>(children.length);
|
||||
for (SqlTreeNode child : children) {
|
||||
SqlTreeLoad load = child.createLoad();
|
||||
if (load != null) {
|
||||
loadChildren.add(load);
|
||||
}
|
||||
}
|
||||
return loadChildren.toArray(new SqlTreeLoad[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -120,25 +122,6 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
return properties != null && properties.length == 1 && children.length == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ScalarDataReader<?> getSingleAttributeReader() {
|
||||
if (properties == null || properties.length == 0) {
|
||||
// if we have no property ask first children (in a distinct select with join)
|
||||
if (children.length == 0) {
|
||||
// expected to be a findIds query
|
||||
return desc.idBinder().getBeanProperty();
|
||||
}
|
||||
return children[0].getSingleAttributeReader();
|
||||
}
|
||||
if (properties[0] instanceof STreePropertyAssocOne) {
|
||||
STreePropertyAssocOne assocOne = (STreePropertyAssocOne)properties[0];
|
||||
if (assocOne.isAssocId()) {
|
||||
return assocOne.idReader();
|
||||
}
|
||||
}
|
||||
return properties[0];
|
||||
}
|
||||
|
||||
private Map<String, String> createPathMap(String prefix, STreeType desc) {
|
||||
HashMap<String, String> m = new HashMap<>();
|
||||
for (STreePropertyAssocMany many : desc.propsMany()) {
|
||||
@@ -176,293 +159,6 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load that takes into account inheritance.
|
||||
*/
|
||||
private final class LoadInherit extends Load {
|
||||
|
||||
private LoadInherit(DbReadContext ctx, EntityBean parentBean) {
|
||||
super(ctx, parentBean);
|
||||
}
|
||||
|
||||
@Override
|
||||
void initBeanType() throws SQLException {
|
||||
InheritInfo localInfo = readId ? inheritInfo.readType(ctx) : desc.inheritInfo();
|
||||
if (localInfo == null) {
|
||||
// the bean must be null
|
||||
localIdBinder = idBinder;
|
||||
localDesc = desc;
|
||||
} else {
|
||||
localBean = localInfo.createEntityBean();
|
||||
localType = localInfo.getType();
|
||||
localIdBinder = localInfo.getIdBinder();
|
||||
localDesc = localInfo.desc();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void loadProperties() {
|
||||
// take account of inheritance
|
||||
for (STreeProperty property : properties) {
|
||||
localDesc.inheritanceLoad(sqlBeanLoad, property, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a bean instance.
|
||||
*/
|
||||
class Load {
|
||||
|
||||
final DbReadContext ctx;
|
||||
final EntityBean parentBean;
|
||||
|
||||
Object lazyLoadParentId;
|
||||
Class<?> localType;
|
||||
STreeType localDesc;
|
||||
IdBinder localIdBinder;
|
||||
EntityBean localBean;
|
||||
|
||||
Mode queryMode;
|
||||
PersistenceContext persistenceContext;
|
||||
Object id;
|
||||
EntityBean contextBean;
|
||||
SqlBeanLoad sqlBeanLoad;
|
||||
boolean lazyLoadMany;
|
||||
|
||||
private Load(DbReadContext ctx, EntityBean parentBean) {
|
||||
this.ctx = ctx;
|
||||
this.parentBean = parentBean;
|
||||
}
|
||||
|
||||
private void initLazyParent() throws SQLException {
|
||||
if (lazyLoadParentIdBinder != null) {
|
||||
lazyLoadParentId = lazyLoadParentIdBinder.read(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void initBeanType() throws SQLException {
|
||||
localDesc = desc;
|
||||
localBean = desc.createEntityBean();
|
||||
localIdBinder = idBinder;
|
||||
}
|
||||
|
||||
private void initPersistenceContext() {
|
||||
queryMode = ctx.getQueryMode();
|
||||
persistenceContext = (!readIdNormal) ? null : ctx.getPersistenceContext();
|
||||
}
|
||||
|
||||
private void readId() throws SQLException {
|
||||
if (readId) {
|
||||
id = localIdBinder.readSet(ctx, localBean);
|
||||
if (id == null) {
|
||||
readIdNullBean();
|
||||
} else if (!temporalVersions) {
|
||||
readIdBean();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readIdBean() {
|
||||
// check the PersistenceContext to see if the bean already exists
|
||||
contextBean = (EntityBean) localDesc.contextPutIfAbsent(persistenceContext, id, localBean);
|
||||
if (contextBean == null) {
|
||||
// bean just added to the persistenceContext
|
||||
contextBean = localBean;
|
||||
} else {
|
||||
// bean already exists in persistenceContext
|
||||
|
||||
if (queryMode.isLoadContextBean()) {
|
||||
// if explicitly set loadContextBean to true, then reload
|
||||
localBean = contextBean;
|
||||
} else if (!contextBean._ebean_getIntercept().isFullyLoadedBean()) {
|
||||
// reload if contextBean is partial object
|
||||
localBean = contextBean;
|
||||
// and switch to lazyLoad query mode in order not to overwrite
|
||||
// existing properties in SqlBeanLoad::load
|
||||
queryMode = Mode.LAZYLOAD_BEAN;
|
||||
} else {
|
||||
// ignore the DB data...
|
||||
localBean = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readIdNullBean() {
|
||||
// bean must be null...
|
||||
localBean = null;
|
||||
// ... but there may exist as reference bean in parent which has to be marked as deleted.
|
||||
if (parentBean != null && nodeBeanProp instanceof STreePropertyAssocOne) {
|
||||
contextBean = ((STreePropertyAssocOne)nodeBeanProp).getValueAsEntityBean(parentBean);
|
||||
if (contextBean != null) {
|
||||
desc.markAsDeleted(contextBean);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initSqlLoadBean() {
|
||||
ctx.setCurrentPrefix(prefix, pathMap);
|
||||
ctx.propagateState(localBean);
|
||||
sqlBeanLoad = new SqlBeanLoad(ctx, localType, localBean, queryMode);
|
||||
}
|
||||
|
||||
void loadProperties() {
|
||||
for (STreeProperty property : properties) {
|
||||
property.load(sqlBeanLoad);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadChildren() throws SQLException {
|
||||
if (localBean == null && queryMode == Mode.LAZYLOAD_MANY) {
|
||||
// batch lazy load many into existing contextBean
|
||||
localBean = contextBean;
|
||||
lazyLoadMany = true;
|
||||
}
|
||||
for (SqlTreeNode child : children) {
|
||||
child.load(ctx, localBean, contextBean);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isLazyLoadManyRoot() {
|
||||
return queryMode == Mode.LAZYLOAD_MANY && isRoot();
|
||||
}
|
||||
|
||||
private EntityBean getContextBean() {
|
||||
return contextBean;
|
||||
}
|
||||
|
||||
private void postLoad() {
|
||||
if (!lazyLoadMany && localBean != null) {
|
||||
ctx.setCurrentPrefix(prefix, pathMap);
|
||||
if (readIdNormal) {
|
||||
createListProxies();
|
||||
}
|
||||
if (temporalMode == SpiQuery.TemporalMode.DRAFT) {
|
||||
localDesc.setDraft(localBean);
|
||||
}
|
||||
localDesc.postLoad(localBean);
|
||||
|
||||
EntityBeanIntercept ebi = localBean._ebean_getIntercept();
|
||||
ebi.setPersistenceContext(persistenceContext);
|
||||
if (Mode.LAZYLOAD_BEAN == queryMode) {
|
||||
// Lazy Load does not reset the dirty state
|
||||
ebi.setLoadedLazy();
|
||||
} else if (readId) {
|
||||
// normal bean loading
|
||||
ebi.setLoaded();
|
||||
}
|
||||
|
||||
if (disableLazyLoad) {
|
||||
// bean does not have an Id or is SqlSelect based
|
||||
ebi.setDisableLazyLoad(true);
|
||||
} else if (partialObject) {
|
||||
if (readId) {
|
||||
// register for lazy loading
|
||||
ctx.register(null, ebi);
|
||||
}
|
||||
} else {
|
||||
ebi.setFullyLoadedBean(true);
|
||||
}
|
||||
|
||||
if (ctx.isAutoTuneProfiling() && !disableLazyLoad) {
|
||||
// collect autoTune profiling for this bean...
|
||||
ctx.profileBean(ebi, prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create lazy loading proxies for the Many's except for the one that is
|
||||
* included in the actual query.
|
||||
*/
|
||||
private void createListProxies() {
|
||||
STreePropertyAssocMany fetchedMany = ctx.getManyProperty();
|
||||
boolean forceNewReference = queryMode == Mode.REFRESH_BEAN;
|
||||
for (STreePropertyAssocMany many : localDesc.propsMany()) {
|
||||
if (many != fetchedMany) {
|
||||
// create a proxy for the many (deferred fetching)
|
||||
BeanCollection<?> ref = many.createReference(localBean, forceNewReference);
|
||||
if (ref != null) {
|
||||
if (disableLazyLoad) {
|
||||
ref.setDisableLazyLoad(true);
|
||||
}
|
||||
if (!ref.isRegisteredWithLoadContext()) {
|
||||
ctx.register(many.asMany(), ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setBeanToParent() {
|
||||
if (parentBean != null) {
|
||||
// set this back to the parentBean
|
||||
nodeBeanProp.setValue(parentBean, contextBean);
|
||||
}
|
||||
}
|
||||
|
||||
private EntityBean complete() {
|
||||
if (!readIdNormal) {
|
||||
// a bean with no Id (never found in context)
|
||||
if (lazyLoadParentId != null) {
|
||||
ctx.setLazyLoadedChildBean(localBean, lazyLoadParentId);
|
||||
}
|
||||
return localBean;
|
||||
} else {
|
||||
if (lazyLoadParentId != null) {
|
||||
ctx.setLazyLoadedChildBean(contextBean, lazyLoadParentId);
|
||||
}
|
||||
return contextBean;
|
||||
}
|
||||
}
|
||||
|
||||
private void initialise() throws SQLException {
|
||||
initLazyParent();
|
||||
initBeanType();
|
||||
initPersistenceContext();
|
||||
readId();
|
||||
initSqlLoadBean();
|
||||
loadProperties();
|
||||
loadChildren();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the load returning the loaded bean.
|
||||
*/
|
||||
final EntityBean perform() throws SQLException {
|
||||
initialise();
|
||||
if (isLazyLoadManyRoot()) {
|
||||
return getContextBean();
|
||||
}
|
||||
postLoad();
|
||||
setBeanToParent();
|
||||
return complete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this bean was already in the context. If already in the
|
||||
* context we need to check if it is already contained in the collection.
|
||||
*/
|
||||
final boolean isContextBean() {
|
||||
return localBean == null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* read the properties from the resultSet.
|
||||
*/
|
||||
@Override
|
||||
public EntityBean load(DbReadContext ctx, EntityBean parentBean, EntityBean contextParent) throws SQLException {
|
||||
return createLoad(ctx, parentBean).perform();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the loader with or without inheritance.
|
||||
*/
|
||||
final Load createLoad(DbReadContext ctx, EntityBean parentBean) {
|
||||
return (inheritInfo != null) ? new LoadInherit(ctx, parentBean) : new Load(ctx, parentBean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void appendGroupBy(DbSqlContext ctx, boolean subQuery) {
|
||||
ctx.pushJoin(prefix);
|
||||
@@ -703,32 +399,4 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unselectLobs() {
|
||||
if (children != null) {
|
||||
for (SqlTreeNode child : children) {
|
||||
child.unselectLobs();
|
||||
}
|
||||
}
|
||||
if (hasLob()) {
|
||||
List<STreeProperty> lst = new ArrayList<>();
|
||||
for (STreeProperty prop : properties) {
|
||||
if (!prop.isDbLob()) {
|
||||
lst.add(prop);
|
||||
}
|
||||
}
|
||||
properties = lst.toArray(new STreeProperty[0]);
|
||||
partialObject = true;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasLob() {
|
||||
for (STreeProperty prop : properties) {
|
||||
if (prop.isDbLob()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,11 @@ final class SqlTreeNodeExtraJoin implements SqlTreeNode {
|
||||
this.manyJoin = assocBeanProperty instanceof STreePropertyAssocMany;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlTreeLoad createLoad() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleProperty() {
|
||||
return false;
|
||||
@@ -73,11 +78,6 @@ final class SqlTreeNodeExtraJoin implements SqlTreeNode {
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScalarType<?> getSingleAttributeReader() {
|
||||
throw new IllegalStateException("No expected");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the extra join is a many join.
|
||||
* <p>
|
||||
@@ -184,14 +184,6 @@ final class SqlTreeNodeExtraJoin implements SqlTreeNode {
|
||||
public void appendWhere(DbSqlContext ctx) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing.
|
||||
*/
|
||||
@Override
|
||||
public EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean parentBean) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMany() {
|
||||
return manyJoin;
|
||||
|
||||
+4
-10
@@ -33,13 +33,13 @@ final class SqlTreeNodeFormulaWhereJoin implements SqlTreeNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleProperty() {
|
||||
return true;
|
||||
public SqlTreeLoad createLoad() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScalarType<?> getSingleAttributeReader() {
|
||||
throw new IllegalStateException("No expected");
|
||||
public boolean isSingleProperty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -98,12 +98,6 @@ final class SqlTreeNodeFormulaWhereJoin implements SqlTreeNode {
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean parentBean) {
|
||||
// nothing to do here
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMany() {
|
||||
return true;
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.deploy.DbReadContext;
|
||||
import io.ebeaninternal.server.deploy.DbSqlContext;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
final class SqlTreeNodeManyRoot extends SqlTreeNodeBean {
|
||||
|
||||
private final STreePropertyAssocMany manyProp;
|
||||
final STreePropertyAssocMany manyProp;
|
||||
|
||||
SqlTreeNodeManyRoot(String prefix, STreePropertyAssocMany prop, SqlTreeProperties props, List<SqlTreeNode> myList,
|
||||
boolean withId, SpiQuery.TemporalMode temporalMode, boolean disableLazyLoad) {
|
||||
@@ -19,22 +16,13 @@ final class SqlTreeNodeManyRoot extends SqlTreeNodeBean {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMany() {
|
||||
return true;
|
||||
public SqlTreeLoad createLoad() {
|
||||
return new SqlTreeLoadManyRoot(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityBean load(DbReadContext cquery, EntityBean parentBean, EntityBean contextParent) throws SQLException {
|
||||
// pass in null for parentBean because added to a collection rather than set to the parentBean
|
||||
SqlTreeNodeBean.Load load = createLoad(cquery, null);
|
||||
EntityBean detailBean = load.perform();
|
||||
if (contextParent != null) {
|
||||
// Add to the collection and initialise collection if needed
|
||||
// A null detailBean may initialise an empty collection
|
||||
// Check for bean existing in collection based on load.isContextBean()
|
||||
manyProp.addBeanToCollectionWithCreate(contextParent, detailBean, load.isContextBean());
|
||||
}
|
||||
return detailBean;
|
||||
public boolean hasMany() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+4
-13
@@ -1,10 +1,7 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.deploy.DbReadContext;
|
||||
import io.ebeaninternal.server.deploy.DbSqlContext;
|
||||
import io.ebeaninternal.server.deploy.TableJoin;
|
||||
|
||||
@@ -37,13 +34,13 @@ final class SqlTreeNodeManyWhereJoin implements SqlTreeNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleProperty() {
|
||||
return false;
|
||||
public SqlTreeLoad createLoad() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScalarType<?> getSingleAttributeReader() {
|
||||
throw new IllegalStateException("No expected");
|
||||
public boolean isSingleProperty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -132,12 +129,6 @@ final class SqlTreeNodeManyWhereJoin implements SqlTreeNode {
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean parentBean) {
|
||||
// nothing to do here
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMany() {
|
||||
return true;
|
||||
|
||||
@@ -1,21 +1,16 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.Version;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.deploy.DbReadContext;
|
||||
import io.ebeaninternal.server.deploy.DbSqlContext;
|
||||
import io.ebeaninternal.server.deploy.TableJoin;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Represents the root node of the Sql Tree.
|
||||
*/
|
||||
final class SqlTreeNodeRoot extends SqlTreeNodeBean implements SqlTreeRoot {
|
||||
final class SqlTreeNodeRoot extends SqlTreeNodeBean {
|
||||
|
||||
private final TableJoin includeJoin;
|
||||
private final boolean sqlDistinct;
|
||||
@@ -34,27 +29,8 @@ final class SqlTreeNodeRoot extends SqlTreeNodeBean implements SqlTreeRoot {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isRoot() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityBean load(DbReadContext ctx) throws SQLException {
|
||||
return load(ctx, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the version bean.
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Version<T> loadVersion(DbReadContext ctx) throws SQLException {
|
||||
// read the sys period lower and upper bounds
|
||||
// these are always the first 2 columns in the resultSet
|
||||
Timestamp start = ctx.getDataReader().getTimestamp();
|
||||
Timestamp end = ctx.getDataReader().getTimestamp();
|
||||
T bean = (T) load(ctx, null, null);
|
||||
return new Version<>(bean, start, end);
|
||||
public SqlTreeLoad createLoad() {
|
||||
return new SqlTreeLoadRoot(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
final class SqlTreePlan {
|
||||
|
||||
private final SqlTreeLoad rootNode;
|
||||
private final STreePropertyAssocMany manyProperty;
|
||||
private final STreeProperty[] encryptedProps;
|
||||
private final Set<String> dependentTables;
|
||||
|
||||
SqlTreePlan(SqlTreeLoad rootNode, STreePropertyAssocMany manyProperty, STreeProperty[] encryptedProps, Set<String> dependentTables) {
|
||||
this.rootNode = rootNode;
|
||||
this.manyProperty = manyProperty;
|
||||
this.encryptedProps = encryptedProps;
|
||||
this.dependentTables = dependentTables;
|
||||
}
|
||||
|
||||
SqlTreeRoot getRootNode() {
|
||||
return (SqlTreeRoot)rootNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the property that is associated with the many. There can only be one
|
||||
* per SqlSelect. This can be null.
|
||||
*/
|
||||
STreePropertyAssocMany getManyProperty() {
|
||||
return manyProperty;
|
||||
}
|
||||
|
||||
STreeProperty[] getEncryptedProps() {
|
||||
return encryptedProps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the tables that are joined in this query.
|
||||
*/
|
||||
Set<String> dependentTables() {
|
||||
return dependentTables;
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,14 @@ final class EnumToDbIntegerMap extends EnumToDbValueMap<Integer> {
|
||||
addInternal(beanValue, dbValue, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getBeanValue(Object dbValue) {
|
||||
if (dbValue instanceof String) {
|
||||
return super.getBeanValue(Integer.parseInt(dbValue.toString()));
|
||||
}
|
||||
return super.getBeanValue(dbValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumToDbIntegerMap add(Object beanValue, String stringDbValue, String name) {
|
||||
try {
|
||||
|
||||
@@ -116,7 +116,8 @@ abstract class EnumToDbValueMap<T> {
|
||||
/**
|
||||
* Return the Bean value given the DB value.
|
||||
*/
|
||||
public Object getBeanValue(T dbValue) {
|
||||
@SuppressWarnings("SuspiciousMethodCalls")
|
||||
public Object getBeanValue(Object dbValue) {
|
||||
if (dbValue == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -125,8 +126,7 @@ abstract class EnumToDbValueMap<T> {
|
||||
beanValue = nameMap.get(dbValue);
|
||||
}
|
||||
if (beanValue == null && !allowNulls) {
|
||||
String msg = "Bean value for " + dbValue + " not found in " + valueMap;
|
||||
throw new IllegalArgumentException(msg);
|
||||
throw new IllegalArgumentException("Bean value for " + dbValue + " not found in " + valueMap);
|
||||
}
|
||||
return beanValue;
|
||||
}
|
||||
|
||||
@@ -73,6 +73,11 @@ public class RsetDataReader implements DataReader {
|
||||
return rset.getObject(pos());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getObject(Class<T> cls) throws SQLException {
|
||||
return rset.getObject(pos(), cls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getBigDecimal() throws SQLException {
|
||||
return rset.getBigDecimal(pos());
|
||||
|
||||
@@ -189,7 +189,12 @@ final class ScalarTypeEnumStandard {
|
||||
if (dbValue == null || dbValue instanceof Enum<?>) {
|
||||
return dbValue;
|
||||
}
|
||||
int ordinal = (Integer) dbValue;
|
||||
int ordinal;
|
||||
if (dbValue instanceof Integer) {
|
||||
ordinal = (Integer) dbValue;
|
||||
} else {
|
||||
ordinal = Integer.parseInt(dbValue.toString());
|
||||
}
|
||||
if (ordinal < 0 || ordinal >= enumArray.length) {
|
||||
String m = "Unexpected ordinal [" + ordinal + "] out of range [" + enumArray.length + "]";
|
||||
throw new IllegalStateException(m);
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import io.ebean.config.JsonConfig;
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebeaninternal.server.core.BasicTypeConverter;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
import java.sql.Date;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
@@ -40,7 +43,7 @@ final class ScalarTypeJodaLocalDate extends ScalarTypeBaseDate<LocalDate> {
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public Date convertToDate(LocalDate value) {
|
||||
return new Date(value .getYear() - 1900, value .getMonthOfYear() - 1, value .getDayOfMonth());
|
||||
return new Date(value.getYear() - 1900, value.getMonthOfYear() - 1, value.getDayOfMonth());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,9 +57,23 @@ final class ScalarTypeJodaLocalDate extends ScalarTypeBaseDate<LocalDate> {
|
||||
@Override
|
||||
public LocalDate toBeanType(Object value) {
|
||||
if (value instanceof java.util.Date) {
|
||||
return LocalDate.fromDateFields((java.util.Date)value);
|
||||
return LocalDate.fromDateFields((java.util.Date) value);
|
||||
}
|
||||
return (LocalDate) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(DataBinder binder, LocalDate value) throws SQLException {
|
||||
if (value == null) {
|
||||
binder.setNull(Types.DATE);
|
||||
} else {
|
||||
binder.setObject(java.time.LocalDate.of(value.getYear(), value.getMonthOfYear(), value.getDayOfMonth()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDate read(DataReader reader) throws SQLException {
|
||||
java.time.LocalDate jtDate = reader.getObject(java.time.LocalDate.class);
|
||||
return jtDate == null ? null : new org.joda.time.LocalDate(jtDate.getYear(), jtDate.getMonthValue(), jtDate.getDayOfMonth());
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import io.ebean.config.JsonConfig;
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebeaninternal.server.core.BasicTypeConverter;
|
||||
|
||||
import java.sql.Date;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.sql.Types;
|
||||
import java.time.LocalDate;
|
||||
@@ -61,4 +64,18 @@ final class ScalarTypeLocalDate extends ScalarTypeBaseDate<LocalDate> {
|
||||
return (LocalDate) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(DataBinder binder, LocalDate value) throws SQLException {
|
||||
if (value == null) {
|
||||
binder.setNull(Types.DATE);
|
||||
} else {
|
||||
binder.setObject(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDate read(DataReader reader) throws SQLException {
|
||||
return reader.getObject(LocalDate.class);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user