Compare commits

..
Author SHA1 Message Date
rob bygrave a5fea5e9be [maven-release-plugin] prepare release ebean-11.28.3 2018-12-06 00:18:53 +13:00
rob bygrave bafba0babf #1572 - Support query.select("customer.id").findSingleAttributeList() ... in addition to the expected and working select("customer").findSingleAttributeList() 2018-12-06 00:15:06 +13:00
rob bygrave e4088c5fbe #1571 - NPE in cacheable bean with embeddedid on delete 2018-12-05 23:03:15 +13:00
RienandRob Bygrave 0405ceb96c NPE in cacheable bean with embeddedid on delete (#1570) 2018-12-05 21:49:27 +13:00
rob bygrave ebf96c87bf [maven-release-plugin] prepare for next development iteration 2018-12-03 22:14:59 +13:00
rob bygrave fcaf11f262 [maven-release-plugin] prepare release ebean-11.28.2 2018-12-03 22:14:46 +13:00
rob bygrave 05df47c0d7 #1569 - Ebean generates wrong ddl (datetime(6) vs timestamp) for H2 with @History annotation 2018-12-03 22:11:47 +13:00
rob bygrave a8892292fb #1569 - Ebean generates wrong ddl (datetime(6) vs timestamp) for H2 with @History annotation 2018-12-03 22:05:18 +13:00
rob bygrave e876ef8d87 #1559 - @Lob with @ElementCollection 2018-12-03 21:07:33 +13:00
rob bygrave 315b39813b #1568 - @OneToMany with orphanRemoval=true ... with @Cache (and not a @ManyToOne) doesn't remove orphans after cache hit 2018-11-30 23:46:11 +13:00
rob bygrave 6d5e0c4766 Add test only - Add test for L2 cache with orphanRemoval 2018-11-30 23:41:23 +13:00
rob bygrave a2ce17aec0 Add test only - Add test for L2 cache with orphanRemoval 2018-11-30 22:42:36 +13:00
rob bygrave fcffa92c55 #1567 - SqlUpdate addBatch() ... with named parameters errors with PersistenceException: No batched statement found for key 2018-11-30 21:47:23 +13:00
rob bygrave 7fda8079c2 [maven-release-plugin] prepare for next development iteration 2018-11-30 09:55:20 +13:00
rob bygrave cca9bb4427 [maven-release-plugin] prepare release ebean-11.28.1 2018-11-30 09:55:06 +13:00
rob bygrave f45089240a #1565 - Bump to ebean-datasource 4.3.1 ... with option for owner database credentials and InitDatabase 2018-11-30 09:44:07 +13:00
rob bygrave 8252a51545 #1565 - Bump to ebean-datasource 4.3.1 ... with option for owner database credentials and InitDatabase 2018-11-29 10:16:48 +13:00
rob bygrave c440c768cf #1564 - Remove deprecated io.ebeaninternal.server.core.ServletContextListener - migrate to io.ebean.event.ServletContextListener 2018-11-29 10:01:56 +13:00
rob bygrave c6ae501354 #1563 - Remove deprecated noop method ServerConfig.loadTestProperties() - migrate to loadProperties() 2018-11-29 09:59:16 +13:00
rob bygrave 70e2b1c2ae #1562 - Query cache use incorrect with RawExpression - incorrect bind hash 2018-11-29 09:54:24 +13:00
rob bygrave 211f28eea3 [maven-release-plugin] prepare for next development iteration 2018-11-27 14:58:40 +13:00
41 changed files with 671 additions and 154 deletions
+4 -4
View File
@@ -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;
}
}
@@ -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;