mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
27
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fcb9df05ad | ||
|
|
3de79b211a | ||
|
|
d93952a920 | ||
|
|
3b4836cad9 | ||
|
|
6048d97aab | ||
|
|
16d32b408e | ||
|
|
0435ab0991 | ||
|
|
95617d1bea | ||
|
|
38d0e7642e | ||
|
|
e4306ce1ab | ||
|
|
444b656780 | ||
|
|
fcbba23441 | ||
|
|
18961047a1 | ||
|
|
34347fc3e1 | ||
|
|
189e0a2ea9 | ||
|
|
182e027521 | ||
|
|
f85fa2c21d | ||
|
|
5a5f479568 | ||
|
|
9ca2486c9c | ||
|
|
656b239925 | ||
|
|
5d46904341 | ||
|
|
f0dd069ed2 | ||
|
|
5bd454bdc0 | ||
|
|
eeebaf9a08 | ||
|
|
636a148509 | ||
|
|
170cd6051e | ||
|
|
522fc2384c |
@@ -1,14 +1,14 @@
|
||||
avaje-ebeanorm-server
|
||||
=====================
|
||||
avaje-ebeanorm
|
||||
==============
|
||||
|
||||
The internal server implementation of EbeanORM API
|
||||
Main EbeanORM artifact
|
||||
|
||||
Maven Dependency
|
||||
----------------
|
||||
<dependency>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm</artifactId>
|
||||
<version>3.2.5</version>
|
||||
<version>3.3.2</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm</artifactId>
|
||||
<version>3.3.1-RC2</version>
|
||||
<version>3.3.3</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>avaje-ebeanorm</name>
|
||||
@@ -170,7 +170,7 @@
|
||||
<plugin>
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm-mavenenhancer</artifactId>
|
||||
<version>3.2.2</version>
|
||||
<version>3.3.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>main</id>
|
||||
|
||||
@@ -2,6 +2,11 @@ package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import java.sql.Connection;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
@@ -11,6 +16,8 @@ import com.avaje.ebeaninternal.api.SpiTransaction;
|
||||
*/
|
||||
public abstract class BeanRequest {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(BeanRequest.class);
|
||||
|
||||
/**
|
||||
* The server processing the request.
|
||||
*/
|
||||
@@ -84,7 +91,14 @@ public abstract class BeanRequest {
|
||||
*/
|
||||
public void rollbackTransIfRequired() {
|
||||
if (createdTransaction) {
|
||||
transaction.rollback();
|
||||
try {
|
||||
transaction.rollback();
|
||||
} catch (PersistenceException e) {
|
||||
// Just log this and carry on. A previous exception has been
|
||||
// thrown and if this rollback throws exception it likely means
|
||||
// that the connection is broken (and the datasource and db will cleanup)
|
||||
log.error("Error trying to rollack a transaction (after a prior exception thrown)", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,16 +82,22 @@ public class BootupClassPathSearch {
|
||||
ClassPathSearchFilter filter = new ClassPathSearchFilter();
|
||||
filter.addDefaultExcludePackages();
|
||||
|
||||
if (packages != null) {
|
||||
if (packages != null && packages.size() > 0) {
|
||||
for (String packageName : packages) {
|
||||
filter.includePackage(packageName);
|
||||
}
|
||||
|
||||
// if they specified include packages, they don't want by default to include everything
|
||||
filter.setDefaultPackageMatch(false);
|
||||
}
|
||||
|
||||
if (jars != null) {
|
||||
if (jars != null && jars.size() > 0) {
|
||||
for (String jarName : jars) {
|
||||
filter.includeJar(jarName);
|
||||
}
|
||||
|
||||
// if they specified jars to specifically include, they don't want everything included
|
||||
filter.setDefaultJarMatch(false);
|
||||
}
|
||||
|
||||
return filter;
|
||||
|
||||
@@ -43,7 +43,8 @@ final class NotExpression implements SpiExpression {
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoFetchHash(builder);
|
||||
builder.add(NotExpression.class);
|
||||
exp.queryPlanHash(request, builder);
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
|
||||
@@ -597,12 +597,35 @@ public class DataSourcePool implements DataSource {
|
||||
*/
|
||||
protected void returnConnection(PooledConnection pooledConnection) {
|
||||
|
||||
if (poolListener != null) {
|
||||
poolListener.onBeforeReturnConnection(pooledConnection);
|
||||
}
|
||||
queue.returnPooledConnection(pooledConnection);
|
||||
// return a normal 'good' connection
|
||||
returnTheConnection(pooledConnection, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is a bad connection and must be removed from the pool's busy list and fully closed.
|
||||
*/
|
||||
protected void returnConnectionForceClose(PooledConnection pooledConnection) {
|
||||
|
||||
returnTheConnection(pooledConnection, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return connection. If forceClose is true then this is a bad connection that
|
||||
* must be removed and closed fully.
|
||||
*/
|
||||
private void returnTheConnection(PooledConnection pooledConnection, boolean forceClose) {
|
||||
|
||||
if (poolListener != null && !forceClose) {
|
||||
poolListener.onBeforeReturnConnection(pooledConnection);
|
||||
}
|
||||
queue.returnPooledConnection(pooledConnection, forceClose);
|
||||
|
||||
if (forceClose) {
|
||||
// Got a bad connection so check the pool
|
||||
checkDataSource();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect statistics of a connection that is fully closing
|
||||
*/
|
||||
|
||||
@@ -508,9 +508,8 @@ public class PooledConnection extends ConnectionDelegator {
|
||||
|
||||
if (hadErrors) {
|
||||
if (!pool.validateConnection(this)) {
|
||||
// the connection is BAD, close it and test the pool
|
||||
closeConnectionFully(false);
|
||||
pool.checkDataSource();
|
||||
// the connection is BAD, remove it, close it and test the pool
|
||||
pool.returnConnectionForceClose(this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -535,10 +534,9 @@ public class PooledConnection extends ConnectionDelegator {
|
||||
pool.returnConnection(this);
|
||||
|
||||
} catch (Exception ex) {
|
||||
// the connection is BAD, close it and test the pool
|
||||
// the connection is BAD, remove it, close it and test the pool
|
||||
logger.warn("Error when trying to return connection to pool, closing fully.", ex);
|
||||
closeConnectionFully(false);
|
||||
pool.checkDataSource();
|
||||
pool.returnConnectionForceClose(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -238,7 +238,7 @@ public class PooledConnectionQueue {
|
||||
/**
|
||||
* Return a PooledConnection.
|
||||
*/
|
||||
protected void returnPooledConnection(PooledConnection c) {
|
||||
protected void returnPooledConnection(PooledConnection c, boolean forceClose) {
|
||||
|
||||
final ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
@@ -246,7 +246,7 @@ public class PooledConnectionQueue {
|
||||
if (!busyList.remove(c)) {
|
||||
logger.error("Connection [{}] not found in BusyList? ", c);
|
||||
}
|
||||
if (c.shouldTrimOnReturn(lastResetTime, maxAgeMillis)) {
|
||||
if (forceClose || c.shouldTrimOnReturn(lastResetTime, maxAgeMillis)) {
|
||||
c.closeConnectionFully(false);
|
||||
|
||||
} else {
|
||||
|
||||
@@ -764,7 +764,14 @@ public final class DefaultPersister implements Persister {
|
||||
// increase depth for batching order
|
||||
t.depth(+1);
|
||||
for (Object removedBean : modifyRemovals) {
|
||||
deleteRecurse(removedBean, t);
|
||||
if (removedBean instanceof EntityBean) {
|
||||
EntityBean eb = (EntityBean)removedBean;
|
||||
if (eb._ebean_getIntercept().isLoaded()) {
|
||||
// only delete if the bean was loaded meaning that
|
||||
// it is know to exist in the DB
|
||||
deleteRecurse(removedBean, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
t.depth(-1);
|
||||
}
|
||||
|
||||
+4
@@ -47,12 +47,16 @@ public class FactoryBaseProperties {
|
||||
|
||||
for (int i = 0; i < props.length; i++) {
|
||||
|
||||
if(DmlMode.WHERE.equals(mode) && !withLobs && !props[i].isDbUpdatable()) {
|
||||
// skip non-updatable column from where clause
|
||||
} else {
|
||||
Bindable item = factoryProperty.create(props[i], mode, withLobs);
|
||||
if (item != null) {
|
||||
list.add(item);
|
||||
} else {
|
||||
// null where readOnly (Secondary tables) or Lob exclusion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -386,6 +386,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
|
||||
if (forwardOnlyHint) {
|
||||
// Use forward only hints for large resultset processing (Issue 56, MySql specific)
|
||||
pstmt = conn.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
|
||||
pstmt.setFetchSize(Integer.MIN_VALUE);
|
||||
} else {
|
||||
pstmt = conn.prepareStatement(sql);
|
||||
}
|
||||
|
||||
+13
-1
@@ -5,6 +5,8 @@ import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebeaninternal.api.Monitor;
|
||||
|
||||
@@ -124,7 +126,7 @@ public final class DefaultPersistenceContext implements PersistenceContext {
|
||||
|
||||
private ClassContext getClassContext(Class<?> beanType) {
|
||||
|
||||
String clsName = beanType.getName();
|
||||
String clsName = getBeanBaseType(beanType).getName();
|
||||
ClassContext classMap = typeCache.get(clsName);
|
||||
if (classMap == null) {
|
||||
classMap = new ClassContext();
|
||||
@@ -132,6 +134,16 @@ public final class DefaultPersistenceContext implements PersistenceContext {
|
||||
}
|
||||
return classMap;
|
||||
}
|
||||
|
||||
private Class<?> getBeanBaseType(Class<?> beanType) {
|
||||
Class<?> parent = beanType.getSuperclass();
|
||||
|
||||
while (parent != null && parent.isAnnotationPresent(Entity.class)) {
|
||||
beanType = parent;
|
||||
parent = parent.getSuperclass();
|
||||
}
|
||||
return beanType;
|
||||
}
|
||||
|
||||
private static class ClassContext {
|
||||
|
||||
|
||||
@@ -593,12 +593,14 @@ public class JdbcTransaction implements SpiTransaction {
|
||||
}
|
||||
connection.commit();
|
||||
}
|
||||
// these will not throw an exception
|
||||
deactivate();
|
||||
notifyCommit();
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RollbackException(e);
|
||||
|
||||
} finally {
|
||||
// these will not throw an exception
|
||||
deactivate();
|
||||
notifyCommit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -634,12 +636,13 @@ public class JdbcTransaction implements SpiTransaction {
|
||||
try {
|
||||
connection.rollback();
|
||||
|
||||
} catch (Exception ex) {
|
||||
throw new PersistenceException(ex);
|
||||
|
||||
} finally {
|
||||
// these will not throw an exception
|
||||
deactivate();
|
||||
notifyRollback(cause);
|
||||
|
||||
} catch (Exception ex) {
|
||||
throw new PersistenceException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
@@ -26,10 +25,6 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.avaje.ebean.config.GlobalProperties;
|
||||
import com.avaje.ebeaninternal.api.ClassUtil;
|
||||
import com.avaje.ebeaninternal.server.util.ClassPathReader;
|
||||
import com.avaje.ebeaninternal.server.util.ClassPathSearchFilter;
|
||||
import com.avaje.ebeaninternal.server.util.ClassPathSearchMatcher;
|
||||
import com.avaje.ebeaninternal.server.util.DefaultClassPathReader;
|
||||
|
||||
/**
|
||||
* Can search the class path for classes using a ClassPathSearchMatcher. A
|
||||
@@ -45,8 +40,8 @@ public class ClassPathSearch {
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
private Object[] classPaths;
|
||||
|
||||
private List<Object> classPath = new ArrayList<Object>();
|
||||
|
||||
private ClassPathSearchFilter filter;
|
||||
|
||||
private ClassPathSearchMatcher matcher;
|
||||
@@ -79,16 +74,27 @@ public class ClassPathSearch {
|
||||
classPathReader = (ClassPathReader) ClassUtil.newInstance(cn, this.getClass());
|
||||
}
|
||||
|
||||
classPaths = classPathReader.readPath(classLoader);
|
||||
Object[] rawClassPaths = classPathReader.readPath(classLoader);
|
||||
|
||||
if (classPaths == null || classPaths.length == 0) {
|
||||
String msg = "ClassPath is EMPTY using ClassPathReader [" + classPathReader + "]";
|
||||
logger.warn(msg);
|
||||
if (rawClassPaths == null || rawClassPaths.length == 0) {
|
||||
logger.warn("ClassPath is EMPTY using ClassPathReader [" + classPathReader + "]");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rawClassPaths.length; i++) {
|
||||
// check for a jarfile with a manifest classpath (e.g. maven surefire)
|
||||
List<URI> classPathFromManifest = getClassPathFromManifest(rawClassPaths[i]);
|
||||
if (classPathFromManifest.isEmpty()) {
|
||||
classPath.add(rawClassPaths[i]);
|
||||
} else {
|
||||
classPath.addAll(classPathFromManifest);
|
||||
}
|
||||
}
|
||||
|
||||
boolean debug = GlobalProperties.getBoolean("ebean.debug.classpath", false);
|
||||
if (debug || logger.isTraceEnabled()) {
|
||||
logger.info("Classpath " + Arrays.toString(classPaths));
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
for (Object entry : classPath) {
|
||||
logger.debug("Classpath Entry: {}",entry);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
@@ -133,47 +139,52 @@ public class ClassPathSearch {
|
||||
*/
|
||||
public List<Class<?>> findClasses() throws IOException {
|
||||
|
||||
if (classPaths == null || classPaths.length == 0) {
|
||||
if (classPath.isEmpty()) {
|
||||
// returning an empty list
|
||||
return matchList;
|
||||
}
|
||||
|
||||
for (int i = 0; i < classPaths.length; i++) {
|
||||
int classPathSize = classPath.size();
|
||||
for (int i = 0; i < classPathSize; i++) {
|
||||
|
||||
ClassPathElement element = getClassPathElement(classPaths[i]);
|
||||
ClassPathElement element = getClassPathElement(classPath.get(i));
|
||||
|
||||
if (element.isDirectory()) {
|
||||
scanDirectory(element);
|
||||
|
||||
} else if (element.isJarOrWar()) {
|
||||
// search name including the ! offset if it is there
|
||||
if (filter.isSearchJar(element.getJarNameWithOffset())) {
|
||||
if (classPathSize == 1 || filter.isSearchJar(element.getJarNameWithOffset())) {
|
||||
scanJar(element);
|
||||
}
|
||||
|
||||
} else {
|
||||
String msg = "Error: expected classPath entry [" + element
|
||||
+ "] to be a directory or a .jar file but it is not either of those?";
|
||||
logger.error(msg);
|
||||
logger.error("Error: expected classPath entry [" + element+ "] to be a directory or a .jar file but it is not either of those?");
|
||||
}
|
||||
}
|
||||
|
||||
if (matchList.isEmpty()) {
|
||||
String msg = "No Entities found in ClassPath using ClassPathReader [" + classPathReader + "] Classpath Searched["
|
||||
+ Arrays.toString(classPaths) + "]";
|
||||
logger.warn(msg);
|
||||
logger.warn("No Entities found in ClassPath using ClassPathReader [" + classPathReader + "] Classpath Searched[" + classPath + "]");
|
||||
}
|
||||
|
||||
return matchList;
|
||||
}
|
||||
|
||||
private ClassPathElement getClassPathElement(Object classPathEntry) {
|
||||
private ClassPathElement getClassPathElement(Object classPathEntry) throws MalformedURLException {
|
||||
|
||||
if (!URL.class.isInstance(classPathEntry)) {
|
||||
URL fileUrl = null;
|
||||
|
||||
if (URI.class.isInstance(classPathEntry)) {
|
||||
fileUrl = ((URI)classPathEntry).toURL();
|
||||
|
||||
} else if (!URL.class.isInstance(classPathEntry)) {
|
||||
// assumed to be a file path
|
||||
return new ClassPathElement(classPathEntry.toString());
|
||||
|
||||
} else {
|
||||
fileUrl = (URL) classPathEntry;
|
||||
}
|
||||
URL fileUrl = (URL) classPathEntry;
|
||||
|
||||
if (!fileUrl.getPath().contains("!")) {
|
||||
return new ClassPathElement(new File(fileUrl.getFile()));
|
||||
}
|
||||
@@ -371,6 +382,34 @@ public class ClassPathSearch {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If URL and actually a jarfile with manifest return the derived classpath.
|
||||
*/
|
||||
private static List<URI> getClassPathFromManifest(Object classPathElement) {
|
||||
|
||||
try {
|
||||
if (classPathElement instanceof URL) {
|
||||
File file = new File(((URL)classPathElement).getFile());
|
||||
if (file.isDirectory()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
JarFile jarFile = new JarFile(file);
|
||||
try {
|
||||
return getClassPathFromManifest(file, jarFile.getManifest());
|
||||
} finally {
|
||||
jarFile.close();
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
|
||||
} catch (IOException e) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If a jarfile with a manifest claspath return that.
|
||||
*/
|
||||
private static List<URI> getClassPathFromManifest(File jarFile, Manifest manifest) {
|
||||
|
||||
if (manifest == null) {
|
||||
|
||||
@@ -11,11 +11,11 @@ public class ClassPathSearchFilter {
|
||||
|
||||
private static final String COM_AVAJE_EBEANINTERNAL_SERVER_BEAN = "com.avaje.ebeaninternal.server.bean";
|
||||
|
||||
private static final String COM_AVAJE_EBEAN_META = "com.avaje.ebean.meta";
|
||||
private static final String COM_AVAJE_EBEAN_META = "com.avaje.ebean.meta";
|
||||
|
||||
private boolean defaultPackageMatch = true;
|
||||
private boolean defaultPackageMatch = true;
|
||||
|
||||
private boolean defaultJarMatch = true;
|
||||
private boolean defaultJarMatch = false;
|
||||
|
||||
private String ebeanJarPrefix = "ebean";
|
||||
|
||||
@@ -121,7 +121,7 @@ public class ClassPathSearchFilter {
|
||||
* Add a jar to explicitly exclude in the search.
|
||||
*/
|
||||
public void excludeJar(String jarName) {
|
||||
includeJarSet.add(jarName);
|
||||
excludeJarSet.add(jarName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,7 +129,6 @@ public class ClassPathSearchFilter {
|
||||
*/
|
||||
public void includeJar(String jarName) {
|
||||
includeJarSet.add(jarName);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.avaje.tests.cascade;
|
||||
|
||||
import javax.persistence.OptimisticLockException;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.tests.model.basic.TSDetail;
|
||||
import com.avaje.tests.model.basic.TSMaster;
|
||||
|
||||
public class TestPrivateOwnedIgnoreTransientOrphan extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void test(){
|
||||
|
||||
/** new object **/
|
||||
TSMaster master0 = new TSMaster();
|
||||
|
||||
/** recovered after first save **/
|
||||
TSMaster master1 = null;
|
||||
|
||||
/** recovered after transient child ignored **/
|
||||
TSMaster master2 = null;
|
||||
|
||||
Ebean.save(master0);
|
||||
|
||||
master1 = Ebean.find(master0.getClass(), master0.getId());
|
||||
|
||||
// Add then remove a bean that was never saved (to the DB)
|
||||
master1.getDetails().add(new TSDetail());
|
||||
master1.getDetails().clear();
|
||||
|
||||
try{
|
||||
Ebean.save(master1);
|
||||
} catch (OptimisticLockException exception) {
|
||||
// Occured when the "unsaved" bean was wrongly being deleted
|
||||
Assert.fail("Optimistic lock exception wrongly thrown: " + exception.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
master2 = master1 = Ebean.find(master1.getClass(), master1.getId());
|
||||
|
||||
Assert.assertTrue(master2.getDetails().isEmpty());
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.avaje.tests.model.basic;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
@@ -14,7 +15,7 @@ public class UUTwo {
|
||||
|
||||
String name;
|
||||
|
||||
@ManyToOne
|
||||
@ManyToOne(cascade=CascadeType.PERSIST)
|
||||
UUOne master;
|
||||
|
||||
public UUID getId() {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.avaje.tests.model.inheritexposedtype;
|
||||
|
||||
import javax.persistence.DiscriminatorValue;
|
||||
import javax.persistence.Entity;
|
||||
|
||||
@Entity
|
||||
@DiscriminatorValue( "photo" )
|
||||
public class IXPhoto extends IXResource {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.avaje.tests.model.inheritexposedtype;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Inheritance;
|
||||
import javax.persistence.InheritanceType;
|
||||
|
||||
@Entity
|
||||
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
|
||||
public class IXResource {
|
||||
|
||||
@Id
|
||||
UUID id;
|
||||
|
||||
@Column(insertable=false, updatable=false)
|
||||
String dtype;
|
||||
|
||||
String name;
|
||||
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(UUID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getDtype() {
|
||||
return dtype;
|
||||
}
|
||||
|
||||
public void setDtype(String dtype) {
|
||||
this.dtype = dtype;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.avaje.tests.model.inheritexposedtype.test;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.tests.model.inheritexposedtype.IXPhoto;
|
||||
|
||||
public class TestInheritExposeDtype extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
IXPhoto p = new IXPhoto();
|
||||
p.setName("the name");
|
||||
Ebean.save(p);
|
||||
|
||||
// update
|
||||
p.setName("new name");
|
||||
Ebean.save(p);
|
||||
|
||||
// delete
|
||||
Ebean.delete(p);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.avaje.tests.model.inheritmany;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
@Entity
|
||||
public class IMRelated {
|
||||
|
||||
@Id
|
||||
Long id;
|
||||
|
||||
String name;
|
||||
|
||||
@ManyToOne(optional=false)
|
||||
IMRoot owner;
|
||||
|
||||
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 IMRoot getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(IMRoot owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.avaje.tests.model.inheritmany;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Inheritance;
|
||||
import javax.persistence.InheritanceType;
|
||||
import javax.persistence.OneToMany;
|
||||
|
||||
@Entity
|
||||
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
|
||||
public class IMRoot {
|
||||
|
||||
@Id
|
||||
Long id;
|
||||
|
||||
@OneToMany(mappedBy="owner")
|
||||
List<IMRelated> related;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public List<IMRelated> getRelated() {
|
||||
return related;
|
||||
}
|
||||
|
||||
public void setRelated(List<IMRelated> related) {
|
||||
this.related = related;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.avaje.tests.model.inheritmany;
|
||||
|
||||
import javax.persistence.DiscriminatorValue;
|
||||
import javax.persistence.Entity;
|
||||
|
||||
@Entity
|
||||
@DiscriminatorValue("ONE")
|
||||
public class IMRootOne extends IMRoot {
|
||||
|
||||
String name;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.avaje.tests.model.inheritmany;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.DiscriminatorValue;
|
||||
import javax.persistence.Entity;
|
||||
|
||||
@Entity
|
||||
@DiscriminatorValue("TWO")
|
||||
public class IMRootTwo extends IMRoot {
|
||||
|
||||
String title;
|
||||
|
||||
Date whenTitle;
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public Date getWhenTitle() {
|
||||
return whenTitle;
|
||||
}
|
||||
|
||||
public void setWhenTitle(Date whenTitle) {
|
||||
this.whenTitle = whenTitle;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.avaje.tests.query.other;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
import com.avaje.ebean.Expression;
|
||||
import com.avaje.ebean.Query;
|
||||
import com.avaje.tests.model.basic.Customer;
|
||||
|
||||
public class TestSubQueryBinding extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
EbeanServer server = Ebean.getServer(null);
|
||||
|
||||
Query<Customer> someCustomerIds = server.find(Customer.class).select("id").where().lt("id", 5).query();
|
||||
|
||||
Expression someCustIdsExpression = server.getExpressionFactory().in("id", someCustomerIds);
|
||||
|
||||
Query<Customer> query = server.find(Customer.class).where().like("name", "Rob%").not(someCustIdsExpression).query();
|
||||
|
||||
query.findList();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.avaje.tests.update;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.text.json.JsonContext;
|
||||
import com.avaje.ebean.text.json.JsonWriteOptions;
|
||||
import com.avaje.tests.model.basic.UUOne;
|
||||
import com.avaje.tests.model.basic.UUTwo;
|
||||
|
||||
public class TestJsonStatelessUpdate extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
UUOne one = new UUOne();
|
||||
one.setName("oneName");
|
||||
|
||||
Ebean.save(one);
|
||||
|
||||
UUTwo two = new UUTwo();
|
||||
two.setMaster(one);
|
||||
two.setName("twoName");
|
||||
|
||||
Ebean.save(two);
|
||||
|
||||
UUTwo twoX = Ebean.find(UUTwo.class, two.getId());
|
||||
|
||||
JsonContext jsonContext = Ebean.createJsonContext();
|
||||
|
||||
JsonWriteOptions writeOptions = JsonWriteOptions.parsePath("(id,name,master(*))");
|
||||
String jsonString = jsonContext.toJsonString(twoX, true, writeOptions);
|
||||
|
||||
System.out.println(jsonString);
|
||||
jsonString = jsonString.replace("twoName", "twoNameModified");
|
||||
jsonString = jsonString.replace("oneName", "oneNameModified");
|
||||
|
||||
|
||||
UUTwo two2 = jsonContext.toBean(UUTwo.class, jsonString);
|
||||
|
||||
Assert.assertEquals(twoX.getId(), two2.getId());
|
||||
Assert.assertEquals("twoNameModified", two2.getName());
|
||||
Assert.assertEquals("oneNameModified", two2.getMaster().getName());
|
||||
|
||||
// The update below cascades to also save "master" and that fails
|
||||
// as it thinks it should INSERT master rather than UPDATE master
|
||||
|
||||
// Ebean.update(two2);
|
||||
|
||||
|
||||
// The following is a workaround, to explicitly update master first
|
||||
// so then Ebean doesn't try to save it when two2 is updated
|
||||
Ebean.beginTransaction();
|
||||
try {
|
||||
UUOne master = two2.getMaster();
|
||||
Ebean.update(master);
|
||||
Ebean.update(two2);
|
||||
|
||||
} finally {
|
||||
Ebean.endTransaction();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package org.avaje.test.model.inheritmany.test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
|
||||
import com.avaje.tests.model.inheritmany.IMRelated;
|
||||
import com.avaje.tests.model.inheritmany.IMRoot;
|
||||
import com.avaje.tests.model.inheritmany.IMRootOne;
|
||||
import com.avaje.tests.model.inheritmany.IMRootTwo;
|
||||
|
||||
|
||||
public class TestInheritWithMany extends BaseTestCase {
|
||||
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
EbeanServer server = Ebean.getServer(null);
|
||||
Assert.assertNotNull(server);
|
||||
|
||||
SpiEbeanServer spiServer = (SpiEbeanServer)server;
|
||||
BeanDescriptor<IMRoot> beanDescriptor = spiServer.getBeanDescriptor(IMRoot.class);
|
||||
InheritInfo inheritInfo = beanDescriptor.getInheritInfo();
|
||||
Assert.assertNotNull(inheritInfo);
|
||||
|
||||
|
||||
IMRootOne one = new IMRootOne();
|
||||
one.setName("One Name");
|
||||
server.save(one);
|
||||
add(one, "aaa");
|
||||
add(one, "bbb");
|
||||
|
||||
|
||||
|
||||
IMRootTwo two = new IMRootTwo();
|
||||
two.setTitle("Two Title");
|
||||
server.save(two);
|
||||
add(two, "ccc");
|
||||
add(two, "ddd");
|
||||
|
||||
|
||||
List<IMRoot> list = server.find(IMRoot.class).select("id").findList();
|
||||
|
||||
for (IMRoot imRoot : list) {
|
||||
// lazy load the related OneToMany which is related to a non-leaf
|
||||
List<IMRelated> related = imRoot.getRelated();
|
||||
for (IMRelated imRelated : related) {
|
||||
imRelated.getName();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void add(IMRoot owner, String string) {
|
||||
|
||||
IMRelated relate = new IMRelated();
|
||||
relate.setName(string);
|
||||
relate.setOwner(owner);
|
||||
|
||||
EbeanServer server = Ebean.getServer(null);
|
||||
server.save(relate);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user