Browse Source
Extracted DelegatingDbRefResolver and associates from MappingMongoConverter. Let MongoDbFactory expose PersistenceExceptionTranslator only to prevent invalid dependency to core package. Renamed DbRefResolveCallback to ResolverCallback. Removed AbstractDbRefResolver and moved the functionality implemented there (triggering of exception translation) into the DbRefResolver. MappingMongoConverter now uses a slightly extended version of DbRefResolver so that we can essentially replace the MongoDbFactory dependency with the DbRefResolver one. Added support for Objenesis based lazy-loading proxies to support domain classes without a default constructor. Explicitly check for Spring 4 being present as with it the default ProxyFactory already supports that out of the box. Added missing JavaDoc and assertions. A lot of cleanups and removal of deprecation warnings in test cases.pull/92/head
54 changed files with 1128 additions and 875 deletions
@ -0,0 +1,34 @@
@@ -0,0 +1,34 @@
|
||||
/* |
||||
* Copyright 2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package org.springframework.data.mongodb; |
||||
|
||||
import org.springframework.dao.UncategorizedDataAccessException; |
||||
|
||||
/** |
||||
* @author Oliver Gierke |
||||
*/ |
||||
public class LazyLoadingException extends UncategorizedDataAccessException { |
||||
|
||||
private static final long serialVersionUID = -7089224903873220037L; |
||||
|
||||
/** |
||||
* @param msg |
||||
* @param cause |
||||
*/ |
||||
public LazyLoadingException(String msg, Throwable cause) { |
||||
super(msg, cause); |
||||
} |
||||
} |
||||
@ -0,0 +1,256 @@
@@ -0,0 +1,256 @@
|
||||
/* |
||||
* Copyright 2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package org.springframework.data.mongodb.core.convert; |
||||
|
||||
import java.lang.reflect.Method; |
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor; |
||||
import org.aopalliance.intercept.MethodInvocation; |
||||
import org.objenesis.Objenesis; |
||||
import org.objenesis.ObjenesisStd; |
||||
import org.springframework.aop.framework.ProxyFactory; |
||||
import org.springframework.cglib.proxy.Callback; |
||||
import org.springframework.cglib.proxy.Enhancer; |
||||
import org.springframework.cglib.proxy.Factory; |
||||
import org.springframework.cglib.proxy.MethodProxy; |
||||
import org.springframework.core.SpringVersion; |
||||
import org.springframework.dao.DataAccessException; |
||||
import org.springframework.dao.support.PersistenceExceptionTranslator; |
||||
import org.springframework.data.mongodb.LazyLoadingException; |
||||
import org.springframework.data.mongodb.MongoDbFactory; |
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; |
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.ClassUtils; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
import com.mongodb.DB; |
||||
import com.mongodb.DBRef; |
||||
|
||||
/** |
||||
* A {@link DbRefResolver} that resolves {@link org.springframework.data.mongodb.core.mapping.DBRef}s by delegating to a |
||||
* {@link DbRefResolverCallback} than is able to generate lazy loading proxies. |
||||
* |
||||
* @author Thomas Darimont |
||||
* @author Oliver Gierke |
||||
*/ |
||||
public class DefaultDbRefResolver implements DbRefResolver { |
||||
|
||||
private static final boolean IS_SPRING_4_OR_BETTER = SpringVersion.getVersion().startsWith("4"); |
||||
private static final boolean OBJENESIS_PRESENT = ClassUtils.isPresent("org.objenesis.Objenesis", null); |
||||
|
||||
private final MongoDbFactory mongoDbFactory; |
||||
private final PersistenceExceptionTranslator exceptionTranslator; |
||||
|
||||
/** |
||||
* Creates a new {@link DefaultDbRefResolver} with the given {@link MongoDbFactory}. |
||||
* |
||||
* @param mongoDbFactory must not be {@literal null}. |
||||
*/ |
||||
public DefaultDbRefResolver(MongoDbFactory mongoDbFactory) { |
||||
|
||||
Assert.notNull(mongoDbFactory, "MongoDbFactory translator must not be null!"); |
||||
|
||||
this.mongoDbFactory = mongoDbFactory; |
||||
this.exceptionTranslator = mongoDbFactory.getExceptionTranslator(); |
||||
} |
||||
|
||||
/* |
||||
* (non-Javadoc) |
||||
* @see org.springframework.data.mongodb.core.convert.DbRefResolver#resolveDbRef(org.springframework.data.mongodb.core.mapping.MongoPersistentProperty, org.springframework.data.mongodb.core.convert.DbRefResolverCallback) |
||||
*/ |
||||
@Override |
||||
public Object resolveDbRef(MongoPersistentProperty property, DbRefResolverCallback callback) { |
||||
|
||||
Assert.notNull(property, "Property must not be null!"); |
||||
Assert.notNull(callback, "Callback must not be null!"); |
||||
|
||||
if (isLazyDbRef(property)) { |
||||
return createLazyLoadingProxy(property, callback); |
||||
} |
||||
|
||||
return callback.resolve(property); |
||||
} |
||||
|
||||
/* |
||||
* (non-Javadoc) |
||||
* @see org.springframework.data.mongodb.core.convert.DbRefResolver#created(org.springframework.data.mongodb.core.mapping.MongoPersistentProperty, org.springframework.data.mongodb.core.mapping.MongoPersistentEntity, java.lang.Object) |
||||
*/ |
||||
@Override |
||||
public DBRef createDbRef(org.springframework.data.mongodb.core.mapping.DBRef annotation, |
||||
MongoPersistentEntity<?> entity, Object id) { |
||||
|
||||
DB db = mongoDbFactory.getDb(); |
||||
db = annotation != null && StringUtils.hasText(annotation.db()) ? mongoDbFactory.getDb(annotation.db()) : db; |
||||
|
||||
return new DBRef(db, entity.getCollection(), id); |
||||
} |
||||
|
||||
/** |
||||
* Creates a proxy for the given {@link MongoPersistentProperty} using the given {@link DbRefResolverCallback} to |
||||
* eventually resolve the value of the property. |
||||
* |
||||
* @param property must not be {@literal null}. |
||||
* @param callback must not be {@literal null}. |
||||
* @return |
||||
*/ |
||||
private Object createLazyLoadingProxy(MongoPersistentProperty property, DbRefResolverCallback callback) { |
||||
|
||||
ProxyFactory proxyFactory = new ProxyFactory(); |
||||
Class<?> propertyType = property.getType(); |
||||
|
||||
for (Class<?> type : propertyType.getInterfaces()) { |
||||
proxyFactory.addInterface(type); |
||||
} |
||||
|
||||
LazyLoadingInterceptor interceptor = new LazyLoadingInterceptor(property, exceptionTranslator, callback); |
||||
|
||||
if (propertyType.isInterface()) { |
||||
proxyFactory.addInterface(propertyType); |
||||
proxyFactory.addAdvice(interceptor); |
||||
return proxyFactory.getProxy(); |
||||
} |
||||
|
||||
proxyFactory.setProxyTargetClass(true); |
||||
proxyFactory.setTargetClass(propertyType); |
||||
|
||||
if (IS_SPRING_4_OR_BETTER || !OBJENESIS_PRESENT) { |
||||
proxyFactory.addAdvice(interceptor); |
||||
return proxyFactory.getProxy(); |
||||
} |
||||
|
||||
return ObjenesisProxyEnhancer.enhanceAndGet(proxyFactory, propertyType, interceptor); |
||||
} |
||||
|
||||
/** |
||||
* @param property |
||||
* @return |
||||
*/ |
||||
private boolean isLazyDbRef(MongoPersistentProperty property) { |
||||
return property.getDBRef() != null && property.getDBRef().lazy(); |
||||
} |
||||
|
||||
/** |
||||
* A {@link MethodInterceptor} that is used within a lazy loading proxy. The property resolving is delegated to a |
||||
* {@link DbRefResolverCallback}. The resolving process is triggered by a method invocation on the proxy and is |
||||
* guaranteed to be performed only once. |
||||
* |
||||
* @author Thomas Darimont |
||||
*/ |
||||
static class LazyLoadingInterceptor implements MethodInterceptor, org.springframework.cglib.proxy.MethodInterceptor { |
||||
|
||||
private final DbRefResolverCallback callback; |
||||
private final MongoPersistentProperty property; |
||||
private final PersistenceExceptionTranslator exceptionTranslator; |
||||
|
||||
private volatile boolean resolved; |
||||
private Object result; |
||||
|
||||
/** |
||||
* Creates a new {@link LazyLoadingInterceptor} for the given {@link MongoPersistentProperty}, |
||||
* {@link PersistenceExceptionTranslator} and {@link DbRefResolverCallback}. |
||||
* |
||||
* @param property must not be {@literal null}. |
||||
* @param callback must not be {@literal null}. |
||||
*/ |
||||
public LazyLoadingInterceptor(MongoPersistentProperty property, PersistenceExceptionTranslator exceptionTranslator, |
||||
DbRefResolverCallback callback) { |
||||
|
||||
Assert.notNull(property, "Property must not be null!"); |
||||
Assert.notNull(exceptionTranslator, "Exception translator must not be null!"); |
||||
Assert.notNull(callback, "Callback must not be null!"); |
||||
|
||||
this.callback = callback; |
||||
this.exceptionTranslator = exceptionTranslator; |
||||
this.property = property; |
||||
} |
||||
|
||||
/* |
||||
* (non-Javadoc) |
||||
* @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation) |
||||
*/ |
||||
@Override |
||||
public Object invoke(MethodInvocation invocation) throws Throwable { |
||||
return intercept(invocation.getThis(), invocation.getMethod(), invocation.getArguments(), null); |
||||
} |
||||
|
||||
/* |
||||
* (non-Javadoc) |
||||
* @see org.springframework.cglib.proxy.MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], org.springframework.cglib.proxy.MethodProxy) |
||||
*/ |
||||
@Override |
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { |
||||
|
||||
if (!resolved) { |
||||
this.result = resolve(); |
||||
this.resolved = true; |
||||
} |
||||
|
||||
return method.invoke(result, args); |
||||
} |
||||
|
||||
/** |
||||
* @return |
||||
*/ |
||||
private synchronized Object resolve() { |
||||
|
||||
if (!resolved) { |
||||
|
||||
try { |
||||
|
||||
return callback.resolve(property); |
||||
|
||||
} catch (RuntimeException ex) { |
||||
|
||||
DataAccessException translatedException = this.exceptionTranslator.translateExceptionIfPossible(ex); |
||||
throw new LazyLoadingException("Unable to lazily resolve DBRef!", translatedException); |
||||
} |
||||
} |
||||
|
||||
return result; |
||||
} |
||||
|
||||
public boolean isResolved() { |
||||
return resolved; |
||||
} |
||||
|
||||
public Object getResult() { |
||||
return result; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Static class to accomodate optional dependency on Objenesis. |
||||
* |
||||
* @author Oliver Gierke |
||||
*/ |
||||
private static class ObjenesisProxyEnhancer { |
||||
|
||||
private static final Objenesis OBJENESIS = new ObjenesisStd(true); |
||||
|
||||
public static Object enhanceAndGet(ProxyFactory proxyFactory, Class<?> type, |
||||
org.springframework.cglib.proxy.MethodInterceptor interceptor) { |
||||
|
||||
Enhancer enhancer = new Enhancer(); |
||||
enhancer.setSuperclass(type); |
||||
enhancer.setCallbackType(org.springframework.cglib.proxy.MethodInterceptor.class); |
||||
|
||||
Factory factory = (Factory) OBJENESIS.newInstance(enhancer.createClass()); |
||||
factory.setCallbacks(new Callback[] { interceptor }); |
||||
return factory; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,78 @@
@@ -0,0 +1,78 @@
|
||||
/* |
||||
* Copyright 2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package org.springframework.data.mongodb.config; |
||||
|
||||
import static org.hamcrest.CoreMatchers.*; |
||||
import static org.junit.Assert.*; |
||||
|
||||
import org.junit.After; |
||||
import org.junit.Before; |
||||
import org.junit.runner.RunWith; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.dao.DataAccessException; |
||||
import org.springframework.data.mongodb.core.CollectionCallback; |
||||
import org.springframework.data.mongodb.core.MongoOperations; |
||||
import org.springframework.test.context.ContextConfiguration; |
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; |
||||
|
||||
import com.mongodb.BasicDBObject; |
||||
import com.mongodb.DBCollection; |
||||
import com.mongodb.Mongo; |
||||
import com.mongodb.MongoClient; |
||||
import com.mongodb.MongoException; |
||||
|
||||
/** |
||||
* @author Oliver Gierke |
||||
*/ |
||||
@RunWith(SpringJUnit4ClassRunner.class) |
||||
@ContextConfiguration |
||||
public abstract class AbstractIntegrationTests { |
||||
|
||||
@Configuration |
||||
static class TestConfig extends AbstractMongoConfiguration { |
||||
|
||||
@Override |
||||
protected String getDatabaseName() { |
||||
return "database"; |
||||
} |
||||
|
||||
@Override |
||||
public Mongo mongo() throws Exception { |
||||
return new MongoClient(); |
||||
} |
||||
} |
||||
|
||||
@Autowired MongoOperations operations; |
||||
|
||||
@Before |
||||
@After |
||||
public void cleanUp() { |
||||
|
||||
for (String collectionName : operations.getCollectionNames()) { |
||||
if (!collectionName.startsWith("system")) { |
||||
operations.execute(collectionName, new CollectionCallback<Void>() { |
||||
@Override |
||||
public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException { |
||||
collection.remove(new BasicDBObject()); |
||||
assertThat(collection.find().hasNext(), is(false)); |
||||
return null; |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,339 @@
@@ -0,0 +1,339 @@
|
||||
/* |
||||
* Copyright 2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package org.springframework.data.mongodb.core.convert; |
||||
|
||||
import static org.hamcrest.Matchers.*; |
||||
import static org.junit.Assert.*; |
||||
import static org.mockito.Matchers.*; |
||||
import static org.mockito.Mockito.*; |
||||
import static org.springframework.data.mongodb.core.convert.LazyLoadingTestUtils.*; |
||||
|
||||
import java.math.BigInteger; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.HashMap; |
||||
import java.util.LinkedList; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.mockito.Mock; |
||||
import org.mockito.runners.MockitoJUnitRunner; |
||||
import org.springframework.data.annotation.Id; |
||||
import org.springframework.data.annotation.PersistenceConstructor; |
||||
import org.springframework.data.mapping.PropertyPath; |
||||
import org.springframework.data.mongodb.MongoDbFactory; |
||||
import org.springframework.data.mongodb.core.MongoExceptionTranslator; |
||||
import org.springframework.data.mongodb.core.convert.MappingMongoConverterUnitTests.Person; |
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext; |
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; |
||||
|
||||
import com.mongodb.BasicDBObject; |
||||
import com.mongodb.DBObject; |
||||
import com.mongodb.DBRef; |
||||
|
||||
/** |
||||
* @author Oliver Gierke |
||||
*/ |
||||
@RunWith(MockitoJUnitRunner.class) |
||||
public class DbRefMappingMongoConverterUnitTests { |
||||
|
||||
MappingMongoConverter converter; |
||||
MongoMappingContext mappingContext; |
||||
|
||||
@Mock MongoDbFactory dbFactory; |
||||
|
||||
@Before |
||||
public void setUp() { |
||||
|
||||
when(dbFactory.getExceptionTranslator()).thenReturn(new MongoExceptionTranslator()); |
||||
|
||||
this.mappingContext = new MongoMappingContext(); |
||||
this.converter = new MappingMongoConverter(new DefaultDbRefResolver(dbFactory), mappingContext); |
||||
} |
||||
|
||||
/** |
||||
* @see DATAMONGO-347 |
||||
*/ |
||||
@Test |
||||
public void createsSimpleDBRefCorrectly() { |
||||
|
||||
Person person = new Person(); |
||||
person.id = "foo"; |
||||
|
||||
DBRef dbRef = converter.toDBRef(person, null); |
||||
assertThat(dbRef.getId(), is((Object) "foo")); |
||||
assertThat(dbRef.getRef(), is("person")); |
||||
} |
||||
|
||||
/** |
||||
* @see DATAMONGO-657 |
||||
*/ |
||||
@Test |
||||
public void convertDocumentWithMapDBRef() { |
||||
|
||||
MapDBRef mapDBRef = new MapDBRef(); |
||||
|
||||
MapDBRefVal val = new MapDBRefVal(); |
||||
val.id = BigInteger.ONE; |
||||
|
||||
Map<String, MapDBRefVal> mapVal = new HashMap<String, MapDBRefVal>(); |
||||
mapVal.put("test", val); |
||||
|
||||
mapDBRef.map = mapVal; |
||||
|
||||
BasicDBObject dbObject = new BasicDBObject(); |
||||
converter.write(mapDBRef, dbObject); |
||||
|
||||
DBObject map = (DBObject) dbObject.get("map"); |
||||
|
||||
assertThat(map.get("test"), instanceOf(DBRef.class)); |
||||
|
||||
DBObject mapValDBObject = new BasicDBObject(); |
||||
mapValDBObject.put("_id", BigInteger.ONE); |
||||
|
||||
DBRef dbRef = mock(DBRef.class); |
||||
when(dbRef.fetch()).thenReturn(mapValDBObject); |
||||
|
||||
((DBObject) dbObject.get("map")).put("test", dbRef); |
||||
|
||||
MapDBRef read = converter.read(MapDBRef.class, dbObject); |
||||
|
||||
assertThat(read.map.get("test").id, is(BigInteger.ONE)); |
||||
} |
||||
|
||||
/** |
||||
* @see DATAMONGO-347 |
||||
*/ |
||||
@Test |
||||
public void createsDBRefWithClientSpecCorrectly() { |
||||
|
||||
PropertyPath path = PropertyPath.from("person", PersonClient.class); |
||||
MongoPersistentProperty property = mappingContext.getPersistentPropertyPath(path).getLeafProperty(); |
||||
|
||||
Person person = new Person(); |
||||
person.id = "foo"; |
||||
|
||||
DBRef dbRef = converter.toDBRef(person, property); |
||||
assertThat(dbRef.getId(), is((Object) "foo")); |
||||
assertThat(dbRef.getRef(), is("person")); |
||||
} |
||||
|
||||
/** |
||||
* @see DATAMONGO-348 |
||||
*/ |
||||
@Test |
||||
public void lazyLoadingProxyForLazyDbRefOnInterface() { |
||||
|
||||
String id = "42"; |
||||
String value = "bubu"; |
||||
MappingMongoConverter converterSpy = spy(converter); |
||||
doReturn(new BasicDBObject("_id", id).append("value", value)).when(converterSpy).readRef((DBRef) any()); |
||||
|
||||
BasicDBObject dbo = new BasicDBObject(); |
||||
ClassWithLazyDbRefs lazyDbRefs = new ClassWithLazyDbRefs(); |
||||
lazyDbRefs.dbRefToInterface = new LinkedList<LazyDbRefTarget>(Arrays.asList(new LazyDbRefTarget("1"))); |
||||
converterSpy.write(lazyDbRefs, dbo); |
||||
|
||||
ClassWithLazyDbRefs result = converterSpy.read(ClassWithLazyDbRefs.class, dbo); |
||||
|
||||
assertProxyIsResolved(result.dbRefToInterface, false); |
||||
assertThat(result.dbRefToInterface.get(0).getId(), is(id)); |
||||
assertProxyIsResolved(result.dbRefToInterface, true); |
||||
assertThat(result.dbRefToInterface.get(0).getValue(), is(value)); |
||||
} |
||||
|
||||
/** |
||||
* @see DATAMONGO-348 |
||||
*/ |
||||
@Test |
||||
public void lazyLoadingProxyForLazyDbRefOnConcreteCollection() { |
||||
|
||||
String id = "42"; |
||||
String value = "bubu"; |
||||
MappingMongoConverter converterSpy = spy(converter); |
||||
doReturn(new BasicDBObject("_id", id).append("value", value)).when(converterSpy).readRef((DBRef) any()); |
||||
|
||||
BasicDBObject dbo = new BasicDBObject(); |
||||
ClassWithLazyDbRefs lazyDbRefs = new ClassWithLazyDbRefs(); |
||||
lazyDbRefs.dbRefToConcreteCollection = new ArrayList<LazyDbRefTarget>(Arrays.asList(new LazyDbRefTarget(id, value))); |
||||
converterSpy.write(lazyDbRefs, dbo); |
||||
|
||||
ClassWithLazyDbRefs result = converterSpy.read(ClassWithLazyDbRefs.class, dbo); |
||||
|
||||
assertProxyIsResolved(result.dbRefToConcreteCollection, false); |
||||
assertThat(result.dbRefToConcreteCollection.get(0).getId(), is(id)); |
||||
assertProxyIsResolved(result.dbRefToConcreteCollection, true); |
||||
assertThat(result.dbRefToConcreteCollection.get(0).getValue(), is(value)); |
||||
} |
||||
|
||||
/** |
||||
* @see DATAMONGO-348 |
||||
*/ |
||||
@Test |
||||
public void lazyLoadingProxyForLazyDbRefOnConcreteType() { |
||||
|
||||
String id = "42"; |
||||
String value = "bubu"; |
||||
MappingMongoConverter converterSpy = spy(converter); |
||||
doReturn(new BasicDBObject("_id", id).append("value", value)).when(converterSpy).readRef((DBRef) any()); |
||||
|
||||
BasicDBObject dbo = new BasicDBObject(); |
||||
ClassWithLazyDbRefs lazyDbRefs = new ClassWithLazyDbRefs(); |
||||
lazyDbRefs.dbRefToConcreteType = new LazyDbRefTarget(id, value); |
||||
converterSpy.write(lazyDbRefs, dbo); |
||||
|
||||
ClassWithLazyDbRefs result = converterSpy.read(ClassWithLazyDbRefs.class, dbo); |
||||
|
||||
assertProxyIsResolved(result.dbRefToConcreteType, false); |
||||
assertThat(result.dbRefToConcreteType.getId(), is(id)); |
||||
assertProxyIsResolved(result.dbRefToConcreteType, true); |
||||
assertThat(result.dbRefToConcreteType.getValue(), is(value)); |
||||
} |
||||
|
||||
/** |
||||
* @see DATAMONGO-348 |
||||
*/ |
||||
@Test |
||||
public void lazyLoadingProxyForLazyDbRefOnConcreteTypeWithPersistenceConstructor() { |
||||
|
||||
String id = "42"; |
||||
String value = "bubu"; |
||||
MappingMongoConverter converterSpy = spy(converter); |
||||
doReturn(new BasicDBObject("_id", id).append("value", value)).when(converterSpy).readRef((DBRef) any()); |
||||
|
||||
BasicDBObject dbo = new BasicDBObject(); |
||||
ClassWithLazyDbRefs lazyDbRefs = new ClassWithLazyDbRefs(); |
||||
lazyDbRefs.dbRefToConcreteTypeWithPersistenceConstructor = new LazyDbRefTargetWithPeristenceConstructor( |
||||
(Object) id, (Object) value); |
||||
converterSpy.write(lazyDbRefs, dbo); |
||||
|
||||
ClassWithLazyDbRefs result = converterSpy.read(ClassWithLazyDbRefs.class, dbo); |
||||
|
||||
assertProxyIsResolved(result.dbRefToConcreteTypeWithPersistenceConstructor, false); |
||||
assertThat(result.dbRefToConcreteTypeWithPersistenceConstructor.getId(), is(id)); |
||||
assertProxyIsResolved(result.dbRefToConcreteTypeWithPersistenceConstructor, true); |
||||
assertThat(result.dbRefToConcreteTypeWithPersistenceConstructor.getValue(), is(value)); |
||||
} |
||||
|
||||
/** |
||||
* @see DATAMONGO-348 |
||||
*/ |
||||
@Test |
||||
public void lazyLoadingProxyForLazyDbRefOnConcreteTypeWithPersistenceConstructorButWithoutDefaultConstructor() { |
||||
|
||||
String id = "42"; |
||||
String value = "bubu"; |
||||
MappingMongoConverter converterSpy = spy(converter); |
||||
doReturn(new BasicDBObject("_id", id).append("value", value)).when(converterSpy).readRef((DBRef) any()); |
||||
|
||||
BasicDBObject dbo = new BasicDBObject(); |
||||
ClassWithLazyDbRefs lazyDbRefs = new ClassWithLazyDbRefs(); |
||||
lazyDbRefs.dbRefToConcreteTypeWithPersistenceConstructorWithoutDefaultConstructor = new LazyDbRefTargetWithPeristenceConstructorWithoutDefaultConstructor( |
||||
(Object) id, (Object) value); |
||||
converterSpy.write(lazyDbRefs, dbo); |
||||
|
||||
ClassWithLazyDbRefs result = converterSpy.read(ClassWithLazyDbRefs.class, dbo); |
||||
|
||||
assertProxyIsResolved(result.dbRefToConcreteTypeWithPersistenceConstructorWithoutDefaultConstructor, false); |
||||
assertThat(result.dbRefToConcreteTypeWithPersistenceConstructorWithoutDefaultConstructor.getId(), is(id)); |
||||
assertProxyIsResolved(result.dbRefToConcreteTypeWithPersistenceConstructorWithoutDefaultConstructor, true); |
||||
assertThat(result.dbRefToConcreteTypeWithPersistenceConstructorWithoutDefaultConstructor.getValue(), is(value)); |
||||
} |
||||
|
||||
class MapDBRef { |
||||
@org.springframework.data.mongodb.core.mapping.DBRef Map<String, MapDBRefVal> map; |
||||
} |
||||
|
||||
class MapDBRefVal { |
||||
BigInteger id; |
||||
} |
||||
|
||||
class PersonClient { |
||||
@org.springframework.data.mongodb.core.mapping.DBRef Person person; |
||||
} |
||||
|
||||
static class ClassWithLazyDbRefs { |
||||
|
||||
@Id String id; |
||||
@org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) List<LazyDbRefTarget> dbRefToInterface; |
||||
@org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) ArrayList<LazyDbRefTarget> dbRefToConcreteCollection; |
||||
@org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) LazyDbRefTarget dbRefToConcreteType; |
||||
@org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) LazyDbRefTargetWithPeristenceConstructor dbRefToConcreteTypeWithPersistenceConstructor; |
||||
@org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) LazyDbRefTargetWithPeristenceConstructorWithoutDefaultConstructor dbRefToConcreteTypeWithPersistenceConstructorWithoutDefaultConstructor; |
||||
} |
||||
|
||||
static class LazyDbRefTarget { |
||||
|
||||
@Id String id; |
||||
String value; |
||||
|
||||
public LazyDbRefTarget() { |
||||
this(null); |
||||
} |
||||
|
||||
public LazyDbRefTarget(String id) { |
||||
this(id, null); |
||||
} |
||||
|
||||
public LazyDbRefTarget(String id, String value) { |
||||
this.id = id; |
||||
this.value = value; |
||||
} |
||||
|
||||
public String getId() { |
||||
return id; |
||||
} |
||||
|
||||
public String getValue() { |
||||
return value; |
||||
} |
||||
} |
||||
|
||||
static class LazyDbRefTargetWithPeristenceConstructor extends LazyDbRefTarget { |
||||
|
||||
boolean persistenceConstructorCalled; |
||||
|
||||
public LazyDbRefTargetWithPeristenceConstructor() {} |
||||
|
||||
@PersistenceConstructor |
||||
public LazyDbRefTargetWithPeristenceConstructor(String id, String value) { |
||||
super(id, value); |
||||
this.persistenceConstructorCalled = true; |
||||
} |
||||
|
||||
public LazyDbRefTargetWithPeristenceConstructor(Object id, Object value) { |
||||
super(id.toString(), value.toString()); |
||||
} |
||||
} |
||||
|
||||
static class LazyDbRefTargetWithPeristenceConstructorWithoutDefaultConstructor extends LazyDbRefTarget { |
||||
|
||||
boolean persistenceConstructorCalled; |
||||
|
||||
@PersistenceConstructor |
||||
public LazyDbRefTargetWithPeristenceConstructorWithoutDefaultConstructor(String id, String value) { |
||||
super(id, value); |
||||
this.persistenceConstructorCalled = true; |
||||
} |
||||
|
||||
public LazyDbRefTargetWithPeristenceConstructorWithoutDefaultConstructor(Object id, Object value) { |
||||
super(id.toString(), value.toString()); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,50 @@
@@ -0,0 +1,50 @@
|
||||
/* |
||||
* Copyright 2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package org.springframework.data.mongodb.core.convert; |
||||
|
||||
import static org.hamcrest.CoreMatchers.*; |
||||
import static org.junit.Assert.*; |
||||
|
||||
import org.springframework.aop.framework.Advised; |
||||
import org.springframework.cglib.proxy.Factory; |
||||
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver.LazyLoadingInterceptor; |
||||
|
||||
/** |
||||
* Utility class to test proxy handling for lazy loading. |
||||
* |
||||
* @author Oliver Gierke |
||||
*/ |
||||
public class LazyLoadingTestUtils { |
||||
|
||||
/** |
||||
* Asserts that the given repository is resolved (expected is {@literal true}) and the value is non-{@literal null} or |
||||
* unresolved (expected is {@literal false}) and the value is {@literal null}. |
||||
* |
||||
* @param target |
||||
* @param expected |
||||
*/ |
||||
public static void assertProxyIsResolved(Object target, boolean expected) { |
||||
|
||||
LazyLoadingInterceptor interceptor = extractInterceptor(target); |
||||
assertThat(interceptor.isResolved(), is(expected)); |
||||
assertThat(interceptor.getResult(), is(expected ? notNullValue() : nullValue())); |
||||
} |
||||
|
||||
private static LazyLoadingInterceptor extractInterceptor(Object proxy) { |
||||
return (LazyLoadingInterceptor) (proxy instanceof Advised ? ((Advised) proxy).getAdvisors()[0].getAdvice() |
||||
: ((Factory) proxy).getCallback(0)); |
||||
} |
||||
} |
||||
@ -1,48 +0,0 @@
@@ -1,48 +0,0 @@
|
||||
/* |
||||
* Copyright 2010-2011 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package org.springframework.data.mongodb.core.geo; |
||||
|
||||
import com.mongodb.Mongo; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.data.mongodb.config.AbstractMongoConfiguration; |
||||
import org.springframework.data.mongodb.core.mapping.event.LoggingEventListener; |
||||
|
||||
@Configuration |
||||
public class GeoSpatialAppConfig extends AbstractMongoConfiguration { |
||||
|
||||
@Override |
||||
public String getDatabaseName() { |
||||
return "database"; |
||||
} |
||||
|
||||
@Override |
||||
@Bean |
||||
public Mongo mongo() throws Exception { |
||||
return new Mongo("127.0.0.1"); |
||||
} |
||||
|
||||
@Bean |
||||
public LoggingEventListener mappingEventsListener() { |
||||
return new LoggingEventListener(); |
||||
} |
||||
|
||||
@Override |
||||
public String getMappingBasePackage() { |
||||
return "org.springframework.data.mongodb.core.core"; |
||||
} |
||||
|
||||
} |
||||
@ -1,16 +0,0 @@
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<beans xmlns="http://www.springframework.org/schema/beans" |
||||
xmlns:mongo="http://www.springframework.org/schema/data/mongo" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd |
||||
http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd"> |
||||
|
||||
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate"> |
||||
<constructor-arg> |
||||
<mongo:db-factory dbname="database"/> |
||||
</constructor-arg> |
||||
</bean> |
||||
|
||||
<bean class="org.springframework.data.mongodb.core.mapping.event.LoggingEventListener"/> |
||||
|
||||
</beans> |
||||
Loading…
Reference in new issue