mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
21
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5fea5e9be | ||
|
|
bafba0babf | ||
|
|
e4088c5fbe | ||
|
|
0405ceb96c | ||
|
|
ebf96c87bf | ||
|
|
fcaf11f262 | ||
|
|
05df47c0d7 | ||
|
|
a8892292fb | ||
|
|
e876ef8d87 | ||
|
|
315b39813b | ||
|
|
6d5e0c4766 | ||
|
|
a2ce17aec0 | ||
|
|
fcffa92c55 | ||
|
|
7fda8079c2 | ||
|
|
cca9bb4427 | ||
|
|
f45089240a | ||
|
|
8252a51545 | ||
|
|
c440c768cf | ||
|
|
c6ae501354 | ||
|
|
70e2b1c2ae | ||
|
|
211f28eea3 |
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>11.27.1</version>
|
||||
<version>11.28.3</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.27.1</tag>
|
||||
<tag>ebean-11.28.3</tag>
|
||||
</scm>
|
||||
|
||||
<profiles>
|
||||
@@ -123,7 +123,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-datasource</artifactId>
|
||||
<version>4.2.2</version>
|
||||
<version>4.3.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -238,7 +238,7 @@
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>1.4.196</version>
|
||||
<version>1.4.197</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -2763,20 +2763,6 @@ public class ServerConfig {
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated - this does nothing now, we always try to read test configuration.
|
||||
* <p>
|
||||
* Load settings from test-ebean.properties and do nothing if the properties is not found.
|
||||
* <p>
|
||||
* This is typically used when test-ebean.properties is put into the test class path and used
|
||||
* to configure Ebean for running tests.
|
||||
* </p>
|
||||
*/
|
||||
@Deprecated
|
||||
public void loadTestProperties() {
|
||||
// do nothing now ... as we always try to read test configuration and that should only
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the properties that we used for configuration and were set via a call to loadFromProperties().
|
||||
*/
|
||||
|
||||
@@ -15,6 +15,8 @@ public class H2HistoryDdl extends DbTriggerBasedHistoryDdl {
|
||||
private static final String TRIGGER_CLASS = H2HistoryTrigger.class.getName();
|
||||
|
||||
public H2HistoryDdl() {
|
||||
this.sysPeriodType = "timestamp";
|
||||
this.currentTimestamp = "now()";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -7,6 +7,10 @@ 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.datasource.DataSourceAlertFactory;
|
||||
import io.ebean.datasource.DataSourceConfig;
|
||||
import io.ebean.datasource.DataSourceFactory;
|
||||
import io.ebean.datasource.DataSourcePoolListener;
|
||||
import io.ebean.service.SpiContainer;
|
||||
import io.ebeaninternal.api.SpiBackgroundExecutor;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
@@ -15,10 +19,6 @@ import io.ebeaninternal.server.cluster.ClusterManager;
|
||||
import io.ebeaninternal.server.core.bootup.BootupClassPathSearch;
|
||||
import io.ebeaninternal.server.core.bootup.BootupClasses;
|
||||
import io.ebeaninternal.server.lib.ShutdownManager;
|
||||
import io.ebean.datasource.DataSourceAlertFactory;
|
||||
import io.ebean.datasource.DataSourceConfig;
|
||||
import io.ebean.datasource.DataSourceFactory;
|
||||
import io.ebean.datasource.DataSourcePoolListener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -249,7 +249,7 @@ public class DefaultContainer implements SpiContainer {
|
||||
return null;
|
||||
}
|
||||
|
||||
DataSourceFactory factory = config.service(DataSourceFactory.class);
|
||||
DataSourceFactory factory = DataSourceFactory.get();
|
||||
if (factory == null) {
|
||||
throw new IllegalStateException("No DataSourceFactory service implementation found in class path."
|
||||
+ " Probably missing dependency to avaje-datasource?");
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebeaninternal.server.lib.ShutdownManager;
|
||||
|
||||
import javax.servlet.ServletContextEvent;
|
||||
|
||||
/**
|
||||
* Deprecated - migrate to io.ebean.event.ServletContextListener.
|
||||
* <p>
|
||||
* Listens for webserver server starting and stopping events.
|
||||
* <p>
|
||||
* Register this listener in the web.xml configuration file. This will listen
|
||||
* for startup and shutdown events.
|
||||
* </p>
|
||||
*/
|
||||
@Deprecated
|
||||
public class ServletContextListener implements javax.servlet.ServletContextListener {
|
||||
|
||||
/**
|
||||
* The servlet container is stopping.
|
||||
*/
|
||||
@Override
|
||||
public void contextDestroyed(ServletContextEvent event) {
|
||||
ShutdownManager.shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Do nothing on startup.
|
||||
*/
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent event) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.deploy;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
import io.ebeaninternal.server.type.DataReader;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
@@ -28,6 +29,24 @@ abstract class AssocOneHelp {
|
||||
property.targetIdBinder.loadIgnore(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and return the property.
|
||||
*/
|
||||
Object read(DataReader reader) throws SQLException {
|
||||
return property.read(reader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and return the property setting value into the bean.
|
||||
*/
|
||||
Object readSet(DataReader reader, EntityBean bean) throws SQLException {
|
||||
Object val = read(reader);
|
||||
if (bean != null) {
|
||||
property.setValue(bean, val);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and return the bean.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.type.DataReader;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
@@ -9,14 +10,41 @@ import java.sql.SQLException;
|
||||
*/
|
||||
final class AssocOneHelpEmbedded extends AssocOneHelp {
|
||||
|
||||
public AssocOneHelpEmbedded(BeanPropertyAssocOne<?> property) {
|
||||
AssocOneHelpEmbedded(BeanPropertyAssocOne<?> property) {
|
||||
super(property);
|
||||
}
|
||||
|
||||
@Override
|
||||
void loadIgnore(DbReadContext ctx) {
|
||||
for (int i = 0; i < property.embeddedProps.length; i++) {
|
||||
property.embeddedProps[i].loadIgnore(ctx);
|
||||
for (BeanProperty property : property.embeddedProps) {
|
||||
property.loadIgnore(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
Object readSet(DataReader reader, EntityBean bean) throws SQLException {
|
||||
Object dbVal = read(reader);
|
||||
if (bean != null) {
|
||||
property.setValue(bean, dbVal);
|
||||
}
|
||||
return dbVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
Object read(DataReader reader) throws SQLException {
|
||||
|
||||
EntityBean embeddedBean = property.targetDescriptor.createEntityBean();
|
||||
boolean notNull = false;
|
||||
for (BeanProperty property : property.embeddedProps) {
|
||||
Object value = property.readSet(reader, embeddedBean);
|
||||
if (value != null) {
|
||||
notNull = true;
|
||||
}
|
||||
}
|
||||
if (notNull) {
|
||||
return embeddedBean;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,8 +67,8 @@ final class AssocOneHelpEmbedded extends AssocOneHelp {
|
||||
EntityBean embeddedBean = property.targetDescriptor.createEntityBean();
|
||||
|
||||
boolean notNull = false;
|
||||
for (int i = 0; i < property.embeddedProps.length; i++) {
|
||||
Object value = property.embeddedProps[i].readSet(ctx, embeddedBean);
|
||||
for (BeanProperty property : property.embeddedProps) {
|
||||
Object value = property.readSet(ctx, embeddedBean);
|
||||
if (value != null) {
|
||||
notNull = true;
|
||||
}
|
||||
@@ -55,8 +83,8 @@ final class AssocOneHelpEmbedded extends AssocOneHelp {
|
||||
|
||||
@Override
|
||||
void appendSelect(DbSqlContext ctx, boolean subQuery) {
|
||||
for (int i = 0; i < property.embeddedProps.length; i++) {
|
||||
property.embeddedProps[i].appendSelect(ctx, subQuery);
|
||||
for (BeanProperty property : property.embeddedProps) {
|
||||
property.appendSelect(ctx, subQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.DataReader;
|
||||
import io.ebeaninternal.server.type.LocalEncryptedType;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
import io.ebeaninternal.server.type.ScalarTypeBoolean;
|
||||
@@ -640,6 +641,23 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object read(DataReader reader) throws SQLException {
|
||||
return scalarType.read(reader);
|
||||
}
|
||||
|
||||
public Object readSet(DataReader reader, EntityBean bean) throws SQLException {
|
||||
try {
|
||||
Object value = scalarType.read(reader);
|
||||
if (bean != null) {
|
||||
setValue(bean, value);
|
||||
}
|
||||
return value;
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException("Error readSet on " + descriptor + "." + name, e);
|
||||
}
|
||||
}
|
||||
|
||||
public Object read(DbReadContext ctx) throws SQLException {
|
||||
return scalarType.read(ctx.getDataReader());
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@ import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebeaninternal.server.query.STreePropertyAssocOne;
|
||||
import io.ebeaninternal.server.query.SqlBeanLoad;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
import io.ebeaninternal.server.type.DataReader;
|
||||
import io.ebeaninternal.server.type.ScalarDataReader;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.IOException;
|
||||
@@ -434,8 +435,8 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScalarType<?> getIdScalarType() {
|
||||
return targetDescriptor.getIdProperty().getScalarType();
|
||||
public ScalarDataReader<?> getIdReader() {
|
||||
return targetDescriptor.getIdProperty();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -583,18 +584,23 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object readSet(DataReader reader, EntityBean bean) throws SQLException {
|
||||
return localHelp.readSet(reader, bean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object read(DataReader reader) throws SQLException {
|
||||
return localHelp.read(reader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException {
|
||||
return localHelp.readSet(ctx, bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the data from the resultSet effectively ignoring it and returning null.
|
||||
*/
|
||||
@Override
|
||||
public Object read(DbReadContext ctx) throws SQLException {
|
||||
// just read the resultSet incrementing the column index
|
||||
// pass in null for the bean so any data read is ignored
|
||||
return localHelp.read(ctx);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebeaninternal.server.query.SqlBeanLoad;
|
||||
import io.ebeaninternal.server.type.DataReader;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Dynamic property based on aggregation (max, min, avg, count).
|
||||
@@ -36,6 +38,15 @@ class DynamicPropertyAggregationFormula extends DynamicPropertyBase {
|
||||
return aggregate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object read(DataReader dataReader) throws SQLException {
|
||||
try {
|
||||
return scalarType.read(dataReader);
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException("Error loading on " + fullName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(SqlBeanLoad sqlBeanLoad) {
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import javax.persistence.ElementCollection;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.Lob;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.MapKey;
|
||||
import javax.persistence.MapKeyColumn;
|
||||
@@ -271,6 +272,10 @@ class AnnotationAssocManys extends AnnotationParser {
|
||||
valueProp.setDbLength(column.length());
|
||||
valueProp.setDbScale(column.scale());
|
||||
}
|
||||
Lob lob = get(prop, Lob.class);
|
||||
if (lob != null) {
|
||||
util.setLobType(valueProp);
|
||||
}
|
||||
elementDescriptor.addBeanProperty(valueProp);
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,11 @@ class RawExpression extends NonPrepareExpression {
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
return sql.hashCode();
|
||||
int hc = sql.hashCode();
|
||||
for (Object value : values) {
|
||||
hc = hc * 92821 + value.hashCode();
|
||||
}
|
||||
return hc;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -135,7 +135,7 @@ public final class DefaultPersister implements Persister {
|
||||
public int[] executeBatch(SpiSqlUpdate sqlUpdate, SpiTransaction transaction) {
|
||||
BatchControl batchControl = transaction.getBatchControl();
|
||||
try {
|
||||
return batchControl.execute(sqlUpdate.getSql(), sqlUpdate.isGetGeneratedKeys());
|
||||
return batchControl.execute(sqlUpdate.getGeneratedSql(), sqlUpdate.isGetGeneratedKeys());
|
||||
} catch (SQLException e) {
|
||||
throw transaction.translate(e.getMessage(), e);
|
||||
}
|
||||
|
||||
@@ -344,7 +344,7 @@ class SaveManyBeans extends SaveManyBase {
|
||||
for (Object removedBean : modifyRemovals) {
|
||||
if (removedBean instanceof EntityBean) {
|
||||
EntityBean eb = (EntityBean) removedBean;
|
||||
if (eb._ebean_getIntercept().isLoaded()) {
|
||||
if (!eb._ebean_getIntercept().isNew()) {
|
||||
// only delete if the bean was loaded meaning that it is known to exist in the DB
|
||||
persister.deleteRequest(persister.createPublishRequest(removedBean, transaction, PersistRequest.Type.DELETE, request.getFlags()));
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.util.JdbcClose;
|
||||
import io.ebean.CountedValue;
|
||||
import io.ebean.util.JdbcClose;
|
||||
import io.ebeaninternal.api.SpiProfileTransactionEvent;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.type.RsetDataReader;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
import io.ebeaninternal.server.type.ScalarDataReader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -60,7 +60,7 @@ class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent {
|
||||
|
||||
private int rowCount;
|
||||
|
||||
private final ScalarType<?> scalarType;
|
||||
private final ScalarDataReader<?> reader;
|
||||
|
||||
private final boolean containsCounts;
|
||||
|
||||
@@ -77,7 +77,7 @@ class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent {
|
||||
this.desc = request.getBeanDescriptor();
|
||||
this.predicates = predicates;
|
||||
this.containsCounts = containsCounts;
|
||||
this.scalarType = queryPlan.getSingleAttributeScalarType();
|
||||
this.reader = queryPlan.getSingleAttributeScalarType();
|
||||
query.setGeneratedSql(sql);
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent {
|
||||
|
||||
List<Object> result = new ArrayList<>();
|
||||
while (dataReader.next()) {
|
||||
Object value = scalarType.read(dataReader);
|
||||
Object value = reader.read(dataReader);
|
||||
if (containsCounts) {
|
||||
value = new CountedValue<>(value, dataReader.getLong());
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import io.ebeaninternal.server.query.CQueryPlanStats.Snapshot;
|
||||
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.ScalarDataReader;
|
||||
import io.ebeaninternal.server.util.Md5;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -286,8 +286,8 @@ public class CQueryPlan {
|
||||
return stats.getLastQueryTime();
|
||||
}
|
||||
|
||||
ScalarType<?> getSingleAttributeScalarType() {
|
||||
return sqlTree.getRootNode().getSingleAttributeScalarType();
|
||||
ScalarDataReader<?> getSingleAttributeScalarType() {
|
||||
return sqlTree.getRootNode().getSingleAttributeReader();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebeaninternal.server.deploy.DbReadContext;
|
||||
import io.ebeaninternal.server.deploy.DbSqlContext;
|
||||
import io.ebeaninternal.server.type.ScalarDataReader;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import java.util.List;
|
||||
@@ -11,7 +12,7 @@ import java.util.List;
|
||||
* <p>
|
||||
* A BeanProperty or a dynamically created property based on formula.
|
||||
*/
|
||||
public interface STreeProperty {
|
||||
public interface STreeProperty extends ScalarDataReader<Object> {
|
||||
|
||||
/**
|
||||
* Return the property name.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
import io.ebeaninternal.server.type.ScalarDataReader;
|
||||
|
||||
public interface STreePropertyAssocOne extends STreePropertyAssoc {
|
||||
|
||||
@@ -13,7 +13,7 @@ public interface STreePropertyAssocOne extends STreePropertyAssoc {
|
||||
/**
|
||||
* Return the scalar type of the associated id property.
|
||||
*/
|
||||
ScalarType<?> getIdScalarType();
|
||||
ScalarDataReader<?> getIdReader();
|
||||
|
||||
/**
|
||||
* Returns true, if this relation has a foreign key.
|
||||
|
||||
@@ -419,15 +419,11 @@ public final class SqlTreeBuilder {
|
||||
} else {
|
||||
logger.error("property [" + propName + "] not found on " + desc + " for query - excluding it.");
|
||||
}
|
||||
|
||||
} else if (p.isEmbedded()) {
|
||||
// add the embedded bean (and effectively
|
||||
// all its properties)
|
||||
} else if (p.isEmbedded() || (p instanceof STreePropertyAssoc && !queryProps.isIncludedBeanJoin(p.getName()))) {
|
||||
// add the embedded bean or the *ToOne assoc bean. We skip the check that the *ToOne propName maps to Id property ...
|
||||
selectProps.add(p);
|
||||
|
||||
} else {
|
||||
String m = "property [" + p.getFullBeanName() + "] expected to be an embedded bean for query - excluding it.";
|
||||
logger.error(m);
|
||||
logger.error("property [" + p.getFullBeanName() + "] expected to be an embedded or *ToOne bean for query - excluding it.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ 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.type.ScalarType;
|
||||
import io.ebeaninternal.server.type.ScalarDataReader;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
@@ -80,9 +80,9 @@ interface SqlTreeNode {
|
||||
boolean hasMany();
|
||||
|
||||
/**
|
||||
* Return the property for singleAttribute query.
|
||||
* Return the reader for the single attribute query.
|
||||
*/
|
||||
ScalarType<?> getSingleAttributeScalarType();
|
||||
ScalarDataReader<?> getSingleAttributeReader();
|
||||
|
||||
/**
|
||||
* Return true if the query is known to only have a single property selected.
|
||||
|
||||
@@ -14,7 +14,7 @@ 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 io.ebeaninternal.server.type.ScalarType;
|
||||
import io.ebeaninternal.server.type.ScalarDataReader;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
@@ -145,22 +145,22 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScalarType<?> getSingleAttributeScalarType() {
|
||||
public 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.getIdBinder().getBeanProperty().getScalarType();
|
||||
return desc.getIdBinder().getBeanProperty();
|
||||
}
|
||||
return children[0].getSingleAttributeScalarType();
|
||||
return children[0].getSingleAttributeReader();
|
||||
}
|
||||
if (properties[0] instanceof STreePropertyAssocOne) {
|
||||
STreePropertyAssocOne assocOne = (STreePropertyAssocOne)properties[0];
|
||||
if (assocOne.isAssocId()) {
|
||||
return assocOne.getIdScalarType();
|
||||
return assocOne.getIdReader();
|
||||
}
|
||||
}
|
||||
return properties[0].getScalarType();
|
||||
return properties[0];
|
||||
}
|
||||
|
||||
private Map<String, String> createPathMap(String prefix, STreeType desc) {
|
||||
|
||||
@@ -76,7 +76,7 @@ class SqlTreeNodeExtraJoin implements SqlTreeNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScalarType<?> getSingleAttributeScalarType() {
|
||||
public ScalarType<?> getSingleAttributeReader() {
|
||||
throw new IllegalStateException("No expected");
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ class SqlTreeNodeManyWhereJoin implements SqlTreeNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScalarType<?> getSingleAttributeScalarType() {
|
||||
public ScalarType<?> getSingleAttributeReader() {
|
||||
throw new IllegalStateException("No expected");
|
||||
}
|
||||
|
||||
|
||||
@@ -12,14 +12,4 @@ public interface ScalarDataReader<T> {
|
||||
*/
|
||||
T read(DataReader dataReader) throws SQLException;
|
||||
|
||||
/**
|
||||
* Ignore typically by moving the index position.
|
||||
*/
|
||||
void loadIgnore(DataReader dataReader);
|
||||
|
||||
/**
|
||||
* Bind the value to the underlying preparedStatement.
|
||||
*/
|
||||
void bind(DataBind b, T value) throws SQLException;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.ebean.text.StringFormatter;
|
||||
import io.ebean.text.StringParser;
|
||||
import io.ebeanservice.docstore.api.mapping.DocPropertyType;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
@@ -103,7 +103,6 @@ public interface ScalarType<T> extends StringParser, StringFormatter, ScalarData
|
||||
* Ignore the reading of this value. Typically this means moving the index
|
||||
* position in the ResultSet.
|
||||
*/
|
||||
@Override
|
||||
void loadIgnore(DataReader reader);
|
||||
|
||||
/**
|
||||
@@ -113,7 +112,6 @@ public interface ScalarType<T> extends StringParser, StringFormatter, ScalarData
|
||||
* JDBC type.
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
void bind(DataBind bind, T value) throws SQLException;
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,4 +45,27 @@ public class RawExpressionTest extends BaseExpressionTest {
|
||||
assertThat(exp("a", 10, 20).isSameByBind(exp("a", 10))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryBindHash_when_sameBindValues() {
|
||||
assert_queryBindHash_isSame(exp("a", 10), exp("a", 10));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryBindHash_when_diffBindValues() {
|
||||
assert_queryBindHash_isDifferent(exp("a", 10), exp("a", 20));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryBindHash_when_diffBindValues2() {
|
||||
assert_queryBindHash_isDifferent(exp("a", 10), exp("a", 10, 11));
|
||||
}
|
||||
|
||||
public void assert_queryBindHash_isDifferent(RawExpression exp0, RawExpression exp1) {
|
||||
assertThat(exp0.queryBindHash()).isNotEqualTo(exp1.queryBindHash());
|
||||
}
|
||||
|
||||
public void assert_queryBindHash_isSame(RawExpression exp0, RawExpression exp1) {
|
||||
assertThat(exp0.queryBindHash()).isEqualTo(exp1.queryBindHash());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -62,6 +62,60 @@ public class TestQueryCacheCountry extends BaseTestCase {
|
||||
assertEquals(1, queryStats1.getHitCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rawExpression() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
awaitL2Cache();
|
||||
clearCache();
|
||||
|
||||
List<Country> countryList0 = Ebean.find(Country.class)
|
||||
.setUseQueryCache(true)
|
||||
.where().raw("code = ?", "NZ")
|
||||
.findList();
|
||||
|
||||
assertThat(countryList0.get(0).getName()).isEqualTo("New Zealand");
|
||||
|
||||
List<Country> countryList1 = Ebean.find(Country.class)
|
||||
.setUseQueryCache(true)
|
||||
.where().raw("code = ?", "AU")
|
||||
.findList();
|
||||
|
||||
assertThat(countryList1.get(0).getName()).isEqualTo("Australia");
|
||||
|
||||
ServerCacheStatistics queryStats1 = queryCache.getStatistics(false);
|
||||
assertEquals(2, queryStats1.getMissCount());
|
||||
assertEquals(2, queryStats1.getSize());
|
||||
assertEquals(0, queryStats1.getHitCount()); // no hits yet
|
||||
|
||||
|
||||
// we get a hit this time
|
||||
List<Country> countryList2 = Ebean.find(Country.class)
|
||||
.setUseQueryCache(true)
|
||||
.where().raw("code = ?", "NZ")
|
||||
.findList();
|
||||
|
||||
assertThat(countryList2.get(0).getName()).isEqualTo("New Zealand");
|
||||
|
||||
ServerCacheStatistics queryStats2 = queryCache.getStatistics(false);
|
||||
assertEquals(2, queryStats2.getMissCount());
|
||||
assertEquals(2, queryStats2.getSize());
|
||||
assertEquals(1, queryStats2.getHitCount()); // got a hit
|
||||
|
||||
// we get a hit on AU
|
||||
List<Country> countryList3 = Ebean.find(Country.class)
|
||||
.setUseQueryCache(true)
|
||||
.where().raw("code = ?", "AU")
|
||||
.findList();
|
||||
|
||||
assertThat(countryList3.get(0).getName()).isEqualTo("Australia");
|
||||
|
||||
ServerCacheStatistics queryStats3 = queryCache.getStatistics(false);
|
||||
assertEquals(2, queryStats3.getMissCount());
|
||||
assertEquals(2, queryStats3.getSize());
|
||||
assertEquals(2, queryStats3.getHitCount()); // got another hit
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
@@ -104,6 +158,10 @@ public class TestQueryCacheCountry extends BaseTestCase {
|
||||
.findList();
|
||||
|
||||
assertNotSame(countryList2, countryList0);
|
||||
|
||||
nz = Ebean.find(Country.class, "NZ");
|
||||
nz.setName("New Zealand");
|
||||
Ebean.save(nz);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,10 +2,15 @@ package org.tests.compositekeys;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.basic.CKeyParent;
|
||||
import org.tests.model.basic.CKeyParentId;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
public class TestCKeyDelete extends BaseTestCase {
|
||||
|
||||
@@ -22,14 +27,37 @@ public class TestCKeyDelete extends BaseTestCase {
|
||||
Ebean.save(p);
|
||||
|
||||
CKeyParent found = Ebean.find(CKeyParent.class).where().idEq(searchId).findOne();
|
||||
|
||||
Assert.assertNotNull(found);
|
||||
assertNotNull(found);
|
||||
|
||||
Ebean.delete(CKeyParent.class, searchId);
|
||||
|
||||
CKeyParent notFound = Ebean.find(CKeyParent.class).where().idEq(searchId).findOne();
|
||||
|
||||
Assert.assertNull(notFound);
|
||||
assertNull(notFound);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteWhere() {
|
||||
|
||||
CKeyParentId id = new CKeyParentId(101, "deleteMe2");
|
||||
CKeyParentId searchId = new CKeyParentId(101, "deleteMe2");
|
||||
|
||||
CKeyParent p = new CKeyParent();
|
||||
p.setId(id);
|
||||
p.setName("testDelete");
|
||||
|
||||
Ebean.save(p);
|
||||
|
||||
List<CKeyParentId> ids = Ebean.find(CKeyParent.class).where().eq("id.oneKey", 101).findIds();
|
||||
assertThat(ids).hasSize(1);
|
||||
|
||||
CKeyParentId foundId = ids.get(0);
|
||||
assertThat(foundId.getOneKey()).isEqualTo(101);
|
||||
assertThat(foundId.getTwoKey()).isEqualTo("deleteMe2");
|
||||
|
||||
Ebean.createQuery(CKeyParent.class).where().eq("id.oneKey", 101).delete();
|
||||
|
||||
CKeyParent found = Ebean.find(CKeyParent.class).where().idEq(searchId).findOne();
|
||||
assertNull(found);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package org.tests.model.basic;
|
||||
|
||||
import io.ebean.annotation.Cache;
|
||||
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.Entity;
|
||||
@@ -10,6 +13,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Cache
|
||||
public class CKeyParent {
|
||||
|
||||
@EmbeddedId
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package org.tests.model.elementcollection;
|
||||
|
||||
import io.ebean.annotation.Cache;
|
||||
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Lob;
|
||||
import javax.persistence.MapKeyColumn;
|
||||
import javax.persistence.Version;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Cache
|
||||
@Entity
|
||||
public class EcmcPerson {
|
||||
|
||||
@Id
|
||||
long id;
|
||||
|
||||
String name;
|
||||
|
||||
@ElementCollection
|
||||
@MapKeyColumn(name = "type", length = 4)
|
||||
@Lob
|
||||
Map<String, String> phoneNumbers = new LinkedHashMap<>();
|
||||
|
||||
@Version
|
||||
long version;
|
||||
|
||||
public EcmcPerson(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "person id:" + id + " name:" + name + " phs:" + phoneNumbers;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Map<String, String> getPhoneNumbers() {
|
||||
return phoneNumbers;
|
||||
}
|
||||
|
||||
public void setPhoneNumbers(Map<String, String> phoneNumbers) {
|
||||
this.phoneNumbers = phoneNumbers;
|
||||
}
|
||||
|
||||
public long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(long version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package org.tests.model.elementcollection;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import org.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestElementCollectionBasicMapAsLob extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
EcmcPerson person = new EcmcPerson("Lob021");
|
||||
person.getPhoneNumbers().put("home", "021 1234");
|
||||
person.getPhoneNumbers().put("work", "021 4321");
|
||||
Ebean.save(person);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
if (isPersistBatchOnCascade()) {
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("insert into ecmc_person");
|
||||
assertThat(sql.get(1)).contains("insert into ecmc_person_phone");
|
||||
} else {
|
||||
assertThat(sql).hasSize(3);
|
||||
assertThat(sql.get(0)).contains("insert into ecmc_person");
|
||||
assertThat(sql.get(1)).contains("insert into ecmc_person_phone");
|
||||
assertThat(sql.get(2)).contains("insert into ecmc_person_phone");
|
||||
}
|
||||
|
||||
LoggedSqlCollector.stop();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package org.tests.model.orphanremoval;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Version;
|
||||
|
||||
@Entity
|
||||
public class OrpDetail2 {
|
||||
|
||||
@Id
|
||||
String id;
|
||||
|
||||
String detail;
|
||||
|
||||
String masterId;
|
||||
|
||||
@Version
|
||||
long version;
|
||||
|
||||
public OrpDetail2(String id, String detail, String masterId) {
|
||||
this.id = id;
|
||||
this.detail = detail;
|
||||
this.masterId = masterId;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getMasterId() {
|
||||
return masterId;
|
||||
}
|
||||
|
||||
public void setMasterId(String masterId) {
|
||||
this.masterId = masterId;
|
||||
}
|
||||
|
||||
public String getDetail() {
|
||||
return detail;
|
||||
}
|
||||
|
||||
public void setDetail(String detail) {
|
||||
this.detail = detail;
|
||||
}
|
||||
|
||||
public long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(long version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.tests.model.orphanremoval;
|
||||
|
||||
import io.ebean.annotation.Cache;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
@@ -7,6 +9,7 @@ import javax.persistence.OneToMany;
|
||||
import javax.persistence.Version;
|
||||
import java.util.List;
|
||||
|
||||
@Cache
|
||||
@Entity
|
||||
public class OrpMaster {
|
||||
|
||||
@@ -15,7 +18,7 @@ public class OrpMaster {
|
||||
|
||||
String name;
|
||||
|
||||
@OneToMany(orphanRemoval = true, cascade = CascadeType.ALL)
|
||||
@OneToMany(orphanRemoval = true, cascade = CascadeType.ALL, mappedBy = "master")
|
||||
List<OrpDetail> details;
|
||||
|
||||
@Version
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package org.tests.model.orphanremoval;
|
||||
|
||||
import io.ebean.annotation.Cache;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Version;
|
||||
import java.util.List;
|
||||
|
||||
@Cache
|
||||
@Entity
|
||||
public class OrpMaster2 {
|
||||
|
||||
@Id
|
||||
String id;
|
||||
|
||||
String name;
|
||||
|
||||
@OneToMany(orphanRemoval = true, cascade = CascadeType.ALL)
|
||||
List<OrpDetail2> details;
|
||||
|
||||
@Version
|
||||
long version;
|
||||
|
||||
public OrpMaster2(String id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public List<OrpDetail2> getDetails() {
|
||||
return details;
|
||||
}
|
||||
|
||||
public void setDetails(List<OrpDetail2> details) {
|
||||
this.details = details;
|
||||
}
|
||||
|
||||
public long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(long version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
@@ -33,4 +33,46 @@ public class TestOrphanRemoveO2M extends BaseTestCase {
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("delete from orp_detail where id=?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCacheUse() {
|
||||
|
||||
// add test data first
|
||||
OrpMaster m0 = new OrpMaster("m2", "master2");
|
||||
m0.getDetails().add(new OrpDetail("d21", "d1"));
|
||||
m0.getDetails().add(new OrpDetail("d22", "d2"));
|
||||
|
||||
Ebean.save(m0);
|
||||
|
||||
OrpMaster m1 = Ebean.find(OrpMaster.class, "m2");
|
||||
m1.getDetails().size();
|
||||
|
||||
m1.getDetails().clear();
|
||||
m1.getDetails().add(new OrpDetail("d23", "d3"));
|
||||
Ebean.save(m1);
|
||||
|
||||
m1 = Ebean.find(OrpMaster.class, "m2");
|
||||
// Expect only one.
|
||||
assertThat(m1.getDetails()).hasSize(1);
|
||||
assertThat(m1.getDetails()).extracting("id").containsExactly("d23");
|
||||
|
||||
m1.getDetails().clear();
|
||||
m1.getDetails().add(new OrpDetail("d24", "d4"));
|
||||
m1.getDetails().add(new OrpDetail("d25", "d5"));
|
||||
Ebean.save(m1);
|
||||
|
||||
m1 = Ebean.find(OrpMaster.class, "m2");
|
||||
assertThat(m1.getDetails()).hasSize(2);
|
||||
assertThat(m1.getDetails()).extracting("id").containsExactly("d24", "d25");
|
||||
|
||||
|
||||
m1 = Ebean.find(OrpMaster.class)
|
||||
.setId("m2")
|
||||
.setUseCache(false)
|
||||
.findOne();
|
||||
|
||||
// Expect only one.
|
||||
assertThat(m1.getDetails()).hasSize(2);
|
||||
assertThat(m1.getDetails()).extracting("id").containsExactly("d24", "d25");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.tests.model.orphanremoval;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestOrphanRemoveO2MFlat extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void testCacheUse() {
|
||||
|
||||
// add test data first
|
||||
OrpMaster2 m0 = new OrpMaster2("m2", "master2");
|
||||
m0.getDetails().add(new OrpDetail2("d21", "d1", "m2"));
|
||||
m0.getDetails().add(new OrpDetail2("d22", "d2", "m2"));
|
||||
|
||||
Ebean.save(m0);
|
||||
|
||||
OrpMaster2 m1 = Ebean.find(OrpMaster2.class, "m2");
|
||||
m1.getDetails().size();
|
||||
|
||||
m1.getDetails().clear();
|
||||
m1.getDetails().add(new OrpDetail2("d23", "d3", "m2"));
|
||||
Ebean.save(m1);
|
||||
|
||||
m1 = Ebean.find(OrpMaster2.class, "m2");
|
||||
// Expect only one.
|
||||
assertThat(m1.getDetails()).hasSize(1);
|
||||
assertThat(m1.getDetails()).extracting("id").containsExactly("d23");
|
||||
|
||||
m1.getDetails().clear();
|
||||
m1.getDetails().add(new OrpDetail2("d24", "d4", "m2"));
|
||||
m1.getDetails().add(new OrpDetail2("d25", "d5", "m2"));
|
||||
Ebean.save(m1);
|
||||
|
||||
m1 = Ebean.find(OrpMaster2.class, "m2");
|
||||
assertThat(m1.getDetails()).hasSize(2);
|
||||
assertThat(m1.getDetails()).extracting("id").containsExactly("d24", "d25");
|
||||
|
||||
|
||||
m1 = Ebean.find(OrpMaster2.class)
|
||||
.setId("m2")
|
||||
.setUseCache(false)
|
||||
.findOne();
|
||||
|
||||
// Expect only one.
|
||||
assertThat(m1.getDetails()).hasSize(2);
|
||||
assertThat(m1.getDetails()).extracting("id").containsExactly("d24", "d25");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -359,6 +359,21 @@ public class TestQuerySingleAttribute extends BaseTestCase {
|
||||
assertThat(sqlOf(query)).contains("select distinct t0.customer_id from contact t0 order by t0.customer_id desc");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void distinctWithOrderByPkWithId() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Contact> query = Ebean.find(Contact.class)
|
||||
.setDistinct(true)
|
||||
.select("customer.id")
|
||||
.orderBy().desc("customer.id");
|
||||
|
||||
query.findSingleAttributeList();
|
||||
|
||||
assertThat(sqlOf(query)).contains("select distinct t0.customer_id from contact t0 order by t0.customer_id desc");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void distinctWithCascadedFetchOrderByPk() {
|
||||
|
||||
|
||||
@@ -75,7 +75,38 @@ public class TestInsertSqlLogging extends BaseTestCase {
|
||||
|
||||
txn.commit();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addBatch_namedParams() {
|
||||
|
||||
Ebean.find(AuditLog.class).where().ge("id", 10000).delete();
|
||||
|
||||
String sql = "insert into audit_log (id, description, modified_description) values (:id, :desc, :modDesc)";
|
||||
SqlUpdate insert = Ebean.createSqlUpdate(sql);
|
||||
|
||||
try (Transaction txn = Ebean.beginTransaction()) {
|
||||
|
||||
insert.setParameter("id", 20000);
|
||||
insert.setParameter("desc", "hello");
|
||||
insert.setParameter("modDesc", "rob");
|
||||
insert.addBatch();
|
||||
|
||||
insert.setParameter("id", 20001);
|
||||
insert.setParameter("desc", "goodbye");
|
||||
insert.setParameter("modDesc", "rob");
|
||||
insert.addBatch();
|
||||
|
||||
insert.setParameter("id", 20002);
|
||||
insert.setParameter("desc", "chow");
|
||||
insert.setParameter("modDesc", "bob");
|
||||
insert.addBatch();
|
||||
|
||||
int[] rows = insert.executeBatch();
|
||||
System.out.println("Rows was " + Arrays.toString(rows));
|
||||
|
||||
txn.commit();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -5,12 +5,12 @@ create table migtest_e_history7 (
|
||||
constraint pk_migtest_e_history7 primary key (id)
|
||||
);
|
||||
|
||||
alter table migtest_e_history7 add column sys_period_start datetime(6) default now(6);
|
||||
alter table migtest_e_history7 add column sys_period_end datetime(6);
|
||||
alter table migtest_e_history7 add column sys_period_start timestamp default now();
|
||||
alter table migtest_e_history7 add column sys_period_end timestamp;
|
||||
create table migtest_e_history7_history(
|
||||
id integer,
|
||||
sys_period_start datetime(6),
|
||||
sys_period_end datetime(6)
|
||||
sys_period_start timestamp,
|
||||
sys_period_end timestamp
|
||||
);
|
||||
create view migtest_e_history7_with_history as select * from migtest_e_history7 union all select * from migtest_e_history7_history;
|
||||
|
||||
|
||||
@@ -170,56 +170,56 @@ alter table migtest_fk_set_null add constraint fk_migtest_fk_set_null_one_id for
|
||||
create index ix_migtest_e_basic_eref_id on migtest_e_basic (eref_id);
|
||||
alter table migtest_e_basic add constraint fk_migtest_e_basic_eref_id foreign key (eref_id) references migtest_e_ref (id) on delete restrict on update restrict;
|
||||
|
||||
alter table migtest_e_history2 add column sys_period_start datetime(6) default now(6);
|
||||
alter table migtest_e_history2 add column sys_period_end datetime(6);
|
||||
alter table migtest_e_history2 add column sys_period_start timestamp default now();
|
||||
alter table migtest_e_history2 add column sys_period_end timestamp;
|
||||
create table migtest_e_history2_history(
|
||||
id integer,
|
||||
test_string varchar(255),
|
||||
obsolete_string1 varchar(255),
|
||||
obsolete_string2 varchar(255),
|
||||
sys_period_start datetime(6),
|
||||
sys_period_end datetime(6)
|
||||
sys_period_start timestamp,
|
||||
sys_period_end timestamp
|
||||
);
|
||||
create view migtest_e_history2_with_history as select * from migtest_e_history2 union all select * from migtest_e_history2_history;
|
||||
|
||||
alter table migtest_e_history3 add column sys_period_start datetime(6) default now(6);
|
||||
alter table migtest_e_history3 add column sys_period_end datetime(6);
|
||||
alter table migtest_e_history3 add column sys_period_start timestamp default now();
|
||||
alter table migtest_e_history3 add column sys_period_end timestamp;
|
||||
create table migtest_e_history3_history(
|
||||
id integer,
|
||||
test_string varchar(255),
|
||||
sys_period_start datetime(6),
|
||||
sys_period_end datetime(6)
|
||||
sys_period_start timestamp,
|
||||
sys_period_end timestamp
|
||||
);
|
||||
create view migtest_e_history3_with_history as select * from migtest_e_history3 union all select * from migtest_e_history3_history;
|
||||
|
||||
alter table migtest_e_history4 add column sys_period_start datetime(6) default now(6);
|
||||
alter table migtest_e_history4 add column sys_period_end datetime(6);
|
||||
alter table migtest_e_history4 add column sys_period_start timestamp default now();
|
||||
alter table migtest_e_history4 add column sys_period_end timestamp;
|
||||
create table migtest_e_history4_history(
|
||||
id integer,
|
||||
test_number integer,
|
||||
sys_period_start datetime(6),
|
||||
sys_period_end datetime(6)
|
||||
sys_period_start timestamp,
|
||||
sys_period_end timestamp
|
||||
);
|
||||
create view migtest_e_history4_with_history as select * from migtest_e_history4 union all select * from migtest_e_history4_history;
|
||||
|
||||
alter table migtest_e_history5 add column sys_period_start datetime(6) default now(6);
|
||||
alter table migtest_e_history5 add column sys_period_end datetime(6);
|
||||
alter table migtest_e_history5 add column sys_period_start timestamp default now();
|
||||
alter table migtest_e_history5 add column sys_period_end timestamp;
|
||||
create table migtest_e_history5_history(
|
||||
id integer,
|
||||
test_number integer,
|
||||
sys_period_start datetime(6),
|
||||
sys_period_end datetime(6)
|
||||
sys_period_start timestamp,
|
||||
sys_period_end timestamp
|
||||
);
|
||||
create view migtest_e_history5_with_history as select * from migtest_e_history5 union all select * from migtest_e_history5_history;
|
||||
|
||||
alter table migtest_e_history6 add column sys_period_start datetime(6) default now(6);
|
||||
alter table migtest_e_history6 add column sys_period_end datetime(6);
|
||||
alter table migtest_e_history6 add column sys_period_start timestamp default now();
|
||||
alter table migtest_e_history6 add column sys_period_end timestamp;
|
||||
create table migtest_e_history6_history(
|
||||
id integer,
|
||||
test_number1 integer,
|
||||
test_number2 integer,
|
||||
sys_period_start datetime(6),
|
||||
sys_period_end datetime(6)
|
||||
sys_period_start timestamp,
|
||||
sys_period_end timestamp
|
||||
);
|
||||
create view migtest_e_history6_with_history as select * from migtest_e_history6 union all select * from migtest_e_history6_history;
|
||||
|
||||
|
||||
@@ -115,13 +115,13 @@ alter table migtest_ckey_parent add constraint fk_migtest_ckey_parent_assoc_id f
|
||||
|
||||
alter table migtest_oto_child add constraint fk_migtest_oto_child_master_id foreign key (master_id) references migtest_oto_master (id) on delete restrict on update restrict;
|
||||
|
||||
alter table migtest_e_history add column sys_period_start datetime(6) default now(6);
|
||||
alter table migtest_e_history add column sys_period_end datetime(6);
|
||||
alter table migtest_e_history add column sys_period_start timestamp default now();
|
||||
alter table migtest_e_history add column sys_period_end timestamp;
|
||||
create table migtest_e_history_history(
|
||||
id integer,
|
||||
test_string bigint,
|
||||
sys_period_start datetime(6),
|
||||
sys_period_end datetime(6)
|
||||
sys_period_start timestamp,
|
||||
sys_period_end timestamp
|
||||
);
|
||||
create view migtest_e_history_with_history as select * from migtest_e_history union all select * from migtest_e_history_history;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user