spring-data-commons object mapping support for MongoDB
This commit is contained in:
@@ -53,10 +53,10 @@ public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
|
||||
// System.err.println("Mongo persisted property [" + propertyName + "] :: " + propertyKey + " = " + dbo.get(propertyKey));
|
||||
if (propertyKey.startsWith("_")) {
|
||||
// Id or class
|
||||
changeSet.set(propertyName, dbo.get(propertyKey));
|
||||
changeSet.set(propertyKey, dbo.get(propertyKey));
|
||||
} else {
|
||||
//throw new IllegalStateException("Unknown property [" + propertyName + "] found in MongoDB store");
|
||||
changeSet.set(propertyName, dbo.get(propertyKey));
|
||||
changeSet.set(propertyKey, dbo.get(propertyKey));
|
||||
}
|
||||
}
|
||||
} catch (MongoException ex) {
|
||||
|
||||
@@ -4,28 +4,51 @@ import java.lang.reflect.Field;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.document.mongodb.MongoTemplate;
|
||||
import org.springframework.persistence.OrderedEntityOperations;
|
||||
import org.springframework.persistence.RelatedEntity;
|
||||
import org.springframework.persistence.support.ChangeSet;
|
||||
import org.springframework.persistence.support.ChangeSetBacked;
|
||||
import org.springframework.persistence.support.ChangeSetPersister.NotFoundException;
|
||||
import org.springframework.persistence.support.EntityInstantiator;
|
||||
import org.springframework.persistence.support.HashMapChangeSet;
|
||||
|
||||
import com.mongodb.DB;
|
||||
|
||||
public class MongoEntityOperations extends OrderedEntityOperations<Object, ChangeSetBacked> {
|
||||
|
||||
@Autowired
|
||||
private DB mongoDb;
|
||||
private MongoTemplate mongoTemplate;
|
||||
|
||||
@Autowired
|
||||
private EntityInstantiator<ChangeSetBacked, ChangeSet> entityInstantiator;
|
||||
|
||||
private MongoChangeSetPersister changeSetPersister;
|
||||
|
||||
public void setEntityInstantiator(EntityInstantiator<ChangeSetBacked, ChangeSet> entityInstantiator) {
|
||||
this.entityInstantiator = entityInstantiator;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setChangeSetPersister(MongoChangeSetPersister changeSetPersister) {
|
||||
this.changeSetPersister = changeSetPersister;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean cacheInEntity() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChangeSetBacked findEntity(Class<ChangeSetBacked> entityClass, Object pk) throws DataAccessException {
|
||||
throw new UnsupportedOperationException();
|
||||
public ChangeSetBacked findEntity(Class<ChangeSetBacked> entityClass, Object key) throws DataAccessException {
|
||||
try {
|
||||
ChangeSet cs = new HashMapChangeSet();
|
||||
changeSetPersister.getPersistentState(entityClass, key, cs);
|
||||
return entityInstantiator.createEntityFromState(cs, entityClass);
|
||||
}
|
||||
catch (NotFoundException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
package org.springframework.data.document.persistence;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.document.mongodb.MongoTemplate;
|
||||
import org.springframework.persistence.document.test.Account;
|
||||
import org.springframework.persistence.document.test.MongoPerson;
|
||||
import org.springframework.persistence.document.test.Person;
|
||||
import org.springframework.persistence.document.test.Resume;
|
||||
import org.springframework.test.annotation.Rollback;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.transaction.BeforeTransaction;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBCursor;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.Mongo;
|
||||
import com.mongodb.MongoException;
|
||||
@@ -26,12 +32,21 @@ public class CrossStoreMongoTests {
|
||||
|
||||
@Autowired
|
||||
private MongoTemplate mongoTemplate;
|
||||
|
||||
private EntityManager entityManager;
|
||||
|
||||
private String colName = MongoPerson.class.getSimpleName().toLowerCase();
|
||||
|
||||
@BeforeTransaction
|
||||
public void setUp() {
|
||||
DBCollection col = this.mongoTemplate.getCollection(MongoPerson.class.getSimpleName().toLowerCase());
|
||||
|
||||
@PersistenceContext
|
||||
public void setEntityManager(EntityManager entityManager) {
|
||||
this.entityManager = entityManager;
|
||||
}
|
||||
|
||||
private void clearData(String collectionName) {
|
||||
DBCollection col = this.mongoTemplate.getCollection(collectionName);
|
||||
if (col != null) {
|
||||
this.mongoTemplate.dropCollection(MongoPerson.class.getName());
|
||||
this.mongoTemplate.dropCollection(collectionName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +54,7 @@ public class CrossStoreMongoTests {
|
||||
@Transactional
|
||||
@Rollback(false)
|
||||
public void testUserConstructor() {
|
||||
clearData(colName);
|
||||
int age = 33;
|
||||
MongoPerson p = new MongoPerson("Thomas", age);
|
||||
Assert.assertEquals(age, p.getAge());
|
||||
@@ -49,14 +65,74 @@ public class CrossStoreMongoTests {
|
||||
@Test
|
||||
@Transactional
|
||||
public void testInstantiatedFinder() throws MongoException {
|
||||
String key = MongoPerson.class.getSimpleName().toLowerCase();
|
||||
DBCollection col = this.mongoTemplate.getCollection(key);
|
||||
DBCollection col = this.mongoTemplate.getCollection(colName);
|
||||
DBObject dbo = col.findOne();
|
||||
Object id1 = dbo.get("_id");
|
||||
MongoPerson found = MongoPerson.findPerson(id1);
|
||||
Object _id = dbo.get("_id");
|
||||
MongoPerson found = MongoPerson.findPerson(_id);
|
||||
Assert.assertNotNull(found);
|
||||
Assert.assertEquals(id1, found.getId());
|
||||
Assert.assertEquals(_id, found.getId());
|
||||
System.out.println("Loaded MongoPerson data: " + found);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
@Rollback(false)
|
||||
public void testCreateMongoToJpaEntityRelationship() {
|
||||
clearData(colName);
|
||||
Account a = new Account();
|
||||
a.setName("My Account");
|
||||
a.setFriendlyName("My Test Acct.");
|
||||
a.setBalance(123.45F);
|
||||
a.setId(2L);
|
||||
MongoPerson p = new MongoPerson("Jack", 22);
|
||||
entityManager.persist(a);
|
||||
p.setAccount(a);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void testReadMongoToJpaEntityRelationship() {
|
||||
DBCollection col = this.mongoTemplate.getCollection(colName);
|
||||
DBCursor dbc = col.find();
|
||||
Object _id = null;
|
||||
for (DBObject dbo : dbc) {
|
||||
System.out.println(dbo);
|
||||
if ("Jack".equals(dbo.get("name"))) {
|
||||
_id = dbo.get("_id");
|
||||
break;
|
||||
}
|
||||
}
|
||||
System.out.println(_id);
|
||||
MongoPerson found = MongoPerson.findPerson(_id);
|
||||
System.out.println(found);
|
||||
if (found != null)
|
||||
System.out.println(found.getAccount());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
@Rollback(false)
|
||||
public void testCreateJpaToMongoEntityRelationship() {
|
||||
clearData("resume");
|
||||
Person p = new Person("Thomas", 20);
|
||||
Resume r = new Resume();
|
||||
r.addEducation("Skanstulls High School, 1975");
|
||||
r.addEducation("Univ. of Stockholm, 1980");
|
||||
r.addJob("DiMark, DBA, 1990-2000");
|
||||
r.addJob("VMware, Developer, 2007-");
|
||||
p.setResume(r);
|
||||
p.setId(1L);
|
||||
entityManager.persist(p);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void testReadJpaToMongoEntityRelationship() {
|
||||
Person found = entityManager.find(Person.class, 1L);
|
||||
System.out.println(found);
|
||||
// TODO: This part isn't working yet - there is no reference to the Momgo _id stored in the db
|
||||
// if (found != null)
|
||||
// System.out.println(found.getResume());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,14 @@ public class Account {
|
||||
|
||||
private String whatever;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
@@ -47,6 +55,11 @@ public class Account {
|
||||
public void setWhatever(String whatever) {
|
||||
this.whatever = whatever;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Account [id=" + id + ", name=" + name + ", balance=" + balance
|
||||
+ ", friendlyName=" + friendlyName + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package org.springframework.persistence.document.test;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import org.springframework.persistence.RelatedEntity;
|
||||
|
||||
@Entity
|
||||
public class Person {
|
||||
|
||||
@Id Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
private int age;
|
||||
|
||||
private java.util.Date birthDate;
|
||||
|
||||
// @Document // need to decide what the annotation here should be
|
||||
@RelatedEntity
|
||||
public Resume resume;
|
||||
|
||||
public Person() {
|
||||
}
|
||||
|
||||
public Person(String name, int age) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
this.birthDate = new java.util.Date();
|
||||
}
|
||||
|
||||
public void birthday() {
|
||||
++age;
|
||||
}
|
||||
|
||||
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 int getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public void setAge(int age) {
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public java.util.Date getBirthDate() {
|
||||
return birthDate;
|
||||
}
|
||||
|
||||
public void setBirthDate(java.util.Date birthDate) {
|
||||
this.birthDate = birthDate;
|
||||
}
|
||||
|
||||
public Resume getResume() {
|
||||
return resume;
|
||||
}
|
||||
|
||||
public void setResume(Resume resume) {
|
||||
this.resume = resume;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.springframework.persistence.document.test;
|
||||
|
||||
import org.springframework.persistence.document.DocumentEntity;
|
||||
|
||||
@DocumentEntity
|
||||
public class Resume {
|
||||
|
||||
private String education = "";
|
||||
|
||||
private String jobs = "";
|
||||
|
||||
public String getEducation() {
|
||||
return education;
|
||||
}
|
||||
|
||||
public void addEducation(String education) {
|
||||
this.education = this.education + (this.education.length() > 0 ? "; " : "") + education;
|
||||
}
|
||||
|
||||
public String getJobs() {
|
||||
return jobs;
|
||||
}
|
||||
|
||||
public void addJob(String job) {
|
||||
this.jobs = this.jobs + (this.jobs.length() > 0 ? "; " : "") + job;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Resume [education=" + education + ", jobs=" + jobs + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,6 +16,37 @@
|
||||
<context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
|
||||
</context:component-scan>
|
||||
|
||||
<!-- Store spanning config -->
|
||||
|
||||
<bean class="org.springframework.persistence.StoreSpanning"
|
||||
factory-method="aspectOf" >
|
||||
<property name="mappingValidator">
|
||||
<bean class="org.springframework.persistence.support.SimpleMappingValidator" />
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="entityOperationsLocator" class="org.springframework.persistence.ChainingEntityOperationsLocator" />
|
||||
|
||||
<bean class="org.springframework.persistence.ChainingForeignStoreKeyManagerLocator" />
|
||||
|
||||
<bean class="org.springframework.persistence.PresentKeyForeignStoreKeyManager" autowire="constructor" />
|
||||
|
||||
<bean class="org.springframework.persistence.GeneratedFieldForeignStoreKeyManager" />
|
||||
|
||||
<bean class="org.springframework.persistence.document.MongoEntityOperations" >
|
||||
<property name="order" value="5" />
|
||||
<property name="entityInstantiator">
|
||||
<bean class="org.springframework.persistence.support.ChangeSetConstructorEntityInstantiator" />
|
||||
</property>
|
||||
<property name="changeSetPersister" ref="mongoChangeSetPersister" />
|
||||
</bean>
|
||||
|
||||
<bean class="org.springframework.persistence.EntityManagerJpaEntityOperations" />
|
||||
|
||||
<bean class="org.springframework.persistence.support.ChangeSetForeignStoreKeyManager">
|
||||
<property name="fieldDelimiter" value="#"/>
|
||||
</bean>
|
||||
|
||||
<!-- Mongo config -->
|
||||
<bean id="mongo" class="org.springframework.data.document.mongodb.MongoFactoryBean">
|
||||
<property name="host" value="localhost" />
|
||||
|
||||
@@ -24,32 +24,29 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.expression.BeanFactoryResolver;
|
||||
import org.springframework.core.CollectionFactory;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.data.document.mongodb.mapping.MappingException;
|
||||
import org.springframework.data.document.mongodb.mapping.MappingIntrospector;
|
||||
import org.springframework.data.document.mongodb.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mapping.model.MappingInstantiationException;
|
||||
import org.springframework.data.mapping.AssociationHandler;
|
||||
import org.springframework.data.mapping.MappingBeanHelper;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mapping.model.*;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import static org.springframework.data.document.mongodb.mapping.MappingIntrospector.isSimpleType;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
@@ -60,20 +57,22 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
|
||||
protected static final ConcurrentMap<Class<?>, Map<String, Field>> fieldsByName = new ConcurrentHashMap<Class<?>, Map<String, Field>>();
|
||||
|
||||
protected GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
|
||||
protected MongoMappingContext mappingContext;
|
||||
protected SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
|
||||
protected MappingContext mappingContext;
|
||||
protected ApplicationContext applicationContext;
|
||||
protected boolean autowirePersistentBeans = false;
|
||||
protected boolean useFieldAccessOnly = false;
|
||||
|
||||
public MappingMongoConverter() {
|
||||
initializeConverters();
|
||||
}
|
||||
|
||||
public MappingMongoConverter(MongoMappingContext mappingContext) {
|
||||
public MappingMongoConverter(MappingContext mappingContext) {
|
||||
this.mappingContext = mappingContext;
|
||||
initializeConverters();
|
||||
}
|
||||
|
||||
public MappingMongoConverter(MongoMappingContext mappingContext, List<Converter<?, ?>> converters) {
|
||||
public MappingMongoConverter(MappingContext mappingContext, List<Converter<?, ?>> converters) {
|
||||
this.mappingContext = mappingContext;
|
||||
if (null != converters) {
|
||||
for (Converter<?, ?> c : converters) {
|
||||
@@ -83,11 +82,11 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
|
||||
initializeConverters();
|
||||
}
|
||||
|
||||
public MongoMappingContext getMappingContext() {
|
||||
public MappingContext getMappingContext() {
|
||||
return mappingContext;
|
||||
}
|
||||
|
||||
public void setMappingContext(MongoMappingContext mappingContext) {
|
||||
public void setMappingContext(MappingContext mappingContext) {
|
||||
this.mappingContext = mappingContext;
|
||||
}
|
||||
|
||||
@@ -99,6 +98,14 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
|
||||
this.autowirePersistentBeans = autowirePersistentBeans;
|
||||
}
|
||||
|
||||
public boolean isUseFieldAccessOnly() {
|
||||
return useFieldAccessOnly;
|
||||
}
|
||||
|
||||
public void setUseFieldAccessOnly(boolean useFieldAccessOnly) {
|
||||
this.useFieldAccessOnly = useFieldAccessOnly;
|
||||
}
|
||||
|
||||
public <T> T convertObjectId(ObjectId id, Class<T> targetType) {
|
||||
return conversionService.convert(id, targetType);
|
||||
}
|
||||
@@ -112,208 +119,193 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
|
||||
return null;
|
||||
}
|
||||
|
||||
final StandardEvaluationContext ctx = new StandardEvaluationContext();
|
||||
final StandardEvaluationContext spelCtx = new StandardEvaluationContext();
|
||||
if (null != applicationContext) {
|
||||
ctx.setBeanResolver(new BeanFactoryResolver(applicationContext));
|
||||
spelCtx.setBeanResolver(new BeanFactoryResolver(applicationContext));
|
||||
}
|
||||
String[] keySet = dbo.keySet().toArray(new String[]{});
|
||||
for (String key : keySet) {
|
||||
ctx.setVariable(key, dbo.get(key));
|
||||
spelCtx.setVariable(key, dbo.get(key));
|
||||
}
|
||||
|
||||
try {
|
||||
if ((clazz.isArray() || (clazz.isAssignableFrom(Collection.class) || clazz.isAssignableFrom(List.class))) && dbo instanceof BasicDBList) {
|
||||
List l = new ArrayList<S>();
|
||||
BasicDBList dbList = (BasicDBList) dbo;
|
||||
for (Object o : dbList) {
|
||||
if (o instanceof DBObject) {
|
||||
Object newObj = read(clazz.getComponentType(), (DBObject) o);
|
||||
if (newObj.getClass().isAssignableFrom(clazz.getComponentType())) {
|
||||
l.add(newObj);
|
||||
} else {
|
||||
l.add(conversionService.convert(newObj, clazz.getComponentType()));
|
||||
}
|
||||
if ((clazz.isArray()
|
||||
|| (clazz.isAssignableFrom(Collection.class)
|
||||
|| clazz.isAssignableFrom(List.class)))
|
||||
&& dbo instanceof BasicDBList) {
|
||||
List l = new ArrayList<S>();
|
||||
BasicDBList dbList = (BasicDBList) dbo;
|
||||
for (Object o : dbList) {
|
||||
if (o instanceof DBObject) {
|
||||
Object newObj = read(clazz.getComponentType(), (DBObject) o);
|
||||
if (newObj.getClass().isAssignableFrom(clazz.getComponentType())) {
|
||||
l.add(newObj);
|
||||
} else {
|
||||
l.add(o);
|
||||
l.add(conversionService.convert(newObj, clazz.getComponentType()));
|
||||
}
|
||||
} else {
|
||||
l.add(o);
|
||||
}
|
||||
return conversionService.convert(l, clazz);
|
||||
}
|
||||
|
||||
final MappingIntrospector<S> introspector = MappingIntrospector.getInstance(clazz);
|
||||
final List<String> cparamNames = new ArrayList<String>();
|
||||
final S instance = introspector.createInstance(new MappingIntrospector.ParameterValueProvider() {
|
||||
public <T> T getParameterValue(String name, Class<T> type, Expression spelExpr) {
|
||||
Object o = getValueInternal(name, type, dbo, ctx, spelExpr);
|
||||
if (o instanceof DBRef) {
|
||||
cparamNames.add(name);
|
||||
return read(type, ((DBRef) o).fetch());
|
||||
} else if (null != o && o.getClass().isAssignableFrom(type)) {
|
||||
if (type == Object.class && dbo.containsField("_class")) {
|
||||
try {
|
||||
cparamNames.add(name);
|
||||
return (T) conversionService.convert(o, Class.forName(dbo.get("_class").toString()));
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new MappingInstantiationException(e.getMessage(), e);
|
||||
}
|
||||
} else {
|
||||
cparamNames.add(name);
|
||||
return (T) o;
|
||||
}
|
||||
} else {
|
||||
cparamNames.add(name);
|
||||
return conversionService.convert(o, type);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// The id is important. Set it first.
|
||||
if (dbo.containsField("_id")) {
|
||||
introspector.setValue(introspector.getIdField().getName(), instance, convertObjectId((ObjectId) dbo.get("_id"), introspector.getIdField().getType()));
|
||||
}
|
||||
|
||||
introspector.doWithProperties(new MappingIntrospector.PropertyHandler() {
|
||||
public void doWithProperty(PropertyDescriptor descriptor, Field field, Expression spelExpr) {
|
||||
String name = descriptor.getName();
|
||||
if (!cparamNames.contains(name)) {
|
||||
Object o = getValueInternal(name, descriptor.getPropertyType(), dbo, ctx, spelExpr);
|
||||
try {
|
||||
if (null != descriptor.getWriteMethod()) {
|
||||
Class<?> requiredType = descriptor.getWriteMethod().getParameterTypes()[0];
|
||||
if (null != o && o.getClass().isAssignableFrom(requiredType)) {
|
||||
introspector.setValue(name, instance, o);
|
||||
} else {
|
||||
introspector.setValue(name, instance, conversionService.convert(o, requiredType));
|
||||
}
|
||||
} else {
|
||||
introspector.setValue(name, instance, o);
|
||||
}
|
||||
} catch (MappingException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Handle mapping-specific stuff
|
||||
if (introspector.isMappable()) {
|
||||
introspector.doWithAssociations(new MappingIntrospector.AssociationHandler() {
|
||||
public void doWithAssociation(MappingIntrospector.Association association) {
|
||||
String name = association.getDescriptor().getName();
|
||||
Object targetObj = dbo.get(name);
|
||||
if (null != targetObj) {
|
||||
if (targetObj instanceof DBRef) {
|
||||
targetObj = read(association.getTargetClass(), ((DBRef) targetObj).fetch());
|
||||
} else if (targetObj instanceof BasicDBList) {
|
||||
BasicDBList dbList = (BasicDBList) targetObj;
|
||||
Collection<Object> targets = CollectionFactory.createCollection(association.getField().getType(), dbList.size());
|
||||
for (Object tgtDbObj : dbList) {
|
||||
if (tgtDbObj instanceof DBRef) {
|
||||
targets.add(read(association.getTargetClass(), ((DBRef) tgtDbObj).fetch()));
|
||||
} else if (tgtDbObj instanceof DBObject) {
|
||||
targets.add(read(association.getTargetClass(), (DBObject) tgtDbObj));
|
||||
} else if (tgtDbObj.getClass().isAssignableFrom(association.getTargetClass())) {
|
||||
targets.add(tgtDbObj);
|
||||
} else {
|
||||
targets.add(conversionService.convert(tgtDbObj, association.getTargetClass()));
|
||||
}
|
||||
}
|
||||
targetObj = targets;
|
||||
}
|
||||
introspector.setValue(name, instance, targetObj);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
introspector.maybeAutowire(instance, applicationContext, autowirePersistentBeans);
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Handing back configured object: " + instance);
|
||||
}
|
||||
return instance;
|
||||
} catch (MappingException e) {
|
||||
throw new RuntimeException(e);
|
||||
return conversionService.convert(l, clazz);
|
||||
}
|
||||
|
||||
// Retrieve persistent entity info
|
||||
PersistentEntity<S> entity = mappingContext.getPersistentEntity(clazz);
|
||||
|
||||
final List<String> ctorParamNames = new ArrayList<String>();
|
||||
final S instance = MappingBeanHelper.constructInstance(entity, new PreferredConstructor.ParameterValueProvider() {
|
||||
public Object getParameterValue(PreferredConstructor.Parameter parameter) {
|
||||
String name = parameter.getName();
|
||||
Class<?> type = parameter.getType();
|
||||
Object obj = dbo.get(name);
|
||||
if (obj instanceof DBRef) {
|
||||
ctorParamNames.add(name);
|
||||
return read(type, ((DBRef) obj).fetch());
|
||||
} else if (obj instanceof DBObject) {
|
||||
ctorParamNames.add(name);
|
||||
return read(type, ((DBObject) obj));
|
||||
} else if (null != obj && obj.getClass().isAssignableFrom(type)) {
|
||||
ctorParamNames.add(name);
|
||||
return obj;
|
||||
} else if (null != obj) {
|
||||
ctorParamNames.add(name);
|
||||
return conversionService.convert(obj, type);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}, spelCtx);
|
||||
|
||||
// Set the ID
|
||||
PersistentProperty<?> idProperty = entity.getIdProperty();
|
||||
if (dbo.containsField("_id") || null != idProperty) {
|
||||
Object idObj = dbo.get("_id");
|
||||
try {
|
||||
MappingBeanHelper.setProperty(instance, idProperty, idObj, useFieldAccessOnly);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// Set properties not already set in the constructor
|
||||
entity.doWithProperties(new PropertyHandler() {
|
||||
public void doWithPersistentProperty(PersistentProperty<?> prop) {
|
||||
if (!ctorParamNames.contains(prop.getName())) {
|
||||
Object obj = getValueInternal(prop, dbo, spelCtx, prop.getValueAnnotation());
|
||||
try {
|
||||
MappingBeanHelper.setProperty(instance, prop, obj, useFieldAccessOnly);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Handle associations
|
||||
entity.doWithAssociations(new AssociationHandler() {
|
||||
public void doWithAssociation(Association association) {
|
||||
PersistentProperty<?> inverseProp = association.getInverse();
|
||||
Object obj = getValueInternal(inverseProp, dbo, spelCtx, inverseProp.getValueAnnotation());
|
||||
try {
|
||||
MappingBeanHelper.setProperty(instance, inverseProp, obj);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (null != applicationContext && autowirePersistentBeans) {
|
||||
applicationContext.getAutowireCapableBeanFactory().autowireBean(instance);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void write(final Object o, final DBObject dbo) {
|
||||
try {
|
||||
final MappingIntrospector<?> introspector = MappingIntrospector.getInstance(o.getClass());
|
||||
//final PersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(o.getClass().getName());
|
||||
|
||||
Field idFld = introspector.getIdField();
|
||||
if (null != idFld) {
|
||||
Object idObj = introspector.getFieldValue(idFld.getName(), o);
|
||||
if (null != idObj) {
|
||||
dbo.put("_id", conversionService.convert(idObj, ObjectId.class));
|
||||
}
|
||||
}
|
||||
|
||||
introspector.doWithProperties(new MappingIntrospector.PropertyHandler() {
|
||||
public void doWithProperty(PropertyDescriptor descriptor, Field field, Expression spelExpr) {
|
||||
String name = descriptor.getName();
|
||||
try {
|
||||
Object newObj = introspector.getFieldValue(name, o);
|
||||
if (null != newObj) {
|
||||
if (isSimpleType(newObj.getClass())) {
|
||||
dbo.put(name, newObj);
|
||||
} else {
|
||||
if (newObj.getClass().isArray() || newObj.getClass().isAssignableFrom(Collection.class)) {
|
||||
BasicDBList dbList = new BasicDBList();
|
||||
for (Object collObj : (Object[]) (newObj.getClass().isArray() ? newObj : ((Collection) newObj).toArray())) {
|
||||
BasicDBObject newDbObj = new BasicDBObject();
|
||||
write(collObj, newDbObj);
|
||||
dbList.add(newDbObj);
|
||||
}
|
||||
dbo.put(name, dbList);
|
||||
} else {
|
||||
DBObject newDbObj = new BasicDBObject();
|
||||
write(newObj, newDbObj);
|
||||
dbo.put(name, newDbObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (MappingException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Handle mapping-specific stuff
|
||||
if (introspector.isMappable()) {
|
||||
introspector.doWithAssociations(new MappingIntrospector.AssociationHandler() {
|
||||
public void doWithAssociation(MappingIntrospector.Association association) {
|
||||
String name = association.getDescriptor().getName();
|
||||
Object targetObj = introspector.getFieldValue(name, o);
|
||||
if (null != targetObj) {
|
||||
if (isSimpleType(targetObj.getClass())) {
|
||||
dbo.put(name, targetObj);
|
||||
} else if (targetObj instanceof Collection || targetObj instanceof List) {
|
||||
// We're doing a ToMany
|
||||
BasicDBList dbRefList = new BasicDBList();
|
||||
for (Object depObj : (targetObj instanceof Collection ? (Collection) targetObj : (List) targetObj)) {
|
||||
DBObject depDbRef = createDBRef(depObj, depObj.getClass());
|
||||
if (null != depDbRef) {
|
||||
dbo.put(name, depDbRef);
|
||||
}
|
||||
dbRefList.add(depDbRef);
|
||||
}
|
||||
dbo.put(name, dbRefList);
|
||||
} else {
|
||||
DBObject depDbRef = createDBRef(targetObj, association.getTargetClass());
|
||||
if (null != depDbRef) {
|
||||
dbo.put(name, depDbRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} catch (MappingException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
public void write(final Object obj, final DBObject dbo) {
|
||||
if (null == obj) {
|
||||
return;
|
||||
}
|
||||
|
||||
PersistentEntity<?> entity = mappingContext.getPersistentEntity(obj.getClass());
|
||||
if (null == entity) {
|
||||
// Must not have explictly added this entity yet
|
||||
entity = mappingContext.addPersistentEntity(obj.getClass());
|
||||
if (null == entity) {
|
||||
// We can't map this entity for some reason
|
||||
throw new MappingException("Unable to map entity " + obj);
|
||||
}
|
||||
}
|
||||
|
||||
// Write the ID
|
||||
final PersistentProperty<?> idProperty = entity.getIdProperty();
|
||||
if (!dbo.containsField("_id") && null != idProperty) {
|
||||
Object idObj = null;
|
||||
try {
|
||||
idObj = MappingBeanHelper.getProperty(obj, idProperty, ObjectId.class, useFieldAccessOnly);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
if (null != idObj) {
|
||||
dbo.put("_id", idObj);
|
||||
}
|
||||
}
|
||||
|
||||
// Write the properties
|
||||
entity.doWithProperties(new PropertyHandler() {
|
||||
public void doWithPersistentProperty(PersistentProperty<?> prop) {
|
||||
String name = prop.getName();
|
||||
if (null != idProperty && name.equals(idProperty.getName())) {
|
||||
return;
|
||||
}
|
||||
if (prop.isAssociation()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Class<?> type = prop.getType();
|
||||
Object propertyObj = null;
|
||||
try {
|
||||
propertyObj = MappingBeanHelper.getProperty(obj, prop, type, useFieldAccessOnly);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
}
|
||||
if (null != propertyObj) {
|
||||
if (prop.isComplexType()) {
|
||||
writeObjectInternal(prop, propertyObj, dbo);
|
||||
} else {
|
||||
dbo.put(name, propertyObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
entity.doWithAssociations(new AssociationHandler() {
|
||||
public void doWithAssociation(Association association) {
|
||||
PersistentProperty<?> inverseProp = association.getInverse();
|
||||
Class<?> type = inverseProp.getType();
|
||||
Object propertyObj = null;
|
||||
try {
|
||||
propertyObj = MappingBeanHelper.getProperty(obj, inverseProp, type, useFieldAccessOnly);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
}
|
||||
if (null != propertyObj) {
|
||||
writeObjectInternal(inverseProp, propertyObj, dbo);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
@@ -329,16 +321,81 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
|
||||
conversionService.addConverter(ObjectIdToBigIntegerConverter.INSTANCE);
|
||||
conversionService.addConverter(BigIntegerToIdConverter.INSTANCE);
|
||||
}
|
||||
if (!conversionService.canConvert(ObjectId.class, Integer.class)) {
|
||||
conversionService.addConverter(IntegerToIdConverter.INSTANCE);
|
||||
conversionService.addConverter(IdToIntegerConverter.INSTANCE);
|
||||
}
|
||||
|
||||
protected void writeObjectInternal(PersistentProperty<?> prop, Object obj, DBObject dbo) {
|
||||
org.springframework.data.document.mongodb.mapping.DBRef dbref = prop.getField()
|
||||
.getAnnotation(org.springframework.data.document.mongodb.mapping.DBRef.class);
|
||||
String name = prop.getName();
|
||||
if (prop.isCollection()) {
|
||||
Class<?> type = prop.getType();
|
||||
BasicDBList dbList = new BasicDBList();
|
||||
Collection<?> coll = (type.isArray() ? Arrays.asList((Object[]) obj) : (Collection<?>) obj);
|
||||
for (Object propObjItem : coll) {
|
||||
if (null != dbref) {
|
||||
DBObject dbRefObj = createDBRef(propObjItem, dbref);
|
||||
if (null != dbRefObj) {
|
||||
dbList.add(dbRefObj);
|
||||
}
|
||||
} else {
|
||||
BasicDBObject propDbObj = new BasicDBObject();
|
||||
write(propObjItem, propDbObj);
|
||||
dbList.add(propDbObj);
|
||||
}
|
||||
}
|
||||
dbo.put(name, dbList);
|
||||
} else {
|
||||
if (null != dbref) {
|
||||
DBObject dbRefObj = createDBRef(obj, dbref);
|
||||
if (null != dbRefObj) {
|
||||
dbo.put(name, dbRefObj);
|
||||
}
|
||||
} else {
|
||||
BasicDBObject propDbObj = new BasicDBObject();
|
||||
write(obj, propDbObj);
|
||||
dbo.put(name, propDbObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Object getValueInternal(String name, Class<?> type, DBObject dbo, StandardEvaluationContext ctx, Expression spelExpr) {
|
||||
protected DBObject createDBRef(Object target, org.springframework.data.document.mongodb.mapping.DBRef dbref) {
|
||||
PersistentEntity<?> targetEntity = mappingContext.getPersistentEntity(target.getClass());
|
||||
if (null == targetEntity || null == targetEntity.getIdProperty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
DBObject dbo = new BasicDBObject();
|
||||
PersistentProperty<?> idProperty = targetEntity.getIdProperty();
|
||||
ObjectId id = null;
|
||||
try {
|
||||
id = MappingBeanHelper.getProperty(target, idProperty, ObjectId.class, useFieldAccessOnly);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
}
|
||||
dbo.put("$id", id);
|
||||
|
||||
String collection = dbref.collection();
|
||||
if ("".equals(collection)) {
|
||||
collection = targetEntity.getType().getSimpleName().toLowerCase();
|
||||
}
|
||||
dbo.put("$ref", collection);
|
||||
|
||||
String db = dbref.db();
|
||||
if (!"".equals(db)) {
|
||||
dbo.put("$db", db);
|
||||
}
|
||||
|
||||
return dbo;
|
||||
}
|
||||
|
||||
protected Object getValueInternal(PersistentProperty<?> prop, DBObject dbo, StandardEvaluationContext ctx, Value spelExpr) {
|
||||
String name = prop.getName();
|
||||
Object o;
|
||||
if (null != spelExpr) {
|
||||
o = spelExpr.getValue(ctx);
|
||||
Expression x = spelExpressionParser.parseExpression(spelExpr.value());
|
||||
o = x.getValue(ctx);
|
||||
} else {
|
||||
DBObject from = dbo;
|
||||
if (dbo instanceof DBRef) {
|
||||
@@ -346,6 +403,25 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
|
||||
}
|
||||
Object dbObj = from.get(name);
|
||||
if (dbObj instanceof DBObject) {
|
||||
Class<?> type = prop.getType();
|
||||
if (type.isArray() && dbObj instanceof BasicDBObject && ((DBObject) dbObj).keySet().size() == 0) {
|
||||
// It's empty
|
||||
return Array.newInstance(type.getComponentType(), 0);
|
||||
} else if (prop.isCollection() && dbObj instanceof BasicDBList) {
|
||||
BasicDBList dbObjList = (BasicDBList) dbObj;
|
||||
Object[] items = (Object[]) Array.newInstance(prop.getComponentType(), dbObjList.size());
|
||||
for (int i = 0; i < dbObjList.size(); i++) {
|
||||
Object dbObjItem = dbObjList.get(i);
|
||||
if (dbObjItem instanceof DBRef) {
|
||||
items[i] = read(prop.getComponentType(), ((DBRef) dbObjItem).fetch());
|
||||
} else if (dbObjItem instanceof DBObject) {
|
||||
items[i] = read(prop.getComponentType(), (DBObject) dbObjItem);
|
||||
} else {
|
||||
items[i] = dbObjItem;
|
||||
}
|
||||
}
|
||||
return Arrays.asList(items);
|
||||
}
|
||||
// It's a complex object, have to read it in
|
||||
o = read(type, (DBObject) dbObj);
|
||||
} else {
|
||||
@@ -355,21 +431,6 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
|
||||
return o;
|
||||
}
|
||||
|
||||
protected DBObject createDBRef(Object obj, Class<?> targetClass) {
|
||||
if (null != obj) {
|
||||
MappingIntrospector<?> introspector = MappingIntrospector.getInstance(obj.getClass());
|
||||
if (introspector.isMappable()) {
|
||||
String idName = introspector.getIdField().getName();
|
||||
Object idVal = introspector.getFieldValue(idName, obj);
|
||||
BasicDBObject dbRefDbo = new BasicDBObject();
|
||||
dbRefDbo.put("$ref", targetClass.getSimpleName().toLowerCase());
|
||||
dbRefDbo.put("$id", convertObjectId(idVal));
|
||||
return dbRefDbo;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple singleton to convert {@link ObjectId}s to their {@link String} representation.
|
||||
*
|
||||
@@ -422,26 +483,4 @@ public class MappingMongoConverter implements MongoConverter, ApplicationContext
|
||||
}
|
||||
}
|
||||
|
||||
public static enum IntegerToIdConverter implements Converter<Integer, ObjectId> {
|
||||
INSTANCE;
|
||||
|
||||
public ObjectId convert(Integer source) {
|
||||
String s = String.valueOf(source);
|
||||
StringBuffer buff = new StringBuffer();
|
||||
for (int i = 0; i < (16 - s.length()); i++) {
|
||||
buff.append("0");
|
||||
}
|
||||
buff.append(s);
|
||||
return new ObjectId(buff.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public static enum IdToIntegerConverter implements Converter<ObjectId, Integer> {
|
||||
INSTANCE;
|
||||
|
||||
public Integer convert(ObjectId source) {
|
||||
return Integer.parseInt(source.toString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ public @interface CompoundIndex {
|
||||
|
||||
String def();
|
||||
|
||||
IndexDirection direction() default IndexDirection.ASCENDING;
|
||||
|
||||
boolean unique() default false;
|
||||
|
||||
boolean sparse() default false;
|
||||
|
||||
@@ -27,6 +27,7 @@ import java.lang.annotation.Target;
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Indexed {
|
||||
|
||||
boolean unique() default false;
|
||||
|
||||
IndexDirection direction() default IndexDirection.ASCENDING;
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.mapping.annotation;
|
||||
package org.springframework.data.document.mongodb.mapping;
|
||||
|
||||
import org.springframework.data.annotation.Reference;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -25,9 +27,16 @@ import java.lang.annotation.Target;
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface Reference {
|
||||
@Target({
|
||||
ElementType.FIELD
|
||||
})
|
||||
@Reference
|
||||
public @interface DBRef {
|
||||
|
||||
Class<?> targetClass() default Object.class;
|
||||
String collection() default "";
|
||||
|
||||
String id() default "";
|
||||
|
||||
String db() default "";
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.mapping.annotation;
|
||||
package org.springframework.data.document.mongodb.mapping;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -26,12 +26,11 @@ import java.lang.annotation.Target;
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({
|
||||
ElementType.PARAMETER,
|
||||
ElementType.FIELD,
|
||||
ElementType.ANNOTATION_TYPE
|
||||
ElementType.TYPE,
|
||||
ElementType.FIELD
|
||||
})
|
||||
public @interface Value {
|
||||
|
||||
String value();
|
||||
public @interface Document {
|
||||
|
||||
String collection() default "";
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by the original author(s).
|
||||
*
|
||||
* 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.document.mongodb.mapping;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class MappingException extends RuntimeException {
|
||||
|
||||
private final Object source;
|
||||
|
||||
public MappingException(String s) {
|
||||
super(s);
|
||||
this.source = null;
|
||||
}
|
||||
|
||||
public MappingException(String s, Object source) {
|
||||
super(String.format("Error encountered mapping object: %s", source));
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public MappingException(String s, Throwable throwable, Object source) {
|
||||
super(s, throwable);
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public Object getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,499 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by the original author(s).
|
||||
*
|
||||
* 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.document.mongodb.mapping;
|
||||
|
||||
import com.mongodb.DBRef;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.bson.types.CodeWScope;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.expression.BeanFactoryResolver;
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.data.mapping.annotation.*;
|
||||
import org.springframework.data.mapping.model.MappingInstantiationException;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.*;
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class MappingIntrospector<T> {
|
||||
|
||||
private static final Log log = LogFactory.getLog(MappingIntrospector.class);
|
||||
private static final ConcurrentMap<Class<?>, MappingIntrospector<?>> introspectors = new ConcurrentHashMap<Class<?>, MappingIntrospector<?>>();
|
||||
private static final Set<String> SIMPLE_TYPES;
|
||||
|
||||
static {
|
||||
Set<String> basics = new HashSet<String>();
|
||||
basics.add(boolean.class.getName());
|
||||
basics.add(long.class.getName());
|
||||
basics.add(short.class.getName());
|
||||
basics.add(int.class.getName());
|
||||
basics.add(byte.class.getName());
|
||||
basics.add(float.class.getName());
|
||||
basics.add(double.class.getName());
|
||||
basics.add(char.class.getName());
|
||||
basics.add(Boolean.class.getName());
|
||||
basics.add(Long.class.getName());
|
||||
basics.add(Short.class.getName());
|
||||
basics.add(Integer.class.getName());
|
||||
basics.add(Byte.class.getName());
|
||||
basics.add(Float.class.getName());
|
||||
basics.add(Double.class.getName());
|
||||
basics.add(Character.class.getName());
|
||||
basics.add(String.class.getName());
|
||||
basics.add(java.util.Date.class.getName());
|
||||
basics.add(Locale.class.getName());
|
||||
basics.add(Class.class.getName());
|
||||
basics.add(DBRef.class.getName());
|
||||
basics.add(Pattern.class.getName());
|
||||
basics.add(CodeWScope.class.getName());
|
||||
basics.add(ObjectId.class.getName());
|
||||
// TODO check on enums..
|
||||
basics.add(Enum.class.getName());
|
||||
SIMPLE_TYPES = Collections.unmodifiableSet(basics);
|
||||
}
|
||||
|
||||
private final Class<T> clazz;
|
||||
private final BeanInfo beanInfo;
|
||||
private final Map<String, PropertyDescriptor> properties;
|
||||
private final Map<String, Field> fields;
|
||||
private final Set<Association> associations;
|
||||
private final Map<String, Method> setters;
|
||||
private final Map<String, Method> getters;
|
||||
private Field idField = null;
|
||||
private PropertyDescriptor idPropertyDescriptor = null;
|
||||
private List<String> ignoredProperties = new ArrayList<String>() {{
|
||||
add("class");
|
||||
}};
|
||||
private List<Class<?>> validIdTypes = new ArrayList<Class<?>>() {{
|
||||
add(ObjectId.class);
|
||||
add(String.class);
|
||||
add(BigInteger.class);
|
||||
}};
|
||||
|
||||
private SpelExpressionParser parser = new SpelExpressionParser();
|
||||
private Map<String, Expression> expressions = new HashMap<String, Expression>();
|
||||
private PreferredConstructor preferredConstructor = null;
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
private MappingIntrospector(Class<T> clazz) throws MappingException {
|
||||
this.clazz = clazz;
|
||||
try {
|
||||
this.beanInfo = Introspector.getBeanInfo(clazz);
|
||||
} catch (IntrospectionException e) {
|
||||
throw new MappingException(e.getMessage(), e, clazz);
|
||||
}
|
||||
Map<String, PropertyDescriptor> properties = new HashMap<String, PropertyDescriptor>();
|
||||
Map<String, Field> fields = new HashMap<String, Field>();
|
||||
Set<Association> associations = new HashSet<Association>();
|
||||
Map<String, Method> setters = new HashMap<String, Method>();
|
||||
Map<String, Method> getters = new HashMap<String, Method>();
|
||||
for (PropertyDescriptor descriptor : beanInfo.getPropertyDescriptors()) {
|
||||
String name = descriptor.getName();
|
||||
if (!ignoredProperties.contains(name)) {
|
||||
try {
|
||||
Field fld = clazz.getDeclaredField(name);
|
||||
Class<?> fldType = fld.getType();
|
||||
fld.setAccessible(true);
|
||||
if (!isTransientField(fld)) {
|
||||
if (fld.isAnnotationPresent(Id.class)) {
|
||||
if (null == idField) {
|
||||
idField = fld;
|
||||
idPropertyDescriptor = descriptor;
|
||||
} else {
|
||||
log.warn("Only the first field found with the @Id annotation will be considered the ID. Ignoring " + idField);
|
||||
}
|
||||
continue;
|
||||
} else if (null == idField
|
||||
&& validIdTypes.contains(fldType)
|
||||
&& ("id".equals(name) || "_id".equals(name))) {
|
||||
idField = fld;
|
||||
idPropertyDescriptor = descriptor;
|
||||
continue;
|
||||
} else if (fld.isAnnotationPresent(Reference.class)
|
||||
|| fld.isAnnotationPresent(OneToMany.class)
|
||||
|| fld.isAnnotationPresent(OneToOne.class)
|
||||
|| fld.isAnnotationPresent(ManyToMany.class)) {
|
||||
Class<?> targetClass = fld.getType();
|
||||
if (fld.isAnnotationPresent(Reference.class)) {
|
||||
Reference ref = fld.getAnnotation(Reference.class);
|
||||
if (ref.targetClass() != Object.class) {
|
||||
targetClass = ref.targetClass();
|
||||
}
|
||||
}
|
||||
if (fldType.isAssignableFrom(Collection.class) || fldType.isAssignableFrom(List.class)) {
|
||||
Type t = fld.getGenericType();
|
||||
if (t instanceof ParameterizedType) {
|
||||
ParameterizedType ptype = (ParameterizedType) t;
|
||||
Type[] paramTypes = ptype.getActualTypeArguments();
|
||||
if (paramTypes.length > 0) {
|
||||
if (paramTypes[0] instanceof TypeVariable) {
|
||||
targetClass = Object.class;
|
||||
} else {
|
||||
targetClass = (Class<?>) paramTypes[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
associations.add(new Association(descriptor, fld, targetClass));
|
||||
continue;
|
||||
} else if (fld.isAnnotationPresent(Value.class)) {
|
||||
// @Value fields are evaluated at runtime and are the same transient fields
|
||||
continue;
|
||||
}
|
||||
fields.put(name, fld);
|
||||
properties.put(name, descriptor);
|
||||
setters.put(name, descriptor.getWriteMethod());
|
||||
getters.put(name, descriptor.getReadMethod());
|
||||
if (fld.isAnnotationPresent(Value.class)) {
|
||||
expressions.put(name, parser.parseExpression(fld.getAnnotation(Value.class).value()));
|
||||
}
|
||||
}
|
||||
} catch (NoSuchFieldException e) {
|
||||
log.warn(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (null == this.idField) {
|
||||
// ID might be in a private field
|
||||
for (Field f : fields.values()) {
|
||||
Class<?> type = f.getType();
|
||||
if (f.isAnnotationPresent(Id.class)) {
|
||||
this.idField = f;
|
||||
break;
|
||||
} else if (validIdTypes.contains(type) && (f.getName().equals("id") || f.getName().equals("_id"))) {
|
||||
this.idField = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.properties = Collections.unmodifiableMap(properties);
|
||||
this.fields = Collections.unmodifiableMap(fields);
|
||||
this.associations = Collections.unmodifiableSet(associations);
|
||||
this.setters = Collections.unmodifiableMap(setters);
|
||||
this.getters = Collections.unmodifiableMap(getters);
|
||||
|
||||
// Find the right constructor
|
||||
for (Constructor<?> constructor : clazz.getConstructors()) {
|
||||
if (constructor.getParameterTypes().length != 0) {
|
||||
// Non-no-arg constructor
|
||||
if (null == preferredConstructor || constructor.isAnnotationPresent(PersistenceConstructor.class)) {
|
||||
String[] paramNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(constructor);
|
||||
Type[] paramTypes = constructor.getGenericParameterTypes();
|
||||
Class<?>[] paramClassTypes = new Class[paramTypes.length];
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
Class<?> targetType;
|
||||
if (paramTypes[i] instanceof ParameterizedType) {
|
||||
ParameterizedType ptype = (ParameterizedType) paramTypes[i];
|
||||
Type[] types = ptype.getActualTypeArguments();
|
||||
if (types.length == 1) {
|
||||
if (types[0] instanceof TypeVariable) {
|
||||
// Placeholder type
|
||||
targetType = Object.class;
|
||||
} else {
|
||||
targetType = (Class<?>) types[0];
|
||||
}
|
||||
} else {
|
||||
targetType = (Class<?>) ptype.getRawType();
|
||||
}
|
||||
} else {
|
||||
targetType = (Class<?>) paramTypes[i];
|
||||
}
|
||||
paramClassTypes[i] = targetType;
|
||||
}
|
||||
preferredConstructor = new PreferredConstructor((Constructor<T>) constructor, paramNames, paramClassTypes);
|
||||
if (constructor.isAnnotationPresent(PersistenceConstructor.class)) {
|
||||
// We're done
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public static <C extends Object> MappingIntrospector<C> getInstance(Class<C> clazz) throws MappingException {
|
||||
MappingIntrospector<C> introspector = (MappingIntrospector<C>) introspectors.get(clazz);
|
||||
if (null == introspector) {
|
||||
introspector = new MappingIntrospector<C>(clazz);
|
||||
introspectors.put(clazz, introspector);
|
||||
}
|
||||
return introspector;
|
||||
}
|
||||
|
||||
public Class<T> getTargetClass() {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
public Field getField(String name) {
|
||||
return fields.get(name);
|
||||
}
|
||||
|
||||
public PropertyDescriptor getPropertyDescriptor(String name) {
|
||||
return properties.get(name);
|
||||
}
|
||||
|
||||
public boolean isMappable() {
|
||||
return (null != clazz.getAnnotation(Persistent.class));
|
||||
}
|
||||
|
||||
public Field getIdField() {
|
||||
return idField;
|
||||
}
|
||||
|
||||
public PropertyDescriptor getIdPropertyDescriptor() {
|
||||
return idPropertyDescriptor;
|
||||
}
|
||||
|
||||
public T createInstance() {
|
||||
return createInstance(null);
|
||||
}
|
||||
|
||||
public T createInstance(ParameterValueProvider provider) {
|
||||
try {
|
||||
if (null == preferredConstructor || null == provider) {
|
||||
return clazz.newInstance();
|
||||
} else {
|
||||
List<Object> params = new LinkedList<Object>();
|
||||
for (ConstructorParameter<?> p : preferredConstructor.parameters) {
|
||||
Expression x = null;
|
||||
Value v = p.getValueAnnotation();
|
||||
if (null != v) {
|
||||
x = parser.parseExpression(v.value());
|
||||
}
|
||||
params.add(provider.getParameterValue(p.name, p.type, x));
|
||||
}
|
||||
return preferredConstructor.constructor.newInstance(params.toArray());
|
||||
}
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new MappingInstantiationException(e.getMessage(), e);
|
||||
} catch (InstantiationException e) {
|
||||
throw new MappingInstantiationException(e.getMessage(), e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new MappingInstantiationException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public Object getFieldValue(String name, Object from) throws MappingException {
|
||||
return getFieldValue(name, from, null);
|
||||
}
|
||||
|
||||
public Object getFieldValue(String name, Object from, Object defaultObj) throws MappingException {
|
||||
try {
|
||||
if (properties.containsKey(name) && null != getters.get(name)) {
|
||||
return getters.get(name).invoke(from);
|
||||
} else {
|
||||
if (fields.containsKey(name)) {
|
||||
Field f = fields.get(name);
|
||||
return f.get(from);
|
||||
} else {
|
||||
for (Association assoc : associations) {
|
||||
if (assoc.getField().getName().equals(name)) {
|
||||
return assoc.getField().get(from);
|
||||
}
|
||||
}
|
||||
for (Field f : clazz.getDeclaredFields()) {
|
||||
// Lastly, check for any private fields
|
||||
if (f.getName().equals(name)) {
|
||||
f.setAccessible(true);
|
||||
return f.get(from);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new MappingException(e.getMessage(), e, from);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new MappingException(e.getMessage(), e, from);
|
||||
}
|
||||
return defaultObj;
|
||||
}
|
||||
|
||||
public void setValue(String name, Object on, Object value) throws MappingException {
|
||||
Field f = fields.get(name);
|
||||
try {
|
||||
if (null != f) {
|
||||
Method setter = setters.get(name);
|
||||
if (null != setter) {
|
||||
setter.invoke(on, value);
|
||||
} else {
|
||||
f.set(on, value);
|
||||
}
|
||||
} else {
|
||||
for (Association assoc : associations) {
|
||||
if (assoc.getField().getName().equals(name)) {
|
||||
assoc.getField().set(on, value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (Field privFld : clazz.getDeclaredFields()) {
|
||||
if (privFld.getName().equals(name)) {
|
||||
privFld.setAccessible(true);
|
||||
privFld.set(on, value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new MappingException(e.getMessage(), e, value);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new MappingException(e.getMessage(), e, value);
|
||||
}
|
||||
}
|
||||
|
||||
public void maybeAutowire(Object obj, ApplicationContext applicationContext, boolean autowire) throws MappingException {
|
||||
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(obj);
|
||||
evaluationContext.setBeanResolver(new BeanFactoryResolver(applicationContext));
|
||||
|
||||
if (autowire) {
|
||||
applicationContext.getAutowireCapableBeanFactory().autowireBean(obj);
|
||||
if (obj instanceof InitializingBean) {
|
||||
try {
|
||||
((InitializingBean) obj).afterPropertiesSet();
|
||||
} catch (Exception e) {
|
||||
throw new MappingException(e.getMessage(), e, obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Expression> entry : expressions.entrySet()) {
|
||||
setValue(entry.getKey(), obj, entry.getValue().getValue(evaluationContext));
|
||||
}
|
||||
}
|
||||
|
||||
public void doWithProperties(PropertyHandler handler) {
|
||||
for (Map.Entry<String, PropertyDescriptor> entry : properties.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
handler.doWithProperty(entry.getValue(), fields.get(name), expressions.get(name));
|
||||
}
|
||||
}
|
||||
|
||||
public void doWithAssociations(AssociationHandler handler) {
|
||||
for (Association association : associations) {
|
||||
handler.doWithAssociation(association);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isSimpleType(Class<?> propertyType) {
|
||||
if (propertyType == null) {
|
||||
return false;
|
||||
}
|
||||
if (propertyType.isArray()) {
|
||||
return isSimpleType(propertyType.getComponentType());
|
||||
}
|
||||
return SIMPLE_TYPES.contains(propertyType.getName());
|
||||
}
|
||||
|
||||
public static boolean isTransientField(Field f) {
|
||||
return (Modifier.isTransient(f.getModifiers()) || null != f.getAnnotation(Transient.class) || null != f.getAnnotation(Autowired.class));
|
||||
}
|
||||
|
||||
public static class Association {
|
||||
|
||||
private final PropertyDescriptor descriptor;
|
||||
private final Field field;
|
||||
private final Class<?> targetClass;
|
||||
|
||||
public Association(PropertyDescriptor descriptor, Field field, Class<?> targetClass) {
|
||||
this.descriptor = descriptor;
|
||||
this.field = field;
|
||||
this.targetClass = targetClass;
|
||||
}
|
||||
|
||||
public PropertyDescriptor getDescriptor() {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
public Field getField() {
|
||||
return field;
|
||||
}
|
||||
|
||||
public Class<?> getTargetClass() {
|
||||
return targetClass;
|
||||
}
|
||||
}
|
||||
|
||||
private class PreferredConstructor {
|
||||
|
||||
Constructor<T> constructor;
|
||||
ConstructorParameter<?>[] parameters;
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public PreferredConstructor(Constructor<T> constructor, String[] names, Class<?>[] types) {
|
||||
this.constructor = constructor;
|
||||
Annotation[][] annos = constructor.getParameterAnnotations();
|
||||
parameters = new ConstructorParameter[names.length];
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
parameters[i] = new ConstructorParameter(names[i], types[i], annos[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ConstructorParameter<V> {
|
||||
String name;
|
||||
Class<V> type;
|
||||
Annotation[] annotations;
|
||||
|
||||
private ConstructorParameter(String name, Class<V> type, Annotation[] annotations) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.annotations = annotations;
|
||||
}
|
||||
|
||||
private Value getValueAnnotation() {
|
||||
for (Annotation anno : annotations) {
|
||||
if (anno instanceof Value) {
|
||||
return (Value) anno;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public interface AssociationHandler {
|
||||
void doWithAssociation(Association association);
|
||||
}
|
||||
|
||||
public interface PropertyHandler {
|
||||
void doWithProperty(PropertyDescriptor descriptor, Field field, Expression spelExpr);
|
||||
}
|
||||
|
||||
public interface ParameterValueProvider {
|
||||
<T> T getParameterValue(String name, Class<T> type, Expression spelExpr);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by the original author(s).
|
||||
*
|
||||
* 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.document.mongodb.mapping;
|
||||
|
||||
import org.springframework.data.mapping.model.AbstractClassMapping;
|
||||
import org.springframework.data.mapping.model.MappingContext;
|
||||
import org.springframework.data.mapping.model.PersistentEntity;
|
||||
|
||||
public class MongoClassMapping extends AbstractClassMapping<MongoCollection> {
|
||||
|
||||
public MongoClassMapping(PersistentEntity entity, MappingContext context) {
|
||||
super(entity, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoCollection getMappedForm() {
|
||||
return (MongoCollection) context.getMappingFactory().createMappedForm(entity);
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by the original author(s).
|
||||
*
|
||||
* 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.document.mongodb.mapping;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class MongoCollection {
|
||||
|
||||
private String name;
|
||||
|
||||
public MongoCollection(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by the original author(s).
|
||||
*
|
||||
* 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.document.mongodb.mapping;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.MongoException;
|
||||
import com.mongodb.util.JSON;
|
||||
import org.bson.types.CodeWScope;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.document.mongodb.CollectionCallback;
|
||||
import org.springframework.data.document.mongodb.MongoTemplate;
|
||||
import org.springframework.data.document.mongodb.index.CompoundIndex;
|
||||
import org.springframework.data.document.mongodb.index.CompoundIndexes;
|
||||
import org.springframework.data.document.mongodb.index.IndexDirection;
|
||||
import org.springframework.data.document.mongodb.index.Indexed;
|
||||
import org.springframework.data.mapping.BasicMappingConfigurationBuilder;
|
||||
import org.springframework.data.mapping.MappingBeanHelper;
|
||||
import org.springframework.data.mapping.model.*;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.math.BigInteger;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class MongoMappingConfigurationBuilder extends BasicMappingConfigurationBuilder {
|
||||
|
||||
protected Map<String, CompoundIndex> compoundIndexes = new HashMap<String, CompoundIndex>();
|
||||
protected Map<String, Indexed> fieldIndexes = new HashMap<String, Indexed>();
|
||||
protected MongoTemplate mongoTemplate;
|
||||
|
||||
public MongoMappingConfigurationBuilder(MongoTemplate mongoTemplate) {
|
||||
this.mongoTemplate = mongoTemplate;
|
||||
// Augment simpleTypes with MongoDB-specific classes
|
||||
Set<String> simpleTypes = MappingBeanHelper.getSimpleTypes();
|
||||
simpleTypes.add(DBRef.class.getName());
|
||||
simpleTypes.add(ObjectId.class.getName());
|
||||
simpleTypes.add(CodeWScope.class.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistentProperty<?> createPersistentProperty(Field field, PropertyDescriptor descriptor) throws MappingConfigurationException {
|
||||
PersistentProperty<?> property = super.createPersistentProperty(field, descriptor);
|
||||
if (field.isAnnotationPresent(Indexed.class)) {
|
||||
Indexed index = field.getAnnotation(Indexed.class);
|
||||
String collection = index.collection();
|
||||
if ("".equals(collection)) {
|
||||
collection = field.getName();
|
||||
}
|
||||
if (!fieldIndexes.containsKey(collection)) {
|
||||
ensureIndex(collection, index.name(), null, index.direction(), index.unique(), index.dropDups(), index.sparse());
|
||||
fieldIndexes.put(collection, index);
|
||||
}
|
||||
}
|
||||
return property;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
@Override
|
||||
public <T> PersistentEntity<T> createPersistentEntity(Class<T> type, MappingContext mappingContext) throws MappingConfigurationException {
|
||||
MongoPersistentEntity<T> entity = new MongoPersistentEntity<T>(mappingContext, type);
|
||||
|
||||
// Check for special collection setting
|
||||
if (type.isAnnotationPresent(Document.class)) {
|
||||
Document doc = type.getAnnotation(Document.class);
|
||||
String collection = doc.collection();
|
||||
if ("".equals(collection)) {
|
||||
collection = type.getSimpleName().toLowerCase();
|
||||
}
|
||||
entity.setCollection(collection);
|
||||
}
|
||||
|
||||
// Make sure indexes get created
|
||||
if (type.isAnnotationPresent(CompoundIndexes.class)) {
|
||||
CompoundIndexes indexes = type.getAnnotation(CompoundIndexes.class);
|
||||
for (CompoundIndex index : indexes.value()) {
|
||||
String indexColl = index.collection();
|
||||
if ("".equals(indexColl)) {
|
||||
indexColl = type.getSimpleName().toLowerCase();
|
||||
}
|
||||
if (!compoundIndexes.containsKey(indexColl)) {
|
||||
ensureIndex(indexColl, index.name(), index.def(), index.direction(), index.unique(), index.dropDups(), index.sparse());
|
||||
compoundIndexes.put(indexColl, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAssociation(Field field, PropertyDescriptor descriptor) throws MappingConfigurationException {
|
||||
if (field.isAnnotationPresent(DBRef.class)) {
|
||||
return true;
|
||||
}
|
||||
return super.isAssociation(field, descriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Association createAssociation(PersistentProperty<?> property) {
|
||||
return super.createAssociation(property);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isIdField(Field field) {
|
||||
if (super.isIdField(field)) {
|
||||
return true;
|
||||
}
|
||||
if (field.getType() == ObjectId.class || field.getType() == BigInteger.class) {
|
||||
if ("id".equals(field.getName()) || "_id".equals(field.getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void ensureIndex(String collection,
|
||||
final String name,
|
||||
final String def,
|
||||
final IndexDirection direction,
|
||||
final boolean unique,
|
||||
final boolean dropDups,
|
||||
final boolean sparse) {
|
||||
mongoTemplate.execute(collection, new CollectionCallback<Object>() {
|
||||
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
|
||||
DBObject defObj;
|
||||
if (null != def) {
|
||||
defObj = (DBObject) JSON.parse(def);
|
||||
} else {
|
||||
defObj = new BasicDBObject();
|
||||
defObj.put(name, (direction == IndexDirection.ASCENDING ? 1 : -1));
|
||||
}
|
||||
DBObject opts = new BasicDBObject();
|
||||
if (!"".equals(name)) {
|
||||
opts.put("name", name);
|
||||
}
|
||||
opts.put("dropDups", dropDups);
|
||||
opts.put("sparse", sparse);
|
||||
opts.put("unique", unique);
|
||||
collection.ensureIndex(defObj, opts);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,417 +0,0 @@
|
||||
package org.springframework.data.document.mongodb.mapping;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.MongoException;
|
||||
import com.mongodb.util.JSON;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.document.mongodb.CollectionCallback;
|
||||
import org.springframework.data.document.mongodb.MongoTemplate;
|
||||
import org.springframework.data.document.mongodb.index.CompoundIndex;
|
||||
import org.springframework.data.document.mongodb.index.CompoundIndexes;
|
||||
import org.springframework.data.document.mongodb.index.IndexDirection;
|
||||
import org.springframework.data.document.mongodb.index.Indexed;
|
||||
import org.springframework.data.mapping.annotation.IdentifiedBy;
|
||||
import org.springframework.data.mapping.annotation.OneToOne;
|
||||
import org.springframework.data.mapping.annotation.PersistenceStrategy;
|
||||
import org.springframework.data.mapping.annotation.Persistent;
|
||||
import org.springframework.data.mapping.model.*;
|
||||
import org.springframework.data.mapping.model.types.Association;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.common.TemplateParserContext;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author J. Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class MongoMappingConfigurationStrategy implements MappingConfigurationStrategy {
|
||||
|
||||
protected static final Log log = LogFactory.getLog(MongoMappingConfigurationStrategy.class);
|
||||
|
||||
protected MongoTemplate mongo;
|
||||
protected MongoMappingFactory mappingFactory;
|
||||
protected SpelExpressionParser expressionParser = new SpelExpressionParser();
|
||||
protected TemplateParserContext templateParserContext = new TemplateParserContext();
|
||||
protected Map<Class<?>, PersistenceDescriptor> descriptors = new ConcurrentHashMap<Class<?>, PersistenceDescriptor>();
|
||||
protected Map<Class<?>, Set<PersistentEntity>> owners = new LinkedHashMap<Class<?>, Set<PersistentEntity>>();
|
||||
|
||||
public MongoMappingConfigurationStrategy(MongoTemplate mongo) {
|
||||
this.mongo = mongo;
|
||||
}
|
||||
|
||||
public MongoMappingConfigurationStrategy(MongoTemplate mongo, MongoMappingFactory mappingFactory) {
|
||||
this.mongo = mongo;
|
||||
this.mappingFactory = mappingFactory;
|
||||
}
|
||||
|
||||
public SpelExpressionParser getExpressionParser() {
|
||||
return expressionParser;
|
||||
}
|
||||
|
||||
public void setExpressionParser(SpelExpressionParser expressionParser) {
|
||||
this.expressionParser = expressionParser;
|
||||
}
|
||||
|
||||
public TemplateParserContext getTemplateParserContext() {
|
||||
return templateParserContext;
|
||||
}
|
||||
|
||||
public void setTemplateParserContext(TemplateParserContext templateParserContext) {
|
||||
this.templateParserContext = templateParserContext;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public boolean isPersistentEntity(Class aClass) {
|
||||
if (null != aClass) {
|
||||
return (null != aClass.getAnnotation(Persistent.class));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<PersistentProperty> getPersistentProperties(Class aClass, MappingContext mappingContext) {
|
||||
return getPersistentProperties(aClass, mappingContext, null);
|
||||
}
|
||||
|
||||
public List<PersistentProperty> getPersistentProperties(Class aClass,
|
||||
MappingContext mappingContext,
|
||||
ClassMapping classMapping) {
|
||||
PersistenceDescriptor pd = null;
|
||||
try {
|
||||
pd = getPersistenceDescriptor(aClass, mappingContext, classMapping);
|
||||
} catch (MappingException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return (null != pd ? pd.getProperties() : null);
|
||||
}
|
||||
|
||||
public PersistentProperty getIdentity(Class aClass, MappingContext mappingContext) throws MappingException {
|
||||
PersistenceDescriptor idPd = getPersistenceDescriptor(aClass, mappingContext, null);
|
||||
return (null != idPd ? idPd.getIdProperty() : null);
|
||||
}
|
||||
|
||||
public IdentityMapping getDefaultIdentityMapping(final ClassMapping classMapping) throws MappingException {
|
||||
final PersistentProperty<?> prop = getPersistenceDescriptor(classMapping.getEntity().getJavaClass(),
|
||||
classMapping.getEntity().getMappingContext(),
|
||||
classMapping).getIdProperty();
|
||||
|
||||
return new IdentityMapping() {
|
||||
public String[] getIdentifierName() {
|
||||
return new String[]{prop.getName()};
|
||||
}
|
||||
|
||||
public ClassMapping getClassMapping() {
|
||||
return classMapping;
|
||||
}
|
||||
|
||||
public Object getMappedForm() {
|
||||
return classMapping.getMappedForm();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public Set getOwningEntities(Class aClass, MappingContext mappingContext) {
|
||||
return owners.get(aClass);
|
||||
}
|
||||
|
||||
protected PersistenceDescriptor getPersistenceDescriptor(final Class<?> javaClass,
|
||||
final MappingContext context,
|
||||
final ClassMapping mapping) throws MappingException {
|
||||
PersistenceDescriptor descriptor = descriptors.get(javaClass);
|
||||
if (null == descriptor) {
|
||||
final MappingIntrospector introspector = MappingIntrospector.getInstance(javaClass);
|
||||
final PersistentEntity entity = getPersistentEntity(javaClass, context, mapping);
|
||||
|
||||
final String collection = javaClass.getSimpleName().toLowerCase();
|
||||
for (Annotation anno : javaClass.getAnnotations()) {
|
||||
if (anno instanceof Persistent) {
|
||||
|
||||
} else if (anno instanceof CompoundIndexes) {
|
||||
CompoundIndexes idxs = (CompoundIndexes) anno;
|
||||
for (CompoundIndex idx : idxs.value()) {
|
||||
String idxColl = collection;
|
||||
if (!"".equals(idx.collection())) {
|
||||
idxColl = idx.collection();
|
||||
}
|
||||
String name = null;
|
||||
if (!"".equals(idx.name())) {
|
||||
name = idx.name();
|
||||
}
|
||||
ensureIndex(idxColl, name, idx.def(), null, idx.unique(), idx.dropDups(), idx.sparse());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EvaluationContext elContext = new StandardEvaluationContext();
|
||||
elContext.setVariable("class", javaClass);
|
||||
|
||||
PropertyDescriptor idPropDesc = MappingIntrospector.getInstance(entity.getJavaClass()).getIdPropertyDescriptor();
|
||||
PersistentProperty<?> id = mappingFactory.createIdentity(entity, context, idPropDesc);
|
||||
|
||||
final List<PersistentProperty> properties = new LinkedList<PersistentProperty>();
|
||||
introspector.doWithProperties(new MappingIntrospector.PropertyHandler() {
|
||||
public void doWithProperty(PropertyDescriptor descriptor, Field field, Expression spelExpr) {
|
||||
PersistentProperty<?> p = null;
|
||||
try {
|
||||
p = createPersistentProperty(entity, context, descriptor, mapping);
|
||||
if (null != p) {
|
||||
properties.add(p);
|
||||
for (Annotation anno : field.getDeclaredAnnotations()) {
|
||||
if (anno instanceof Indexed) {
|
||||
Indexed idx = (Indexed) anno;
|
||||
String idxColl = collection;
|
||||
if (!"".equals(idx.collection())) {
|
||||
idxColl = idx.collection();
|
||||
}
|
||||
String name = p.getName();
|
||||
if (!"".equals(idx.name())) {
|
||||
name = idx.name();
|
||||
}
|
||||
ensureIndex(idxColl, name, null, idx.direction(), idx.unique(), idx.dropDups(), idx.sparse());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (MappingException e) {
|
||||
throw new IllegalMappingException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
descriptor = new PersistenceDescriptor(entity, id, properties);
|
||||
descriptors.put(javaClass, descriptor);
|
||||
}
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
protected PersistentEntity getPersistentEntity(Class<?> javaClass, MappingContext context, ClassMapping mapping) {
|
||||
Assert.notNull(javaClass);
|
||||
if (null != mapping) {
|
||||
return mapping.getEntity();
|
||||
} else {
|
||||
return context.getPersistentEntity(javaClass.getName());
|
||||
}
|
||||
}
|
||||
|
||||
protected PersistentProperty<?> createPersistentProperty(PersistentEntity entity,
|
||||
MappingContext mappingContext,
|
||||
PropertyDescriptor descriptor,
|
||||
ClassMapping mapping) throws MappingException {
|
||||
MappingIntrospector introspector = MappingIntrospector.getInstance(entity.getJavaClass());
|
||||
Field f = introspector.getField(descriptor.getName());
|
||||
|
||||
if (null != f) {
|
||||
// Handle associations and persistent types
|
||||
PersistentProperty<?> prop = extractChildType(entity, mappingContext, descriptor, mapping);
|
||||
if (null == prop) {
|
||||
// Must be a simple type
|
||||
prop = mappingFactory.createSimple(entity, mappingContext, descriptor);
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected PersistentProperty<?> extractChildType(PersistentEntity entity,
|
||||
MappingContext mappingContext,
|
||||
PropertyDescriptor descriptor,
|
||||
ClassMapping mapping) throws MappingException {
|
||||
MappingIntrospector introspector = MappingIntrospector.getInstance(entity.getJavaClass());
|
||||
Field f = introspector.getField(descriptor.getName());
|
||||
|
||||
Class<?> childClass = null;
|
||||
Association<?> assoc = null;
|
||||
if (f.getType().isAssignableFrom(List.class)) {
|
||||
if (null != f.getAnnotation(org.springframework.data.mapping.annotation.OneToMany.class)) {
|
||||
org.springframework.data.mapping.annotation.OneToMany otm = f.getAnnotation(
|
||||
org.springframework.data.mapping.annotation.OneToMany.class);
|
||||
if (Object.class != otm.targetClass()) {
|
||||
childClass = otm.targetClass();
|
||||
} else {
|
||||
childClass = extractType(f);
|
||||
}
|
||||
assoc = mappingFactory.createOneToMany(entity, mappingContext, descriptor);
|
||||
} else if (null != f.getAnnotation(org.springframework.data.mapping.annotation.ManyToMany.class)) {
|
||||
org.springframework.data.mapping.annotation.ManyToMany mtm = f.getAnnotation(
|
||||
org.springframework.data.mapping.annotation.ManyToMany.class);
|
||||
if (Object.class != mtm.targetClass()) {
|
||||
childClass = mtm.targetClass();
|
||||
} else {
|
||||
childClass = extractType(f);
|
||||
}
|
||||
assoc = mappingFactory.createManyToMany(entity, mappingContext, descriptor);
|
||||
}
|
||||
} else {
|
||||
if (null != f.getAnnotation(OneToOne.class) || null != f.getType().getAnnotation(Persistent.class)) {
|
||||
childClass = f.getType();
|
||||
assoc = mappingFactory.createOneToOne(entity, mappingContext, descriptor);
|
||||
}
|
||||
}
|
||||
if (null != childClass && null != assoc) {
|
||||
if (childClass != entity.getJavaClass()) {
|
||||
PersistentEntity childEntity = getPersistentEntity(childClass, mappingContext, mapping);
|
||||
addOwner(entity.getJavaClass(), childEntity);
|
||||
assoc.setAssociatedEntity(childEntity);
|
||||
} else {
|
||||
addOwner(entity.getJavaClass(), entity);
|
||||
assoc.setAssociatedEntity(entity);
|
||||
}
|
||||
}
|
||||
|
||||
return assoc;
|
||||
}
|
||||
|
||||
protected Class<?> extractType(Field f) {
|
||||
Type t = f.getGenericType();
|
||||
if (null != t && t instanceof ParameterizedType) {
|
||||
Type[] types = ((ParameterizedType) t).getActualTypeArguments();
|
||||
return ((Class) types[0]);
|
||||
}
|
||||
return f.getType();
|
||||
}
|
||||
|
||||
protected IdentifiedBy extractIdentifiedBy(Class<?> clazz, Field field) {
|
||||
Persistent cAnno = clazz.getAnnotation(Persistent.class);
|
||||
Persistent fldAnno = (null != field ? field.getAnnotation(Persistent.class) : null);
|
||||
return (null != fldAnno ? fldAnno.identifiedBy() : cAnno.identifiedBy());
|
||||
}
|
||||
|
||||
protected PersistenceStrategy extractPersistenceStrategy(Class<?> clazz, Field field) {
|
||||
Persistent cAnno = clazz.getAnnotation(Persistent.class);
|
||||
Persistent fldAnno = (null != field ? field.getAnnotation(Persistent.class) : null);
|
||||
return (null != fldAnno ? fldAnno.strategy() : cAnno.strategy());
|
||||
}
|
||||
|
||||
protected String extractId(Class<?> clazz, Field field) {
|
||||
Persistent cAnno = clazz.getAnnotation(Persistent.class);
|
||||
Persistent fldAnno = (null != field ? field.getAnnotation(Persistent.class) : null);
|
||||
return (null != fldAnno ? fldAnno.id() : cAnno.id());
|
||||
}
|
||||
|
||||
protected void addOwner(Class<?> javaClass, PersistentEntity parent) {
|
||||
if (!owners.containsKey(javaClass)) {
|
||||
Set<PersistentEntity> owningEntities = new HashSet<PersistentEntity>();
|
||||
owningEntities.add(parent);
|
||||
owners.put(javaClass, owningEntities);
|
||||
}
|
||||
}
|
||||
|
||||
protected void ensureIndex(String collection,
|
||||
final String name,
|
||||
final String def,
|
||||
final IndexDirection direction,
|
||||
final boolean unique,
|
||||
final boolean dropDups,
|
||||
final boolean sparse) {
|
||||
mongo.execute(collection, new CollectionCallback<Object>() {
|
||||
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
|
||||
DBObject defObj;
|
||||
if (null != def) {
|
||||
defObj = (DBObject) JSON.parse(def);
|
||||
} else {
|
||||
defObj = new BasicDBObject();
|
||||
defObj.put(name, (direction == IndexDirection.ASCENDING ? 1 : -1));
|
||||
}
|
||||
DBObject opts = new BasicDBObject();
|
||||
if (!"".equals(name)) {
|
||||
opts.put("name", name);
|
||||
}
|
||||
opts.put("dropDups", dropDups);
|
||||
opts.put("sparse", sparse);
|
||||
opts.put("unique", unique);
|
||||
collection.ensureIndex(defObj, opts);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected class PersistenceDescriptor {
|
||||
|
||||
PersistentEntity entity;
|
||||
PersistentProperty idProperty = null;
|
||||
List<PersistentProperty> properties = null;
|
||||
|
||||
PersistenceDescriptor(PersistentEntity entity, PersistentProperty idProperty, List<PersistentProperty> properties) {
|
||||
this.entity = entity;
|
||||
this.idProperty = idProperty;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public PersistentEntity getEntity() {
|
||||
return entity;
|
||||
}
|
||||
|
||||
public PersistentProperty getIdProperty() {
|
||||
return idProperty;
|
||||
}
|
||||
|
||||
public List<PersistentProperty> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected class ValuePropertyDescriptor extends PropertyDescriptor {
|
||||
|
||||
Object value;
|
||||
|
||||
ValuePropertyDescriptor(String s, Class<?> aClass, Object value) throws IntrospectionException {
|
||||
super(s, aClass);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(Object value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getReadMethod() {
|
||||
try {
|
||||
return getClass().getMethod("getValue", null);
|
||||
} catch (NoSuchMethodException e) {
|
||||
// IGNORED
|
||||
}
|
||||
return super.getReadMethod();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadMethod(Method method) throws IntrospectionException {
|
||||
// IGNORED
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getWriteMethod() {
|
||||
try {
|
||||
return getClass().getMethod("setValue", Object.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
// IGNORED
|
||||
}
|
||||
return super.getWriteMethod();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWriteMethod(Method method) throws IntrospectionException {
|
||||
// IGNORED
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by the original author(s).
|
||||
*
|
||||
* 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.document.mongodb.mapping;
|
||||
|
||||
import org.springframework.data.document.mongodb.MongoTemplate;
|
||||
import org.springframework.data.mapping.model.AbstractMappingContext;
|
||||
import org.springframework.data.mapping.model.MappingConfigurationStrategy;
|
||||
import org.springframework.data.mapping.model.MappingFactory;
|
||||
import org.springframework.data.mapping.model.PersistentEntity;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class MongoMappingContext extends AbstractMappingContext {
|
||||
|
||||
private MongoTemplate mongo;
|
||||
private MongoMappingFactory mappingFactory = null;
|
||||
private MappingConfigurationStrategy strategy = null;
|
||||
|
||||
public MongoMappingContext(MongoTemplate mongo) {
|
||||
this.mongo = mongo;
|
||||
this.mappingFactory = new MongoMappingFactory();
|
||||
strategy = new MongoMappingConfigurationStrategy(mongo, mappingFactory);
|
||||
}
|
||||
|
||||
public MongoMappingContext(MongoTemplate mongo, MongoMappingFactory mappingFactory) {
|
||||
this.mongo = mongo;
|
||||
this.mappingFactory = mappingFactory;
|
||||
strategy = new MongoMappingConfigurationStrategy(mongo, mappingFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PersistentEntity createPersistentEntity(Class javaClass) {
|
||||
MongoPersistentEntity entity = new MongoPersistentEntity(javaClass, this);
|
||||
return entity;
|
||||
}
|
||||
|
||||
public MappingConfigurationStrategy getMappingSyntaxStrategy() {
|
||||
return strategy;
|
||||
}
|
||||
|
||||
public MappingFactory getMappingFactory() {
|
||||
return mappingFactory;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by the original author(s).
|
||||
*
|
||||
* 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.document.mongodb.mapping;
|
||||
|
||||
import org.springframework.data.document.mongodb.MongoTemplate;
|
||||
import org.springframework.data.mapping.model.MappingFactory;
|
||||
import org.springframework.data.mapping.model.PersistentEntity;
|
||||
import org.springframework.data.mapping.model.PersistentProperty;
|
||||
import org.springframework.data.mapping.reflect.ClassPropertyFetcher;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class MongoMappingFactory extends MappingFactory<MongoCollection, MongoProperty> {
|
||||
|
||||
@Override
|
||||
public MongoCollection createMappedForm(PersistentEntity entity) {
|
||||
String name = entity.getJavaClass().getSimpleName().toLowerCase();
|
||||
return new MongoCollection(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoProperty createMappedForm(PersistentProperty property) {
|
||||
String name = property.getName();
|
||||
return new MongoProperty(name, false);
|
||||
}
|
||||
}
|
||||
@@ -16,28 +16,26 @@
|
||||
|
||||
package org.springframework.data.document.mongodb.mapping;
|
||||
|
||||
import org.springframework.data.mapping.model.AbstractPersistentEntity;
|
||||
import org.springframework.data.mapping.model.ClassMapping;
|
||||
import org.springframework.data.mapping.BasicPersistentEntity;
|
||||
import org.springframework.data.mapping.model.MappingContext;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class MongoPersistentEntity extends AbstractPersistentEntity {
|
||||
public class MongoPersistentEntity<T> extends BasicPersistentEntity {
|
||||
|
||||
protected String collection;
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public MongoPersistentEntity(Class<?> javaClass, MappingContext context) {
|
||||
super(javaClass, context);
|
||||
public MongoPersistentEntity(MappingContext mappingContext, Class<T> type) {
|
||||
super(mappingContext, type);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public ClassMapping<MongoCollection> getMapping() {
|
||||
return new MongoClassMapping(this, getMappingContext());
|
||||
public String getCollection() {
|
||||
return collection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
super.afterPropertiesSet();
|
||||
public void setCollection(String collection) {
|
||||
this.collection = collection;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by the original author(s).
|
||||
*
|
||||
* 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.document.mongodb.mapping;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class MongoProperty {
|
||||
|
||||
private String name;
|
||||
|
||||
public MongoProperty(String name, boolean indexed) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -100,7 +100,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
|
||||
|
||||
protected List<?> readCollection(Query query) {
|
||||
|
||||
MongoEntityInformation<?> metadata = method.getEntityMetadata();
|
||||
MongoEntityInformation<?, ?> metadata = method.getEntityInformation();
|
||||
|
||||
String collectionName = metadata.getCollectionName();
|
||||
return template.find(collectionName, query, metadata.getJavaType());
|
||||
@@ -155,7 +155,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
Object execute(Query query) {
|
||||
|
||||
MongoEntityInformation<?> metadata = method.getEntityMetadata();
|
||||
MongoEntityInformation<?, ?> metadata = method.getEntityInformation();
|
||||
int count = getCollectionCursor(metadata.getCollectionName(), query.getQueryObject()).count();
|
||||
|
||||
List<?> result = template.find(metadata.getCollectionName(), applyPagination(query, pageable),
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.document.mongodb.repository;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -30,7 +31,7 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
class MongoEntityInformation<T extends Object> extends AbstractEntityInformation<T> {
|
||||
class MongoEntityInformation<T extends Object, ID extends Serializable> extends AbstractEntityInformation<T, ID> {
|
||||
|
||||
private static final List<String> FIELD_NAMES = Arrays.asList("ID", "id", "_id");
|
||||
private Field field;
|
||||
@@ -64,11 +65,22 @@ class MongoEntityInformation<T extends Object> extends AbstractEntityInformation
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the name of the collection the entity shall be persisted to.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getCollectionName() {
|
||||
|
||||
return StringUtils.uncapitalize(getJavaType().getSimpleName());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the attribute that the id will be persisted to.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getIdAttribute() {
|
||||
|
||||
return "_id";
|
||||
@@ -82,8 +94,22 @@ class MongoEntityInformation<T extends Object> extends AbstractEntityInformation
|
||||
* org.springframework.data.repository.support.IdAware#getId(java.lang.Object
|
||||
* )
|
||||
*/
|
||||
public Object getId(Object entity) {
|
||||
@SuppressWarnings("unchecked")
|
||||
public ID getId(Object entity) {
|
||||
|
||||
return ReflectionUtils.getField(field, entity);
|
||||
return (ID) ReflectionUtils.getField(field, entity);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.support.EntityInformation#getIdType()
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Class<ID> getIdType() {
|
||||
|
||||
return (Class<ID>) field.getType();
|
||||
}
|
||||
}
|
||||
@@ -15,85 +15,86 @@
|
||||
*/
|
||||
package org.springframework.data.document.mongodb.repository;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.data.repository.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
*
|
||||
* TODO - Extract methods for {@link #getAnnotatedQuery()} into superclass as it is currently copied from Spring Data
|
||||
* JPA
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
class MongoQueryMethod extends QueryMethod {
|
||||
|
||||
private final Method method;
|
||||
private final MongoEntityInformation<?> entityInformation;
|
||||
|
||||
/**
|
||||
* Creates a new {@link MongoQueryMethod} from the given {@link Method}.
|
||||
*
|
||||
* @param method
|
||||
*/
|
||||
public MongoQueryMethod(Method method, Class<?> domainClass) {
|
||||
super(method);
|
||||
this.method = method;
|
||||
this.entityInformation = new MongoEntityInformation(ClassUtils.getReturnedDomainClass(method));
|
||||
}
|
||||
private final Method method;
|
||||
private final MongoEntityInformation<?, ?> entityInformation;
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the method has an annotated query.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean hasAnnotatedQuery() {
|
||||
return getAnnotatedQuery() != null;
|
||||
}
|
||||
/**
|
||||
* Creates a new {@link MongoQueryMethod} from the given {@link Method}.
|
||||
*
|
||||
* @param method
|
||||
*/
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public MongoQueryMethod(Method method, Class<?> domainClass) {
|
||||
super(method);
|
||||
this.method = method;
|
||||
this.entityInformation = new MongoEntityInformation(ClassUtils.getReturnedDomainClass(method));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the query string declared in a {@link Query} annotation or {@literal null} if neither the annotation
|
||||
* found nor the attribute was specified.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getAnnotatedQuery() {
|
||||
|
||||
String query = (String) AnnotationUtils.getValue(getQueryAnnotation());
|
||||
return StringUtils.hasText(query) ? query : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the field specification to be used for the query.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getFieldSpecification() {
|
||||
|
||||
String value = (String) AnnotationUtils.getValue(getQueryAnnotation(), "fields");
|
||||
return StringUtils.hasText(value) ? value : null;
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.QueryMethod#getEntityMetadata()
|
||||
*/
|
||||
@Override
|
||||
public MongoEntityInformation<?> getEntityMetadata() {
|
||||
|
||||
return entityInformation;
|
||||
}
|
||||
/**
|
||||
* Returns whether the method has an annotated query.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean hasAnnotatedQuery() {
|
||||
return getAnnotatedQuery() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Query} annotation that is applied to the method or {@code null} if none available.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private Query getQueryAnnotation() {
|
||||
/**
|
||||
* Returns the query string declared in a {@link Query} annotation or {@literal null} if neither the annotation
|
||||
* found nor the attribute was specified.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getAnnotatedQuery() {
|
||||
|
||||
return method.getAnnotation(Query.class);
|
||||
}
|
||||
String query = (String) AnnotationUtils.getValue(getQueryAnnotation());
|
||||
return StringUtils.hasText(query) ? query : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the field specification to be used for the query.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getFieldSpecification() {
|
||||
|
||||
String value = (String) AnnotationUtils.getValue(getQueryAnnotation(), "fields");
|
||||
return StringUtils.hasText(value) ? value : null;
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.QueryMethod#getEntityMetadata()
|
||||
*/
|
||||
@Override
|
||||
public MongoEntityInformation<?, ?> getEntityInformation() {
|
||||
|
||||
return entityInformation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Query} annotation that is applied to the method or {@code null} if none available.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private Query getQueryAnnotation() {
|
||||
|
||||
return method.getAnnotation(Query.class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,16 +16,31 @@
|
||||
package org.springframework.data.document.mongodb.repository;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
|
||||
|
||||
/**
|
||||
* Mongo specific {@link org.springframework.data.repository.Repository} interface.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface MongoRepository<T, ID extends Serializable> extends
|
||||
PagingAndSortingRepository<T, ID> {
|
||||
public interface MongoRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.repository.Repository#findAll()
|
||||
*/
|
||||
@Override
|
||||
List<T> findAll();
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.repository.PagingAndSortingRepository#findAll(org.springframework.data.domain.Sort)
|
||||
*/
|
||||
@Override
|
||||
List<T> findAll(Sort sort);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class MongoRepositoryFactoryBean extends RepositoryFactoryBeanSupport<MongoRepository<?, ?>> {
|
||||
public class MongoRepositoryFactoryBean<T extends MongoRepository<S, ID>, S, ID extends Serializable> extends RepositoryFactoryBeanSupport<T, S, ID> {
|
||||
|
||||
private MongoTemplate template;
|
||||
|
||||
@@ -101,14 +101,7 @@ public class MongoRepositoryFactoryBean extends RepositoryFactoryBeanSupport<Mon
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Object getTargetRepository(RepositoryMetadata metadata) {
|
||||
|
||||
MongoEntityInformation<Object> info = new MongoEntityInformation<Object>(
|
||||
(Class<Object>) metadata.getDomainClass());
|
||||
return new SimpleMongoRepository<Object, Serializable>(info, template);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
@@ -162,6 +155,31 @@ public class MongoRepositoryFactoryBean extends RepositoryFactoryBeanSupport<Mon
|
||||
|
||||
super.validate(metadata, customImplementation);
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.RepositoryFactorySupport#getEntityInformation(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T, ID extends Serializable> MongoEntityInformation<T, ID> getEntityInformation(
|
||||
Class<T> domainClass) {
|
||||
|
||||
return new MongoEntityInformation<T, ID>(domainClass);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.RepositoryFactorySupport#getTargetRepository(org.springframework.data.repository.support.RepositoryMetadata)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
protected Object getTargetRepository(RepositoryMetadata metadata) {
|
||||
|
||||
MongoEntityInformation<?, ?> info = getEntityInformation(
|
||||
metadata.getDomainClass());
|
||||
return new SimpleMongoRepository(info, template);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,7 +217,7 @@ public class MongoRepositoryFactoryBean extends RepositoryFactoryBeanSupport<Mon
|
||||
index.on(property, order);
|
||||
}
|
||||
|
||||
MongoEntityInformation<?> metadata = query.getQueryMethod().getEntityMetadata();
|
||||
MongoEntityInformation<?, ?> metadata = query.getQueryMethod().getEntityInformation();
|
||||
operations.ensureIndex(metadata.getCollectionName(), index);
|
||||
LOG.debug(String.format("Created index %s!", index.toString()));
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public class PartTreeMongoQuery extends AbstractMongoQuery {
|
||||
public PartTreeMongoQuery(MongoQueryMethod method, MongoTemplate template) {
|
||||
|
||||
super(method, template);
|
||||
this.tree = new PartTree(method.getName(), method.getEntityMetadata().getJavaType());
|
||||
this.tree = new PartTree(method.getName(), method.getEntityInformation().getJavaType());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -41,7 +41,7 @@ import static org.springframework.data.document.mongodb.query.Criteria.where;
|
||||
public class SimpleMongoRepository<T, ID extends Serializable> implements PagingAndSortingRepository<T, ID> {
|
||||
|
||||
private final MongoTemplate template;
|
||||
private final MongoEntityInformation<T> entityInformation;
|
||||
private final MongoEntityInformation<T, ID> entityInformation;
|
||||
|
||||
/**
|
||||
* Creates a ew {@link SimpleMongoRepository} for the given {@link MongoInformation} and {@link MongoTemplate}.
|
||||
@@ -49,7 +49,7 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements Paging
|
||||
* @param metadata
|
||||
* @param template
|
||||
*/
|
||||
public SimpleMongoRepository(MongoEntityInformation<T> metadata, MongoTemplate template) {
|
||||
public SimpleMongoRepository(MongoEntityInformation<T, ID> metadata, MongoTemplate template) {
|
||||
|
||||
Assert.notNull(template);
|
||||
Assert.notNull(metadata);
|
||||
@@ -105,12 +105,15 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements Paging
|
||||
return where(entityInformation.getIdAttribute()).is(objectId);
|
||||
}
|
||||
|
||||
public T findOne(ID id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.repository.Repository#exists(java.io.Serializable )
|
||||
*/
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.repository.Repository#exists(java.io.Serializable )
|
||||
*/
|
||||
public boolean exists(ID id) {
|
||||
|
||||
return findById(id) != null;
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
package org.springframework.data.mapping.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author J. Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value = {ElementType.FIELD})
|
||||
public @interface Id {
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package org.springframework.data.mapping.annotation;
|
||||
|
||||
/**
|
||||
* @author J. Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public enum IdentifiedBy {
|
||||
DEFAULT,
|
||||
ANNOTATION,
|
||||
PROPERTY,
|
||||
VALUE,
|
||||
PARENT
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package org.springframework.data.mapping.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author J. Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value = {ElementType.FIELD})
|
||||
public @interface ManyToMany {
|
||||
|
||||
Class<?> targetClass() default Object.class;
|
||||
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package org.springframework.data.mapping.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author J. Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value = {ElementType.FIELD})
|
||||
public @interface OneToMany {
|
||||
|
||||
Class<?> targetClass() default Object.class;
|
||||
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package org.springframework.data.mapping.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author J. Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value = {ElementType.FIELD})
|
||||
public @interface OneToOne {
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by the original author(s).
|
||||
*
|
||||
* 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.mapping.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.CONSTRUCTOR)
|
||||
public @interface PersistenceConstructor {
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package org.springframework.data.mapping.annotation;
|
||||
|
||||
/**
|
||||
* @author J. Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public enum PersistenceStrategy {
|
||||
DEFAULT,
|
||||
KEY_VALUE,
|
||||
DOCUMENT,
|
||||
SERIALIZED
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by the original author(s).
|
||||
*
|
||||
* 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.mapping.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author J. Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value = {ElementType.TYPE, ElementType.FIELD})
|
||||
public @interface Persistent {
|
||||
|
||||
PersistenceStrategy strategy() default PersistenceStrategy.DOCUMENT;
|
||||
|
||||
IdentifiedBy identifiedBy() default IdentifiedBy.DEFAULT;
|
||||
|
||||
String id() default "id";
|
||||
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package org.springframework.data.mapping.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author J. Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface Transient {
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/* Copyright (C) 2010 SpringSource
|
||||
*
|
||||
* 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.mapping.model;
|
||||
|
||||
/**
|
||||
* Abstract implementation of the ClassMapping interface
|
||||
*
|
||||
* @author Graeme Rocher
|
||||
* @since 1.0
|
||||
*/
|
||||
public abstract class AbstractClassMapping<T> implements ClassMapping{
|
||||
protected PersistentEntity entity;
|
||||
protected MappingContext context;
|
||||
|
||||
public AbstractClassMapping(PersistentEntity entity, MappingContext context) {
|
||||
super();
|
||||
this.entity = entity;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public PersistentEntity getEntity() {
|
||||
return this.entity;
|
||||
}
|
||||
|
||||
public abstract T getMappedForm();
|
||||
|
||||
public IdentityMapping getIdentifier() {
|
||||
return context.getMappingSyntaxStrategy().getDefaultIdentityMapping(this);
|
||||
}
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
/* Copyright (C) 2010 SpringSource
|
||||
*
|
||||
* 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.mapping.model;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.converter.ConverterRegistry;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.validation.Validator;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
/**
|
||||
* Abstract implementation of the MappingContext interface
|
||||
*
|
||||
* @author Graeme Rocher
|
||||
* @since 1.0
|
||||
*/
|
||||
public abstract class AbstractMappingContext implements MappingContext {
|
||||
|
||||
protected Collection<PersistentEntity> persistentEntities = new ConcurrentLinkedQueue<PersistentEntity>();
|
||||
protected Map<String, PersistentEntity> persistentEntitiesByName = new ConcurrentHashMap<String, PersistentEntity>();
|
||||
protected Map<PersistentEntity, Map<String, PersistentEntity>> persistentEntitiesByDiscriminator = new ConcurrentHashMap<PersistentEntity, Map<String, PersistentEntity>>();
|
||||
protected Map<PersistentEntity, Validator> entityValidators = new ConcurrentHashMap<PersistentEntity, Validator>();
|
||||
protected Collection<Listener> eventListeners = new ConcurrentLinkedQueue<Listener>();
|
||||
protected GenericConversionService conversionService = new GenericConversionService();
|
||||
|
||||
public ConversionService getConversionService() {
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
public ConverterRegistry getConverterRegistry() {
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
public void addMappingContextListener(Listener listener) {
|
||||
if (listener != null)
|
||||
eventListeners.add(listener);
|
||||
}
|
||||
|
||||
public void addTypeConverter(Converter converter) {
|
||||
conversionService.addConverter(converter);
|
||||
}
|
||||
|
||||
public Validator getEntityValidator(PersistentEntity entity) {
|
||||
if (entity != null) {
|
||||
return entityValidators.get(entity);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void addEntityValidator(PersistentEntity entity, Validator validator) {
|
||||
if (entity != null && validator != null) {
|
||||
entityValidators.put(entity, validator);
|
||||
}
|
||||
}
|
||||
|
||||
public PersistentEntity addExternalPersistentEntity(Class javaClass) {
|
||||
if (javaClass == null) throw new IllegalArgumentException("PersistentEntity class cannot be null");
|
||||
|
||||
PersistentEntity entity = persistentEntitiesByName.get(javaClass.getName());
|
||||
|
||||
if (entity == null) {
|
||||
entity = addPersistentEntityInternal(javaClass, true);
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
public final PersistentEntity addPersistentEntity(Class javaClass) {
|
||||
if (javaClass == null) throw new IllegalArgumentException("PersistentEntity class cannot be null");
|
||||
|
||||
PersistentEntity entity = persistentEntitiesByName.get(javaClass.getName());
|
||||
|
||||
if (entity == null) {
|
||||
entity = addPersistentEntityInternal(javaClass, false);
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
private PersistentEntity addPersistentEntityInternal(Class javaClass, boolean isExternal) {
|
||||
PersistentEntity entity;
|
||||
entity = createPersistentEntity(javaClass);
|
||||
entity.setExternal(isExternal);
|
||||
|
||||
persistentEntities.remove(entity);
|
||||
persistentEntities.add(entity);
|
||||
persistentEntitiesByName.put(entity.getName(), entity);
|
||||
try {
|
||||
entity.afterPropertiesSet();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(String.format("Error initializing PersistentEntity: %s", entity), e);
|
||||
}
|
||||
if (!entity.isRoot()) {
|
||||
PersistentEntity root = entity.getRootEntity();
|
||||
Map<String, PersistentEntity> children = persistentEntitiesByDiscriminator.get(root);
|
||||
if (children == null) {
|
||||
children = new ConcurrentHashMap<String, PersistentEntity>();
|
||||
persistentEntitiesByDiscriminator.put(root, children);
|
||||
}
|
||||
children.put(entity.getDiscriminator(), entity);
|
||||
}
|
||||
for (Listener eventListener : eventListeners) {
|
||||
eventListener.persistentEntityAdded(entity);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
public PersistentEntity getChildEntityByDiscriminator(PersistentEntity root, String discriminator) {
|
||||
final Map<String, PersistentEntity> children = persistentEntitiesByDiscriminator.get(root);
|
||||
if (children != null) {
|
||||
return children.get(discriminator);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected abstract PersistentEntity createPersistentEntity(Class javaClass);
|
||||
|
||||
public Collection<PersistentEntity> getPersistentEntities() {
|
||||
return persistentEntities;
|
||||
}
|
||||
|
||||
public boolean isPersistentEntity(Class type) {
|
||||
return type != null && getPersistentEntity(type.getName()) != null;
|
||||
|
||||
}
|
||||
|
||||
public boolean isPersistentEntity(Object value) {
|
||||
return value != null && isPersistentEntity(value.getClass());
|
||||
}
|
||||
|
||||
public PersistentEntity getPersistentEntity(String name) {
|
||||
final int proxyIndicator = name.indexOf("_$$_");
|
||||
if (proxyIndicator > -1) {
|
||||
name = name.substring(0, proxyIndicator);
|
||||
}
|
||||
|
||||
return persistentEntitiesByName.get(name);
|
||||
}
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
/* Copyright 2004-2005 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.mapping.model;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.data.mapping.model.types.Association;
|
||||
import org.springframework.data.mapping.model.types.OneToMany;
|
||||
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Abstract implementation to be subclasses on a per datastore basis
|
||||
*
|
||||
* @author Graeme Rocher
|
||||
* @since 1.0
|
||||
*/
|
||||
public abstract class AbstractPersistentEntity<T> implements PersistentEntity<T> {
|
||||
|
||||
protected Class<T> javaClass;
|
||||
protected List<PersistentProperty> persistentProperties;
|
||||
protected List<Association> associations;
|
||||
protected Map<String, PersistentProperty> propertiesByName = new HashMap<String, PersistentProperty>();
|
||||
protected MappingContext context;
|
||||
protected PersistentProperty identity;
|
||||
protected List<String> persistentPropertyNames;
|
||||
private String decapitalizedName;
|
||||
protected Set owners;
|
||||
private PersistentEntity parentEntity = null;
|
||||
private boolean external;
|
||||
|
||||
public AbstractPersistentEntity(Class<T> javaClass, MappingContext context) {
|
||||
if (javaClass == null) throw new IllegalArgumentException("The argument [javaClass] cannot be null");
|
||||
this.javaClass = javaClass;
|
||||
this.context = context;
|
||||
this.decapitalizedName = Introspector.decapitalize(javaClass.getSimpleName());
|
||||
}
|
||||
|
||||
public boolean isExternal() {
|
||||
return external;
|
||||
}
|
||||
|
||||
|
||||
public void setExternal(boolean external) {
|
||||
this.external = external;
|
||||
}
|
||||
|
||||
public MappingContext getMappingContext() {
|
||||
return this.context;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
this.identity = context.getMappingSyntaxStrategy().getIdentity(javaClass, context);
|
||||
this.owners = context.getMappingSyntaxStrategy().getOwningEntities(javaClass, context);
|
||||
this.persistentProperties = context.getMappingSyntaxStrategy().getPersistentProperties(javaClass, context);
|
||||
persistentPropertyNames = new ArrayList<String>();
|
||||
associations = new ArrayList();
|
||||
for (PersistentProperty persistentProperty : persistentProperties) {
|
||||
if (!(persistentProperty instanceof OneToMany))
|
||||
persistentPropertyNames.add(persistentProperty.getName());
|
||||
if (persistentProperty instanceof Association) {
|
||||
associations.add((Association) persistentProperty);
|
||||
}
|
||||
}
|
||||
for (PersistentProperty persistentProperty : persistentProperties) {
|
||||
propertiesByName.put(persistentProperty.getName(), persistentProperty);
|
||||
}
|
||||
|
||||
Class<?> superClass = javaClass.getSuperclass();
|
||||
if (superClass != null
|
||||
&& !superClass.equals(Object.class)
|
||||
&& !Modifier.isAbstract(superClass.getModifiers())) {
|
||||
this.parentEntity = context.addPersistentEntity(superClass);
|
||||
}
|
||||
|
||||
|
||||
getMapping().getMappedForm(); // initialize mapping
|
||||
}
|
||||
|
||||
public boolean hasProperty(String name, Class type) {
|
||||
final PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(getJavaClass(), name);
|
||||
return pd != null && pd.getPropertyType().equals(type);
|
||||
}
|
||||
|
||||
public boolean isIdentityName(String propertyName) {
|
||||
return getIdentity().getName().equals(propertyName);
|
||||
}
|
||||
|
||||
public PersistentEntity getParentEntity() {
|
||||
return parentEntity;
|
||||
}
|
||||
|
||||
public String getDiscriminator() {
|
||||
return getJavaClass().getSimpleName();
|
||||
}
|
||||
|
||||
public PersistentEntity getRootEntity() {
|
||||
PersistentEntity root = this;
|
||||
PersistentEntity parent = getParentEntity();
|
||||
while (parent != null) {
|
||||
root = parent;
|
||||
parent = parent.getParentEntity();
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
public boolean isRoot() {
|
||||
return getParentEntity() == null;
|
||||
}
|
||||
|
||||
public boolean isOwningEntity(PersistentEntity owner) {
|
||||
return owner != null && owners.contains(owner.getJavaClass());
|
||||
}
|
||||
|
||||
public String getDecapitalizedName() {
|
||||
return this.decapitalizedName;
|
||||
}
|
||||
|
||||
public List<String> getPersistentPropertyNames() {
|
||||
return this.persistentPropertyNames;
|
||||
}
|
||||
|
||||
public T newInstance() {
|
||||
try {
|
||||
return getJavaClass().newInstance();
|
||||
} catch (InstantiationException e) {
|
||||
throw new MappingInstantiationException("Unable to create entity of type [" + getJavaClass() + "]: " + e.getMessage(), e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new MappingInstantiationException("Unable to create entity of type [" + getJavaClass() + "]: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return javaClass.getName();
|
||||
}
|
||||
|
||||
public PersistentProperty getIdentity() {
|
||||
return this.identity;
|
||||
}
|
||||
|
||||
public Class<T> getJavaClass() {
|
||||
return this.javaClass;
|
||||
}
|
||||
|
||||
public boolean isInstance(Object obj) {
|
||||
return getJavaClass().isInstance(obj);
|
||||
}
|
||||
|
||||
public List<PersistentProperty> getPersistentProperties() {
|
||||
return persistentProperties;
|
||||
}
|
||||
|
||||
public List<Association> getAssociations() {
|
||||
return associations;
|
||||
}
|
||||
|
||||
public PersistentProperty getPropertyByName(String name) {
|
||||
return propertiesByName.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return javaClass.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || !(o instanceof PersistentEntity)) return false;
|
||||
if (this == o) return true;
|
||||
|
||||
PersistentEntity other = (PersistentEntity) o;
|
||||
return javaClass.equals(other.getJavaClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PersistentEntity[" + javaClass.getName() + "]";
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/* Copyright 2004-2005 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.mapping.model;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
|
||||
/**
|
||||
* Abstract implementation of the PersistentProperty interface that
|
||||
* uses the PropertyDescriptor instance to establish name and type
|
||||
*
|
||||
* @author Graeme Rocher
|
||||
* @since 1.0
|
||||
*/
|
||||
public abstract class AbstractPersistentProperty implements PersistentProperty {
|
||||
protected PersistentEntity owner;
|
||||
protected MappingContext context;
|
||||
protected String name;
|
||||
protected Class type;
|
||||
private boolean nullable = false;
|
||||
|
||||
public AbstractPersistentProperty(PersistentEntity owner, MappingContext context, PropertyDescriptor descriptor) {
|
||||
this.owner = owner;
|
||||
this.context = context;
|
||||
this.name = descriptor.getName();
|
||||
this.type = descriptor.getPropertyType();
|
||||
}
|
||||
|
||||
public AbstractPersistentProperty(PersistentEntity owner, MappingContext context, String name, Class type) {
|
||||
this.owner = owner;
|
||||
this.context = context;
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getCapitilizedName() {
|
||||
String name = getName();
|
||||
return name.substring(0, 1).toUpperCase() + name.substring(1);
|
||||
}
|
||||
|
||||
public Class getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public PersistentEntity getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getName() + ":" + getType().getName();
|
||||
}
|
||||
|
||||
public boolean isNullable() {
|
||||
return nullable;
|
||||
}
|
||||
|
||||
public void setNullable(boolean nullable) {
|
||||
this.nullable = nullable;
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package org.springframework.data.mapping.model;
|
||||
|
||||
/**
|
||||
* A class mapping is a mapping between a class and some external
|
||||
* form such as a table, column family, or document (depending on the underlying data store)
|
||||
*
|
||||
* @author Graeme Rocher
|
||||
* @since 1.0
|
||||
*/
|
||||
public interface ClassMapping<T> {
|
||||
/**
|
||||
* Obtains the PersistentEntity for this class mapping
|
||||
*
|
||||
* @return The PersistentEntity
|
||||
*/
|
||||
PersistentEntity getEntity();
|
||||
|
||||
/**
|
||||
* Returns the mapped form of the class such as a Table, a Key Space, Document etc.
|
||||
*
|
||||
* @return The mapped representation
|
||||
*/
|
||||
T getMappedForm();
|
||||
|
||||
/**
|
||||
* Returns details of the identifier used for this class
|
||||
*
|
||||
* @return The Identity
|
||||
*/
|
||||
IdentityMapping getIdentifier();
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package org.springframework.data.mapping.model;
|
||||
|
||||
/**
|
||||
* Exception thrown when something goes wrong configuring a datastore
|
||||
*
|
||||
* @author Graeme Rocher
|
||||
* @since 1.0
|
||||
*/
|
||||
public class DatastoreConfigurationException extends RuntimeException{
|
||||
|
||||
public DatastoreConfigurationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DatastoreConfigurationException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/* Copyright (C) 2010 SpringSource
|
||||
*
|
||||
* 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.mapping.model;
|
||||
|
||||
/**
|
||||
* @author Graeme Rocher
|
||||
* @since 1.0
|
||||
*/
|
||||
public interface IdentityMapping extends PropertyMapping{
|
||||
|
||||
/**
|
||||
* The identifier property name(s) as an array. Usually there is just one identifier
|
||||
* name, however in the case of a composite or natural identifier there
|
||||
* may be serveral names
|
||||
*
|
||||
* @return A String[] of identifier names that make up the key
|
||||
*/
|
||||
String[] getIdentifierName();
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/* Copyright (C) 2010 SpringSource
|
||||
*
|
||||
* 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.mapping.model;
|
||||
|
||||
/**
|
||||
* Thrown when an error occurs reading the mapping between object and datastore
|
||||
*
|
||||
* @author Graeme Rocher
|
||||
* @since 1.0
|
||||
*/
|
||||
public class IllegalMappingException extends RuntimeException {
|
||||
|
||||
public IllegalMappingException(String s, Throwable throwable) {
|
||||
super(s, throwable);
|
||||
}
|
||||
|
||||
public IllegalMappingException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package org.springframework.data.mapping.model;
|
||||
|
||||
import org.springframework.data.document.mongodb.mapping.MappingException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* <p>This interface defines a strategy for reading how
|
||||
* persistent properties are defined in a persistent entity.</p>
|
||||
* <p/>
|
||||
* <p>Subclasses can implement this interface in order to provide
|
||||
* a different mechanism for mapping entities such as annotations or XML.</p>
|
||||
*
|
||||
* @author Graeme Rocher
|
||||
* @since 1.0
|
||||
*/
|
||||
public interface MappingConfigurationStrategy {
|
||||
|
||||
/**
|
||||
* Tests whether the given class is a persistent entity
|
||||
*
|
||||
* @param javaClass The java class
|
||||
* @return true if it is a persistent entity
|
||||
*/
|
||||
<T> boolean isPersistentEntity(Class<T> javaClass);
|
||||
|
||||
/**
|
||||
* @see #getPersistentProperties(Class, MappingContext, ClassMapping)
|
||||
*/
|
||||
<T> List<PersistentProperty> getPersistentProperties(Class<T> javaClass, MappingContext context);
|
||||
|
||||
/**
|
||||
* Obtains a List of PersistentProperty instances for the given Mapped class
|
||||
*
|
||||
* @param javaClass The Java class
|
||||
* @param context The MappingContext instance
|
||||
* @param mapping The mapping for this class
|
||||
* @return The PersistentProperty instances
|
||||
*/
|
||||
<T> List<PersistentProperty> getPersistentProperties(Class<T> javaClass, MappingContext context, ClassMapping mapping);
|
||||
|
||||
/**
|
||||
* Obtains the identity of a persistent entity
|
||||
*
|
||||
* @param javaClass The Java class
|
||||
* @param context The MappingContext
|
||||
* @return A PersistentProperty instance
|
||||
*/
|
||||
<T> PersistentProperty getIdentity(Class<T> javaClass, MappingContext context);
|
||||
|
||||
/**
|
||||
* Obtains the default manner in which identifiers are mapped. In GORM
|
||||
* this is just using a property called 'id', but in other frameworks this
|
||||
* may differ. For example JPA expects an annotated @Id property
|
||||
*
|
||||
* @param classMapping The ClassMapping instance
|
||||
* @return The default identifier mapping
|
||||
*/
|
||||
IdentityMapping getDefaultIdentityMapping(ClassMapping classMapping) throws MappingException;
|
||||
|
||||
/**
|
||||
* Returns a set of entities that "own" the given entity. Ownership
|
||||
* dictates default cascade strategies. So if entity A owns entity B
|
||||
* then saves, updates and deletes will cascade from A to B
|
||||
*
|
||||
* @param javaClass The Java class
|
||||
* @param context The MappingContext
|
||||
* @return A Set of owning classes
|
||||
*/
|
||||
<T> Set getOwningEntities(Class<T> javaClass, MappingContext context);
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
/* Copyright 2004-2005 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.mapping.model;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.converter.ConverterRegistry;
|
||||
import org.springframework.validation.Validator;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* <p>This interface defines the overall context including all known
|
||||
* PersistentEntity instances and methods to obtain instances on demand</p>
|
||||
* <p/>
|
||||
* <p>This interface is used internally to establish associations
|
||||
* between entities and also at runtime to obtain entities by name</p>
|
||||
* <p/>
|
||||
* <p>The generic type parameters T & R are used to specify the
|
||||
* mapped form of a class (example Table) and property (example Column) respectively.</p>
|
||||
* <p/>
|
||||
*
|
||||
* @author Graeme Rocher
|
||||
* @since 1.0
|
||||
*/
|
||||
public interface MappingContext {
|
||||
|
||||
|
||||
/**
|
||||
* Obtains a list of PersistentEntity instances
|
||||
*
|
||||
* @return A list of PersistentEntity instances
|
||||
*/
|
||||
Collection<PersistentEntity> getPersistentEntities();
|
||||
|
||||
/**
|
||||
* Obtains a PersistentEntity by name
|
||||
*
|
||||
* @param name The name of the entity
|
||||
* @return The entity or null
|
||||
*/
|
||||
PersistentEntity getPersistentEntity(String name);
|
||||
|
||||
/**
|
||||
* Obtains a child of the given root entity using the given discriminator
|
||||
*
|
||||
* @param root The root entity
|
||||
* @param discriminator The discriminator
|
||||
* @return The child entity or null if non exists
|
||||
*/
|
||||
PersistentEntity getChildEntityByDiscriminator(PersistentEntity root, String discriminator);
|
||||
|
||||
/**
|
||||
* Adds a PersistentEntity instance
|
||||
*
|
||||
* @param javaClass The Java class representing the entity
|
||||
* @return The PersistentEntity instance
|
||||
*/
|
||||
PersistentEntity addPersistentEntity(Class javaClass);
|
||||
|
||||
/**
|
||||
* Adds a persistent entity that is not mapped by this MappingContext instance.
|
||||
* Used for cross store persistence
|
||||
*
|
||||
* @param javaClass The Java class
|
||||
* @return The persistent entity
|
||||
*/
|
||||
PersistentEntity addExternalPersistentEntity(Class javaClass);
|
||||
|
||||
/**
|
||||
* Adds a validator to be used by the entity for validation
|
||||
*
|
||||
* @param entity The PersistentEntity
|
||||
* @param validator The validator
|
||||
*/
|
||||
void addEntityValidator(PersistentEntity entity, Validator validator);
|
||||
|
||||
/**
|
||||
* Add a converter used to convert property values to and from the datastore
|
||||
*
|
||||
* @param converter The converter to add
|
||||
*/
|
||||
void addTypeConverter(Converter converter);
|
||||
|
||||
/**
|
||||
* Obtains the ConversionService instance to use for type conversion
|
||||
*
|
||||
* @return The conversion service instance
|
||||
*/
|
||||
ConversionService getConversionService();
|
||||
|
||||
/**
|
||||
* Obtains the converter registry
|
||||
*
|
||||
* @return The converter registry used for type conversion
|
||||
*/
|
||||
ConverterRegistry getConverterRegistry();
|
||||
|
||||
/**
|
||||
* Obtains a validator for the given entity
|
||||
*
|
||||
* @param entity The entity
|
||||
* @return A validator or null if none exists for the given entity
|
||||
*/
|
||||
Validator getEntityValidator(PersistentEntity entity);
|
||||
|
||||
/**
|
||||
* Returns the syntax reader used to interpret the entity
|
||||
* mapping syntax
|
||||
*
|
||||
* @return The SyntaxReader
|
||||
*/
|
||||
MappingConfigurationStrategy getMappingSyntaxStrategy();
|
||||
|
||||
/**
|
||||
* Obtains the MappingFactory instance
|
||||
*
|
||||
* @return The mapping factory instance
|
||||
*/
|
||||
MappingFactory getMappingFactory();
|
||||
|
||||
/**
|
||||
* Returns whether the specified class is a persistent entity
|
||||
*
|
||||
* @param type The type to check
|
||||
* @return True if it is
|
||||
*/
|
||||
boolean isPersistentEntity(Class<?> type);
|
||||
|
||||
/**
|
||||
* Returns whether the specified value is a persistent entity
|
||||
*
|
||||
* @param value The value to check
|
||||
* @return True if it is
|
||||
*/
|
||||
boolean isPersistentEntity(Object value);
|
||||
|
||||
/**
|
||||
* Adds a new mapping context listener instance
|
||||
*
|
||||
* @param listener The listener
|
||||
*/
|
||||
void addMappingContextListener(Listener listener);
|
||||
|
||||
/**
|
||||
* Implementors can register for events when the mapping context changes
|
||||
*/
|
||||
public static interface Listener {
|
||||
|
||||
/**
|
||||
* Fired when a new entity is added
|
||||
*
|
||||
* @param entity The entity
|
||||
*/
|
||||
void persistentEntityAdded(PersistentEntity entity);
|
||||
}
|
||||
}
|
||||
@@ -1,278 +0,0 @@
|
||||
/* Copyright 2004-2005 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.mapping.model;
|
||||
|
||||
import org.springframework.data.mapping.model.types.*;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.sql.Blob;
|
||||
import java.sql.Clob;
|
||||
import java.sql.Time;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* <p>An abstract factory for creating persistent property instances.</p>
|
||||
* <p/>
|
||||
* <p>Subclasses should implement the createMappedForm method in order to
|
||||
* provide a mechanisms for representing the property in a form appropriate
|
||||
* for mapping to the underlying datastore. Example:</p>
|
||||
* <p/>
|
||||
* <pre>
|
||||
* <code>
|
||||
* class RelationalPropertyFactory<Column> extends PropertyFactory {
|
||||
* public Column createMappedForm(PersistentProperty mpp) {
|
||||
* return new Column(mpp)
|
||||
* }
|
||||
* }
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @author Graeme Rocher
|
||||
* @since 1.0
|
||||
*/
|
||||
public abstract class MappingFactory<R, T> {
|
||||
public static final Set<String> SIMPLE_TYPES;
|
||||
|
||||
static {
|
||||
Set<String> basics = new HashSet<String>();
|
||||
basics.add(boolean.class.getName());
|
||||
basics.add(long.class.getName());
|
||||
basics.add(short.class.getName());
|
||||
basics.add(int.class.getName());
|
||||
basics.add(byte.class.getName());
|
||||
basics.add(float.class.getName());
|
||||
basics.add(double.class.getName());
|
||||
basics.add(char.class.getName());
|
||||
basics.add(Boolean.class.getName());
|
||||
basics.add(Long.class.getName());
|
||||
basics.add(Short.class.getName());
|
||||
basics.add(Integer.class.getName());
|
||||
basics.add(Byte.class.getName());
|
||||
basics.add(Float.class.getName());
|
||||
basics.add(Double.class.getName());
|
||||
basics.add(Character.class.getName());
|
||||
basics.add(String.class.getName());
|
||||
basics.add(java.util.Date.class.getName());
|
||||
basics.add(Time.class.getName());
|
||||
basics.add(Timestamp.class.getName());
|
||||
basics.add(java.sql.Date.class.getName());
|
||||
basics.add(BigDecimal.class.getName());
|
||||
basics.add(BigInteger.class.getName());
|
||||
basics.add(Locale.class.getName());
|
||||
basics.add(Calendar.class.getName());
|
||||
basics.add(GregorianCalendar.class.getName());
|
||||
basics.add(java.util.Currency.class.getName());
|
||||
basics.add(TimeZone.class.getName());
|
||||
basics.add(Object.class.getName());
|
||||
basics.add(Class.class.getName());
|
||||
basics.add(byte[].class.getName());
|
||||
basics.add(Byte[].class.getName());
|
||||
basics.add(char[].class.getName());
|
||||
basics.add(Character[].class.getName());
|
||||
basics.add(Blob.class.getName());
|
||||
basics.add(Clob.class.getName());
|
||||
basics.add(Serializable.class.getName());
|
||||
basics.add(URI.class.getName());
|
||||
basics.add(URL.class.getName());
|
||||
|
||||
SIMPLE_TYPES = Collections.unmodifiableSet(basics);
|
||||
}
|
||||
|
||||
|
||||
public MappingFactory() {
|
||||
super();
|
||||
}
|
||||
|
||||
public static boolean isSimpleType(Class propType) {
|
||||
if (propType == null) return false;
|
||||
if (propType.isArray()) {
|
||||
return isSimpleType(propType.getComponentType());
|
||||
}
|
||||
final String typeName = propType.getName();
|
||||
return isSimpleType(typeName);
|
||||
}
|
||||
|
||||
public static boolean isSimpleType(final String typeName) {
|
||||
return SIMPLE_TYPES.contains(typeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the mapped form of a persistent entity
|
||||
*
|
||||
* @param entity The entity
|
||||
* @return The mapped form
|
||||
*/
|
||||
public abstract R createMappedForm(PersistentEntity entity);
|
||||
|
||||
/**
|
||||
* Creates the mapped form of a PersistentProperty instance
|
||||
*
|
||||
* @param mpp The PersistentProperty instance
|
||||
* @return The mapped form
|
||||
*/
|
||||
public abstract T createMappedForm(PersistentProperty mpp);
|
||||
|
||||
/**
|
||||
* Creates an identifier property
|
||||
*
|
||||
* @param owner The owner
|
||||
* @param context The context
|
||||
* @param pd The PropertyDescriptor
|
||||
* @return An Identity instance
|
||||
*/
|
||||
public Identity<T> createIdentity(PersistentEntity owner, MappingContext context, PropertyDescriptor pd) {
|
||||
return new Identity<T>(owner, context, pd) {
|
||||
public PropertyMapping<T> getMapping() {
|
||||
return createPropertyMapping(this, owner);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a simple property type used for mapping basic types such as String, long, integer etc.
|
||||
*
|
||||
* @param owner The owner
|
||||
* @param context The MappingContext
|
||||
* @param pd The PropertyDescriptor
|
||||
* @return A Simple property type
|
||||
*/
|
||||
public Simple<T> createSimple(PersistentEntity owner, MappingContext context, PropertyDescriptor pd) {
|
||||
return new Simple<T>(owner, context, pd) {
|
||||
public PropertyMapping<T> getMapping() {
|
||||
return createPropertyMapping(this, owner);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected PropertyMapping<T> createPropertyMapping(final PersistentProperty<T> property, final PersistentEntity owner) {
|
||||
return new PropertyMapping<T>() {
|
||||
public ClassMapping getClassMapping() {
|
||||
return owner.getMapping();
|
||||
}
|
||||
|
||||
public T getMappedForm() {
|
||||
return createMappedForm(property);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a one-to-one association type used for mapping a one-to-one association between entities
|
||||
*
|
||||
* @param entity The entity
|
||||
* @param context The context
|
||||
* @param property The property
|
||||
* @return The ToOne instance
|
||||
*/
|
||||
public ToOne createOneToOne(PersistentEntity entity, MappingContext context, PropertyDescriptor property) {
|
||||
return new OneToOne<T>(entity, context, property) {
|
||||
public PropertyMapping getMapping() {
|
||||
return createPropertyMapping(this, owner);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a many-to-one association type used for a mapping a many-to-one association between entities
|
||||
*
|
||||
* @param entity The entity
|
||||
* @param context The context
|
||||
* @param property The property
|
||||
* @return The ToOne instance
|
||||
*/
|
||||
public ToOne createManyToOne(PersistentEntity entity, MappingContext context, PropertyDescriptor property) {
|
||||
return new ManyToOne<T>(entity, context, property) {
|
||||
public PropertyMapping getMapping() {
|
||||
return createPropertyMapping(this, owner);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link OneToMany} type used to model a one-to-many association between entities
|
||||
*
|
||||
* @param entity The entity
|
||||
* @param context The context
|
||||
* @param property The property
|
||||
* @return The {@link OneToMany} instance
|
||||
*/
|
||||
public OneToMany createOneToMany(PersistentEntity entity, MappingContext context, PropertyDescriptor property) {
|
||||
return new OneToMany<T>(entity, context, property) {
|
||||
public PropertyMapping getMapping() {
|
||||
return createPropertyMapping(this, owner);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ManyToMany} type used to model a many-to-many association between entities
|
||||
*
|
||||
* @param entity The entity
|
||||
* @param context The context
|
||||
* @param property The property
|
||||
* @return The {@link ManyToMany} instance
|
||||
*/
|
||||
public ManyToMany createManyToMany(PersistentEntity entity, MappingContext context, PropertyDescriptor property) {
|
||||
return new ManyToMany<T>(entity, context, property) {
|
||||
public PropertyMapping getMapping() {
|
||||
return createPropertyMapping(this, owner);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an {@link Embedded} type used to model an embedded association (composition)
|
||||
*
|
||||
* @param entity The entity
|
||||
* @param context The context
|
||||
* @param property The property
|
||||
* @return The {@link Embedded} instance
|
||||
*/
|
||||
public Embedded createEmbedded(PersistentEntity entity,
|
||||
MappingContext context, PropertyDescriptor property) {
|
||||
return new Embedded<T>(entity, context, property) {
|
||||
public PropertyMapping getMapping() {
|
||||
return createPropertyMapping(this, owner);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Basic} collection type
|
||||
*
|
||||
* @param entity The entity
|
||||
* @param context The context
|
||||
* @param property The property
|
||||
* @return The Basic collection type
|
||||
*/
|
||||
public Basic createBasicCollection(PersistentEntity entity,
|
||||
MappingContext context, PropertyDescriptor property) {
|
||||
return new Basic(entity, context, property) {
|
||||
public PropertyMapping getMapping() {
|
||||
return createPropertyMapping(this, owner);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by the original author(s).
|
||||
*
|
||||
* 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.mapping.model;
|
||||
|
||||
/**
|
||||
* Created by IntelliJ IDEA.
|
||||
* User: jbrisbin
|
||||
* Date: 2/25/11
|
||||
* Time: 9:07 AM
|
||||
* To change this template use File | Settings | File Templates.
|
||||
*/
|
||||
public class MappingInstantiationException extends RuntimeException {
|
||||
public MappingInstantiationException(String s, Throwable throwable) {
|
||||
super(s, throwable);
|
||||
}
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
package org.springframework.data.mapping.model;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.data.mapping.model.types.Association;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a persistent entity
|
||||
*
|
||||
* @author Graeme Rocher
|
||||
* @since 1.0
|
||||
*/
|
||||
public interface PersistentEntity<T> extends InitializingBean {
|
||||
|
||||
/**
|
||||
* The entity name including any package prefix
|
||||
*
|
||||
* @return The entity name
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Whether this PersistentEntity is mapped using a different store. Used for cross store persistence
|
||||
*
|
||||
* @return True if this entity is externally mapped
|
||||
*/
|
||||
boolean isExternal();
|
||||
|
||||
/**
|
||||
* Whether this PersistentEntity is mapped using a different store. Used for cross store persistence
|
||||
*
|
||||
* @return True if this entity is externally mapped
|
||||
*/
|
||||
void setExternal(boolean external);
|
||||
|
||||
/**
|
||||
* Returns the identity of the instance
|
||||
*
|
||||
* @return The identity
|
||||
*/
|
||||
PersistentProperty getIdentity();
|
||||
|
||||
/**
|
||||
* A list of properties to be persisted
|
||||
*
|
||||
* @return A list of PersistentProperty instances
|
||||
*/
|
||||
List<PersistentProperty> getPersistentProperties();
|
||||
|
||||
/**
|
||||
* A list of the associations for this entity. This is typically
|
||||
* a subset of the list returned by {@link #getPersistentProperties()}
|
||||
*
|
||||
* @return A list of associations
|
||||
*/
|
||||
List<Association> getAssociations();
|
||||
|
||||
/**
|
||||
* Obtains a PersistentProperty instance by name
|
||||
*
|
||||
* @param name The name of the property
|
||||
* @return The PersistentProperty or null if it doesn't exist
|
||||
*/
|
||||
PersistentProperty getPropertyByName(String name);
|
||||
|
||||
/**
|
||||
* @return The underlying Java class for this entity
|
||||
*/
|
||||
Class<T> getJavaClass();
|
||||
|
||||
/**
|
||||
* Tests whether the given instance is an instance of this persistent entity
|
||||
*
|
||||
* @param obj The object
|
||||
* @return True if it is
|
||||
*/
|
||||
boolean isInstance(Object obj);
|
||||
|
||||
/**
|
||||
* Defines the mapping between this persistent entity
|
||||
* and an external form
|
||||
*
|
||||
* @return The ClassMapping instance
|
||||
*/
|
||||
<M> ClassMapping<M> getMapping();
|
||||
|
||||
/**
|
||||
* Constructs a new instance
|
||||
*
|
||||
* @return The new instnace
|
||||
*/
|
||||
T newInstance();
|
||||
|
||||
/**
|
||||
* A list of property names that a persistent
|
||||
*
|
||||
* @return A List of strings
|
||||
*/
|
||||
List<String> getPersistentPropertyNames();
|
||||
|
||||
/**
|
||||
* @return Returns the name of the class decapitalized form
|
||||
*/
|
||||
String getDecapitalizedName();
|
||||
|
||||
/**
|
||||
* Returns whether the specified entity asserts ownership over this
|
||||
* entity
|
||||
*
|
||||
* @param owner The owning entity
|
||||
* @return True if it does own this entity
|
||||
*/
|
||||
boolean isOwningEntity(PersistentEntity<?> owner);
|
||||
|
||||
/**
|
||||
* Returns the parent entity of this entity
|
||||
*
|
||||
* @return The ParentEntity instance
|
||||
*/
|
||||
PersistentEntity<?> getParentEntity();
|
||||
|
||||
/**
|
||||
* Obtains the root entity of an inheritance hierarchy
|
||||
*
|
||||
* @return The root entity
|
||||
*/
|
||||
PersistentEntity<?> getRootEntity();
|
||||
|
||||
/**
|
||||
* Whether this entity is a root entity
|
||||
*
|
||||
* @return True if it is a root entity
|
||||
*/
|
||||
boolean isRoot();
|
||||
|
||||
/**
|
||||
* The discriminator used when persisting subclasses of an inheritance hierarchy
|
||||
*
|
||||
* @return The discriminator
|
||||
*/
|
||||
String getDiscriminator();
|
||||
|
||||
/**
|
||||
* Obtains the MappingContext where this PersistentEntity is defined
|
||||
*
|
||||
* @return The MappingContext instance
|
||||
*/
|
||||
MappingContext getMappingContext();
|
||||
|
||||
/**
|
||||
* Checks whether an entity has a bean property of the given name and type
|
||||
*
|
||||
* @param name The name
|
||||
* @param type The type
|
||||
* @return True if it does
|
||||
*/
|
||||
boolean hasProperty(String name, Class<?> type);
|
||||
|
||||
/**
|
||||
* True if the given property is the identifier
|
||||
*
|
||||
* @param propertyName the property name
|
||||
* @return True if it is the identifier
|
||||
*/
|
||||
boolean isIdentityName(String propertyName);
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package org.springframework.data.mapping.model;
|
||||
|
||||
/**
|
||||
* @author Graeme Rocher
|
||||
* @since 1.0
|
||||
*/
|
||||
public interface PersistentProperty<T> {
|
||||
|
||||
/**
|
||||
* The name of the property
|
||||
*
|
||||
* @return The property name
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* The name with the first letter in upper case as per Java bean conventions
|
||||
*
|
||||
* @return The capitilized name
|
||||
*/
|
||||
String getCapitilizedName();
|
||||
|
||||
/**
|
||||
* The type of the property
|
||||
*
|
||||
* @return The property type
|
||||
*/
|
||||
Class<T> getType();
|
||||
|
||||
/**
|
||||
* Specifies the mapping between this property and an external form
|
||||
* such as a column, key/value pair etc.
|
||||
*
|
||||
* @return The PropertyMapping instance
|
||||
*/
|
||||
PropertyMapping<T> getMapping();
|
||||
|
||||
/**
|
||||
* Obtains the owner of this persistent property
|
||||
*
|
||||
* @return The owner
|
||||
*/
|
||||
PersistentEntity<?> getOwner();
|
||||
|
||||
/**
|
||||
* Whether the property can be set to null
|
||||
*
|
||||
* @return True if it can
|
||||
*/
|
||||
boolean isNullable();
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package org.springframework.data.mapping.model;
|
||||
|
||||
/**
|
||||
* A marker interface for a property mapping which specifies
|
||||
* what or where a particular property is mapped to
|
||||
*
|
||||
* @author Graeme Rocher
|
||||
* @since 1.0
|
||||
*/
|
||||
public interface PropertyMapping<T> {
|
||||
|
||||
/**
|
||||
* Retrieves the ClassMapping instance of the owning class
|
||||
*
|
||||
* @return The ClassMapping instance
|
||||
*/
|
||||
ClassMapping getClassMapping();
|
||||
|
||||
/**
|
||||
* Returns the mapped form of the property such as a Column, a Key/Value pair, attribute etc.
|
||||
* @return The mapped representation
|
||||
*/
|
||||
T getMappedForm();
|
||||
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
/* Copyright 2004-2005 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.mapping.model.types;
|
||||
|
||||
import org.springframework.data.mapping.model.*;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.FetchType;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Models an association between one class and another
|
||||
*
|
||||
* @author Graeme Rocher
|
||||
* @since 1.0
|
||||
*/
|
||||
public abstract class Association<T> extends AbstractPersistentProperty {
|
||||
|
||||
public static final List<CascadeType> DEFAULT_OWNER_CASCADE = new ArrayList<CascadeType>() {{
|
||||
add(CascadeType.ALL);
|
||||
}};
|
||||
|
||||
public static final List<CascadeType> DEFAULT_CHILD_CASCADE = new ArrayList<CascadeType>() {{
|
||||
add(CascadeType.PERSIST);
|
||||
}};
|
||||
|
||||
private PersistentEntity associatedEntity;
|
||||
private String referencedPropertyName;
|
||||
private boolean owningSide;
|
||||
private List<CascadeType> cascadeOperations = new ArrayList<CascadeType>();
|
||||
private FetchType fetchStrategy = FetchType.EAGER;
|
||||
|
||||
public Association(PersistentEntity owner, MappingContext context, PropertyDescriptor descriptor) {
|
||||
super(owner, context, descriptor);
|
||||
}
|
||||
|
||||
public Association(PersistentEntity owner, MappingContext context, String name, Class type) {
|
||||
super(owner, context, name, type);
|
||||
}
|
||||
|
||||
public FetchType getFetchStrategy() {
|
||||
return fetchStrategy;
|
||||
}
|
||||
|
||||
public void setFetchStrategy(FetchType fetchStrategy) {
|
||||
this.fetchStrategy = fetchStrategy;
|
||||
}
|
||||
|
||||
public boolean isBidirectional() {
|
||||
return getInverseSide() != null;
|
||||
}
|
||||
|
||||
public Association getInverseSide() {
|
||||
final PersistentProperty associatedProperty = associatedEntity.getPropertyByName(referencedPropertyName);
|
||||
if(associatedProperty == null) return null;
|
||||
if(associatedProperty instanceof Association) {
|
||||
return (Association) associatedProperty;
|
||||
}
|
||||
else {
|
||||
throw new IllegalMappingException("The inverse side ["+associatedEntity.getName()+"." + associatedProperty.getName() +"] of the association ["+getOwner().getName()+"." + this.getName() +"] is not valid. Associations can only map to other entities and collection types.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the this association cascade for the given cascade operation
|
||||
*
|
||||
* @param cascadeOperation The cascadeOperation
|
||||
* @return True if it does
|
||||
*/
|
||||
public boolean doesCascade(CascadeType cascadeOperation) {
|
||||
List<CascadeType> cascades = getCascadeOperations();
|
||||
return cascadeOperation != null && (cascades.contains(CascadeType.ALL) || cascades.contains(cascadeOperation));
|
||||
}
|
||||
|
||||
protected List<CascadeType> getCascadeOperations() {
|
||||
List<CascadeType> cascades;
|
||||
if(cascadeOperations.isEmpty()) {
|
||||
if(isOwningSide()) cascades = DEFAULT_OWNER_CASCADE;
|
||||
else {
|
||||
cascades = DEFAULT_CHILD_CASCADE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
cascades = this.cascadeOperations;
|
||||
}
|
||||
return cascades;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this side owns the relationship. This controls
|
||||
* the default cascading behavior if none is specified
|
||||
*
|
||||
* @return True if this property is the owning side
|
||||
*/
|
||||
public boolean isOwningSide() {
|
||||
return owningSide;
|
||||
}
|
||||
|
||||
public void setOwningSide(boolean owningSide) {
|
||||
this.owningSide = owningSide;
|
||||
}
|
||||
|
||||
public void setAssociatedEntity(PersistentEntity associatedEntity) {
|
||||
this.associatedEntity = associatedEntity;
|
||||
}
|
||||
|
||||
public PersistentEntity getAssociatedEntity() {
|
||||
return associatedEntity;
|
||||
}
|
||||
|
||||
public void setReferencedPropertyName(String referencedPropertyName) {
|
||||
this.referencedPropertyName = referencedPropertyName;
|
||||
}
|
||||
|
||||
public String getReferencedPropertyName() {
|
||||
return referencedPropertyName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getOwner().getName() + "->" + getName();
|
||||
}
|
||||
|
||||
public boolean isList() {
|
||||
return List.class.isAssignableFrom(getType());
|
||||
}
|
||||
|
||||
public boolean isCircular() {
|
||||
return isBidirectional() && getAssociatedEntity().equals(getOwner());
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/* Copyright (C) 2011 SpringSource
|
||||
*
|
||||
* 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.mapping.model.types;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
|
||||
import org.springframework.data.mapping.model.MappingContext;
|
||||
import org.springframework.data.mapping.model.PersistentEntity;
|
||||
import org.springframework.data.mapping.model.PropertyMapping;
|
||||
|
||||
/**
|
||||
* Models a basic collection type such as a list of Strings
|
||||
*
|
||||
* @author Graeme Rocher
|
||||
* @since 1.0
|
||||
*
|
||||
*/
|
||||
public abstract class Basic extends Association {
|
||||
|
||||
public Basic(PersistentEntity owner, MappingContext context,
|
||||
PropertyDescriptor descriptor) {
|
||||
super(owner, context, descriptor);
|
||||
}
|
||||
|
||||
public Basic(PersistentEntity owner, MappingContext context, String name, Class type) {
|
||||
super(owner, context, name, type);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Association getInverseSide() {
|
||||
return null; // basic collection types have no inverse side
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOwningSide() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOwningSide(boolean owningSide) {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistentEntity getAssociatedEntity() {
|
||||
return null; // basic collection types have no associated entity
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
/* Copyright (C) 2010 SpringSource
|
||||
*
|
||||
* 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.mapping.model.types;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.converter.ConverterRegistry;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
/**
|
||||
* A registrar that registers basic type converters
|
||||
*
|
||||
* @author Graeme Rocher
|
||||
* @since 1.0
|
||||
*/
|
||||
public class BasicTypeConverterRegistrar {
|
||||
|
||||
public void register(ConverterRegistry registry) {
|
||||
registry.addConverter(new Converter<Date, String>() {
|
||||
public String convert(Date date) {
|
||||
return String.valueOf(date.getTime());
|
||||
}
|
||||
});
|
||||
registry.addConverter(new Converter<Date, Calendar>() {
|
||||
public Calendar convert(Date date) {
|
||||
final GregorianCalendar calendar = new GregorianCalendar();
|
||||
calendar.setTime(date);
|
||||
return calendar;
|
||||
}
|
||||
});
|
||||
|
||||
registry.addConverter(new Converter<Integer, Long>() {
|
||||
public Long convert(Integer integer) {
|
||||
return integer.longValue();
|
||||
}
|
||||
});
|
||||
|
||||
registry.addConverter(new Converter<Integer, Double>() {
|
||||
public Double convert(Integer integer) {
|
||||
return integer.doubleValue();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
registry.addConverter(new Converter<CharSequence, Date>() {
|
||||
|
||||
public Date convert(CharSequence s) {
|
||||
try {
|
||||
final Long time = Long.valueOf(s.toString());
|
||||
return new Date(time);
|
||||
} catch (NumberFormatException e) {
|
||||
// ignore
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
registry.addConverter(new Converter<CharSequence, Double>() {
|
||||
|
||||
public Double convert(CharSequence s) {
|
||||
try {
|
||||
return Double.valueOf(s.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
return (double) 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
registry.addConverter(new Converter<CharSequence, Integer>() {
|
||||
|
||||
public Integer convert(CharSequence s) {
|
||||
try {
|
||||
return Integer.valueOf(s.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
// ignore
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
registry.addConverter(new Converter<CharSequence, Long>() {
|
||||
|
||||
public Long convert(CharSequence s) {
|
||||
try {
|
||||
return Long.valueOf(s.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
// ignore
|
||||
}
|
||||
return 0L;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
registry.addConverter(new Converter<Object, String>() {
|
||||
public String convert(Object o) {
|
||||
return o.toString();
|
||||
}
|
||||
});
|
||||
|
||||
registry.addConverter(new Converter<Calendar, String>() {
|
||||
public String convert(Calendar calendar) {
|
||||
return String.valueOf(calendar.getTime().getTime());
|
||||
}
|
||||
});
|
||||
|
||||
registry.addConverter(new Converter<CharSequence, Calendar>() {
|
||||
|
||||
public Calendar convert(CharSequence s) {
|
||||
try {
|
||||
Date date = new Date(Long.valueOf(s.toString()));
|
||||
Calendar c = new GregorianCalendar();
|
||||
c.setTime(date);
|
||||
return c;
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/* Copyright 2004-2005 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.mapping.model.types;
|
||||
|
||||
import org.springframework.data.mapping.model.MappingContext;
|
||||
import org.springframework.data.mapping.model.PersistentEntity;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
|
||||
/**
|
||||
* Models an embedded component
|
||||
*
|
||||
* @author Graeme Rocher
|
||||
* @since 1.0
|
||||
*/
|
||||
public abstract class Embedded<T> extends ToOne<T> {
|
||||
|
||||
public Embedded(PersistentEntity owner, MappingContext context, PropertyDescriptor descriptor) {
|
||||
super(owner, context, descriptor);
|
||||
}
|
||||
|
||||
public Embedded(PersistentEntity owner, MappingContext context, String name, Class type) {
|
||||
super(owner, context, name, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOwningSide() {
|
||||
return true; // embedded instances are always owned
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/* Copyright 2004-2005 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.mapping.model.types;
|
||||
|
||||
import org.springframework.data.mapping.model.AbstractPersistentProperty;
|
||||
import org.springframework.data.mapping.model.MappingContext;
|
||||
import org.springframework.data.mapping.model.PersistentEntity;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
|
||||
/**
|
||||
* Represents the identity of a persistent entity
|
||||
*
|
||||
* @author Graeme Rocher
|
||||
* @since 1.0
|
||||
*/
|
||||
public abstract class Identity<T> extends AbstractPersistentProperty {
|
||||
public Identity(PersistentEntity owner, MappingContext context, PropertyDescriptor descriptor) {
|
||||
super(owner, context, descriptor);
|
||||
}
|
||||
public Identity(PersistentEntity owner, MappingContext context, String name, Class type) {
|
||||
super(owner, context, name, type);
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/* Copyright (C) 2010 SpringSource
|
||||
*
|
||||
* 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.mapping.model.types;
|
||||
|
||||
import org.springframework.data.mapping.model.MappingContext;
|
||||
import org.springframework.data.mapping.model.PersistentEntity;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
|
||||
/**
|
||||
* Models a many-to-many association between one class and another
|
||||
*
|
||||
* @author Graeme Rocher
|
||||
* @since 1.0
|
||||
*/
|
||||
public abstract class ManyToMany<T> extends Association<T> {
|
||||
public ManyToMany(PersistentEntity owner, MappingContext context, PropertyDescriptor descriptor) {
|
||||
super(owner, context, descriptor);
|
||||
}
|
||||
|
||||
public ManyToMany(PersistentEntity owner, MappingContext context, String name, Class type) {
|
||||
super(owner, context, name, type);
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/* Copyright 2004-2005 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.mapping.model.types;
|
||||
|
||||
import org.springframework.data.mapping.model.MappingContext;
|
||||
import org.springframework.data.mapping.model.PersistentEntity;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
|
||||
/**
|
||||
* Models a many-to-one association
|
||||
*
|
||||
* @author Graeme Rocher
|
||||
* @since 1.0
|
||||
*/
|
||||
public abstract class ManyToOne<T> extends ToOne<T> {
|
||||
public ManyToOne(PersistentEntity owner, MappingContext context, PropertyDescriptor descriptor) {
|
||||
super(owner, context, descriptor);
|
||||
}
|
||||
|
||||
public ManyToOne(PersistentEntity owner, MappingContext context, String name, Class type) {
|
||||
super(owner, context, name, type);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/* Copyright 2004-2005 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.mapping.model.types;
|
||||
|
||||
import org.springframework.data.mapping.model.MappingContext;
|
||||
import org.springframework.data.mapping.model.PersistentEntity;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
|
||||
/**
|
||||
* Models a one-to-many association
|
||||
*
|
||||
* @author Graeme Rocher
|
||||
* @since 1.0
|
||||
*/
|
||||
public abstract class OneToMany<T> extends Association<T> {
|
||||
public OneToMany(PersistentEntity owner, MappingContext context, PropertyDescriptor descriptor) {
|
||||
super(owner, context, descriptor);
|
||||
}
|
||||
|
||||
public OneToMany(PersistentEntity owner, MappingContext context, String name, Class type) {
|
||||
super(owner, context, name, type);
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/* Copyright 2004-2005 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.mapping.model.types;
|
||||
|
||||
import org.springframework.data.mapping.model.MappingContext;
|
||||
import org.springframework.data.mapping.model.PersistentEntity;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
|
||||
/**
|
||||
* Models a one-to-one association
|
||||
*
|
||||
* @author Graeme Rocher
|
||||
* @since 1.0
|
||||
*/
|
||||
public abstract class OneToOne<T> extends ToOne<T> {
|
||||
public OneToOne(PersistentEntity owner, MappingContext context, PropertyDescriptor descriptor) {
|
||||
super(owner, context, descriptor);
|
||||
}
|
||||
|
||||
public OneToOne(PersistentEntity owner, MappingContext context, String name, Class type) {
|
||||
super(owner, context, name, type);
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/* Copyright 2004-2005 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.mapping.model.types;
|
||||
|
||||
import org.springframework.data.mapping.model.AbstractPersistentProperty;
|
||||
import org.springframework.data.mapping.model.MappingContext;
|
||||
import org.springframework.data.mapping.model.PersistentEntity;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
|
||||
/**
|
||||
* Models a simple property type
|
||||
*
|
||||
* @author Graeme Rocher
|
||||
* @since 1.0
|
||||
*/
|
||||
public abstract class Simple<T> extends AbstractPersistentProperty {
|
||||
public Simple(PersistentEntity owner, MappingContext context, PropertyDescriptor descriptor) {
|
||||
super(owner, context, descriptor);
|
||||
}
|
||||
|
||||
public Simple(PersistentEntity owner, MappingContext context, String name, Class type) {
|
||||
super(owner, context, name, type);
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/* Copyright (C) 2010 SpringSource
|
||||
*
|
||||
* 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.mapping.model.types;
|
||||
|
||||
import org.springframework.data.mapping.model.MappingContext;
|
||||
import org.springframework.data.mapping.model.PersistentEntity;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
|
||||
/**
|
||||
* @author Graeme Rocher
|
||||
* @since 1.1
|
||||
*/
|
||||
public abstract class ToOne<T> extends Association<T> {
|
||||
|
||||
private boolean foreignKeyInChild;
|
||||
|
||||
public ToOne(PersistentEntity owner, MappingContext context, PropertyDescriptor descriptor) {
|
||||
super(owner, context, descriptor);
|
||||
}
|
||||
|
||||
public ToOne(PersistentEntity owner, MappingContext context, String name, Class type) {
|
||||
super(owner, context, name, type);
|
||||
}
|
||||
|
||||
public void setForeignKeyInChild(boolean foreignKeyInChild) {
|
||||
this.foreignKeyInChild = foreignKeyInChild;
|
||||
}
|
||||
|
||||
public boolean isForeignKeyInChild() {
|
||||
return foreignKeyInChild;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,372 +0,0 @@
|
||||
/* Copyright 2004-2005 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.mapping.reflect;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.data.mapping.annotation.PersistenceConstructor;
|
||||
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Reads the properties of a class in an optimized manner avoiding
|
||||
* exceptions
|
||||
*
|
||||
* @author Graeme Rocher
|
||||
* @since 1.0
|
||||
*/
|
||||
public class ClassPropertyFetcher {
|
||||
|
||||
private static final Log log = LogFactory.getLog(ClassPropertyFetcher.class);
|
||||
|
||||
private final Class clazz;
|
||||
final Map<String, PropertyFetcher> staticFetchers = new HashMap<String, PropertyFetcher>();
|
||||
final Map<String, PropertyFetcher> instanceFetchers = new HashMap<String, PropertyFetcher>();
|
||||
private final ReferenceInstanceCallback callback;
|
||||
private PropertyDescriptor[] propertyDescriptors;
|
||||
private Constructor preferredConstructor;
|
||||
private Map<String, PropertyDescriptor> propertyDescriptorsByName = new HashMap<String, PropertyDescriptor>();
|
||||
private Map<String, Field> fieldsByName = new HashMap<String, Field>();
|
||||
private Map<Class, List<PropertyDescriptor>> typeToPropertyMap = new HashMap<Class, List<PropertyDescriptor>>();
|
||||
|
||||
private static Map<Class, ClassPropertyFetcher> cachedClassPropertyFetchers = new WeakHashMap<Class, ClassPropertyFetcher>();
|
||||
|
||||
public static ClassPropertyFetcher forClass(final Class c) {
|
||||
ClassPropertyFetcher cpf = cachedClassPropertyFetchers.get(c);
|
||||
if (cpf == null) {
|
||||
cpf = new ClassPropertyFetcher(c);
|
||||
cachedClassPropertyFetchers.put(c, cpf);
|
||||
}
|
||||
return cpf;
|
||||
}
|
||||
|
||||
ClassPropertyFetcher(final Class clazz) {
|
||||
this.clazz = clazz;
|
||||
this.callback = new ReferenceInstanceCallback() {
|
||||
public Object getReferenceInstance() {
|
||||
try {
|
||||
return ReflectionUtils.instantiate(clazz);
|
||||
} catch (InstantiationException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The Java that this ClassPropertyFetcher was constructor for
|
||||
*/
|
||||
public Class getJavaClass() {
|
||||
return this.clazz;
|
||||
}
|
||||
|
||||
public Object getReference() {
|
||||
if (callback != null)
|
||||
return this.callback.getReferenceInstance();
|
||||
return null;
|
||||
}
|
||||
|
||||
public PropertyDescriptor[] getPropertyDescriptors() {
|
||||
return propertyDescriptors;
|
||||
}
|
||||
|
||||
public boolean isReadableProperty(String name) {
|
||||
return staticFetchers.containsKey(name) || instanceFetchers.containsKey(name);
|
||||
}
|
||||
|
||||
private void init() {
|
||||
|
||||
List<Class> allClasses = resolveAllClasses(clazz);
|
||||
for (Class c : allClasses) {
|
||||
Field[] fields = c.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
processField(field);
|
||||
}
|
||||
Method[] methods = c.getDeclaredMethods();
|
||||
for (Method method : methods) {
|
||||
processMethod(method);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
this.propertyDescriptors = Introspector.getBeanInfo(clazz).getPropertyDescriptors();
|
||||
} catch (IntrospectionException e) {
|
||||
// ignore
|
||||
}
|
||||
if (propertyDescriptors != null) {
|
||||
for (PropertyDescriptor desc : propertyDescriptors) {
|
||||
propertyDescriptorsByName.put(desc.getName(), desc);
|
||||
final Class<?> propertyType = desc.getPropertyType();
|
||||
List<PropertyDescriptor> pds = typeToPropertyMap.get(propertyType);
|
||||
if (pds == null) {
|
||||
pds = new ArrayList<PropertyDescriptor>();
|
||||
typeToPropertyMap.put(propertyType, pds);
|
||||
}
|
||||
pds.add(desc);
|
||||
|
||||
Method readMethod = desc.getReadMethod();
|
||||
if (readMethod != null) {
|
||||
boolean staticReadMethod = Modifier.isStatic(readMethod.getModifiers());
|
||||
if (staticReadMethod) {
|
||||
staticFetchers.put(desc.getName(), new GetterPropertyFetcher(readMethod, staticReadMethod));
|
||||
} else {
|
||||
instanceFetchers.put(desc.getName(), new GetterPropertyFetcher(readMethod, staticReadMethod));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Constructor[] constructors = clazz.getConstructors();
|
||||
if (constructors.length == 1 && constructors[0].getGenericParameterTypes().length > 0) {
|
||||
preferredConstructor = constructors[0];
|
||||
} else {
|
||||
for (Constructor<?> c : constructors) {
|
||||
if (c.isAnnotationPresent(PersistenceConstructor.class)) {
|
||||
preferredConstructor = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public <T> Constructor<T> getPreferredConstructor() {
|
||||
return preferredConstructor;
|
||||
}
|
||||
|
||||
private void processMethod(Method method) {
|
||||
if (method.isSynthetic())
|
||||
return;
|
||||
if (!Modifier.isPublic(method.getModifiers()))
|
||||
return;
|
||||
if (Modifier.isStatic(method.getModifiers())
|
||||
&& method.getReturnType() != Void.class) {
|
||||
if (method.getParameterTypes().length == 0) {
|
||||
String name = method.getName();
|
||||
if (name.indexOf('$') == -1) {
|
||||
if (name.length() > 3 && name.startsWith("get") && Character.isUpperCase(name.charAt(3))) {
|
||||
name = name.substring(3);
|
||||
} else if (name.length() > 2
|
||||
&& name.startsWith("is")
|
||||
&& Character.isUpperCase(name.charAt(2))
|
||||
&& (method.getReturnType() == Boolean.class || method.getReturnType() == boolean.class)) {
|
||||
name = name.substring(2);
|
||||
}
|
||||
PropertyFetcher fetcher = new GetterPropertyFetcher(method, true);
|
||||
staticFetchers.put(name, fetcher);
|
||||
staticFetchers.put(Introspector.decapitalize(name), fetcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processField(Field field) {
|
||||
if (field.isSynthetic())
|
||||
return;
|
||||
final int modifiers = field.getModifiers();
|
||||
final String name = field.getName();
|
||||
if (!Modifier.isPublic(modifiers)) {
|
||||
if (name.indexOf('$') == -1) {
|
||||
fieldsByName.put(name, field);
|
||||
}
|
||||
} else {
|
||||
if (name.indexOf('$') == -1) {
|
||||
boolean staticField = Modifier.isStatic(modifiers);
|
||||
if (staticField) {
|
||||
staticFetchers.put(name, new FieldReaderFetcher(field, staticField));
|
||||
} else {
|
||||
instanceFetchers.put(name, new FieldReaderFetcher(field, staticField));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private List<Class> resolveAllClasses(Class c) {
|
||||
List<Class> list = new ArrayList<Class>();
|
||||
Class currentClass = c;
|
||||
while (currentClass != null) {
|
||||
list.add(currentClass);
|
||||
currentClass = currentClass.getSuperclass();
|
||||
}
|
||||
Collections.reverse(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
public Object getPropertyValue(String name) {
|
||||
return getPropertyValue(name, false);
|
||||
}
|
||||
|
||||
public Object getPropertyValue(String name, boolean onlyInstanceProperties) {
|
||||
PropertyFetcher fetcher = resolveFetcher(name, onlyInstanceProperties);
|
||||
return getPropertyValueWithFetcher(name, fetcher);
|
||||
}
|
||||
|
||||
private Object getPropertyValueWithFetcher(String name, PropertyFetcher fetcher) {
|
||||
if (fetcher != null) {
|
||||
try {
|
||||
return fetcher.get(callback);
|
||||
} catch (Exception e) {
|
||||
log.warn("Error fetching property's " + name + " value from class " + clazz.getName(), e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T getStaticPropertyValue(String name, Class<T> c) {
|
||||
PropertyFetcher fetcher = staticFetchers.get(name);
|
||||
if (fetcher != null) {
|
||||
Object v = getPropertyValueWithFetcher(name, fetcher);
|
||||
return returnOnlyIfInstanceOf(v, c);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T getPropertyValue(String name, Class<T> c) {
|
||||
return returnOnlyIfInstanceOf(getPropertyValue(name, false), c);
|
||||
}
|
||||
|
||||
private <T> T returnOnlyIfInstanceOf(Object value, Class<T> type) {
|
||||
if ((value != null) && (type == Object.class || ReflectionUtils.isAssignableFrom(type, value.getClass()))) {
|
||||
return (T) value;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private PropertyFetcher resolveFetcher(String name,
|
||||
boolean onlyInstanceProperties) {
|
||||
PropertyFetcher fetcher = null;
|
||||
if (!onlyInstanceProperties) {
|
||||
fetcher = staticFetchers.get(name);
|
||||
}
|
||||
if (fetcher == null) {
|
||||
fetcher = instanceFetchers.get(name);
|
||||
}
|
||||
return fetcher;
|
||||
}
|
||||
|
||||
public Class getPropertyType(String name) {
|
||||
return getPropertyType(name, false);
|
||||
}
|
||||
|
||||
public Class getPropertyType(String name, boolean onlyInstanceProperties) {
|
||||
PropertyFetcher fetcher = resolveFetcher(name, onlyInstanceProperties);
|
||||
if (fetcher != null) {
|
||||
return fetcher.getPropertyType(name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public PropertyDescriptor getPropertyDescriptor(String name) {
|
||||
return propertyDescriptorsByName.get(name);
|
||||
}
|
||||
|
||||
public List<PropertyDescriptor> getPropertiesOfType(Class javaClass) {
|
||||
final List<PropertyDescriptor> propertyDescriptorList = typeToPropertyMap.get(javaClass);
|
||||
if (propertyDescriptorList == null) return Collections.emptyList();
|
||||
return propertyDescriptorList;
|
||||
}
|
||||
|
||||
public List<PropertyDescriptor> getPropertiesAssignableToType(Class assignableType) {
|
||||
List<PropertyDescriptor> properties = new ArrayList<PropertyDescriptor>();
|
||||
for (Class type : typeToPropertyMap.keySet()) {
|
||||
if (assignableType.isAssignableFrom(type)) {
|
||||
properties.addAll(typeToPropertyMap.get(type));
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
public static interface ReferenceInstanceCallback {
|
||||
public Object getReferenceInstance();
|
||||
}
|
||||
|
||||
static interface PropertyFetcher {
|
||||
public Object get(ReferenceInstanceCallback callback)
|
||||
throws IllegalArgumentException, IllegalAccessException,
|
||||
InvocationTargetException;
|
||||
|
||||
public Class getPropertyType(String name);
|
||||
}
|
||||
|
||||
static class GetterPropertyFetcher implements PropertyFetcher {
|
||||
private final Method readMethod;
|
||||
private final boolean staticMethod;
|
||||
|
||||
GetterPropertyFetcher(Method readMethod, boolean staticMethod) {
|
||||
this.readMethod = readMethod;
|
||||
this.staticMethod = staticMethod;
|
||||
ReflectionUtils.makeAccessible(readMethod);
|
||||
}
|
||||
|
||||
public Object get(ReferenceInstanceCallback callback)
|
||||
throws IllegalArgumentException, IllegalAccessException,
|
||||
InvocationTargetException {
|
||||
if (staticMethod) {
|
||||
return readMethod.invoke(null, (Object[]) null);
|
||||
} else {
|
||||
if (callback != null) {
|
||||
return readMethod.invoke(callback.getReferenceInstance(),
|
||||
(Object[]) null);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Class getPropertyType(String name) {
|
||||
return readMethod.getReturnType();
|
||||
}
|
||||
}
|
||||
|
||||
static class FieldReaderFetcher implements PropertyFetcher {
|
||||
private final Field field;
|
||||
private final boolean staticField;
|
||||
|
||||
public FieldReaderFetcher(Field field, boolean staticField) {
|
||||
this.field = field;
|
||||
this.staticField = staticField;
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
}
|
||||
|
||||
public Object get(ReferenceInstanceCallback callback)
|
||||
throws IllegalArgumentException, IllegalAccessException,
|
||||
InvocationTargetException {
|
||||
if (staticField) {
|
||||
return field.get(null);
|
||||
} else {
|
||||
if (callback != null) {
|
||||
return field.get(callback.getReferenceInstance());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Class getPropertyType(String name) {
|
||||
return field.getType();
|
||||
}
|
||||
}
|
||||
|
||||
public Field getDeclaredField(String name) {
|
||||
return fieldsByName.get(name);
|
||||
}
|
||||
}
|
||||
@@ -1,209 +0,0 @@
|
||||
/* Copyright 2004-2005 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.mapping.reflect;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.Boolean;
|
||||
import java.lang.Byte;
|
||||
import java.lang.Character;
|
||||
import java.lang.Class;
|
||||
import java.lang.Double;
|
||||
import java.lang.Exception;
|
||||
import java.lang.Float;
|
||||
import java.lang.IllegalAccessException;
|
||||
import java.lang.InstantiationException;
|
||||
import java.lang.Integer;
|
||||
import java.lang.Long;
|
||||
import java.lang.NoSuchMethodException;
|
||||
import java.lang.NullPointerException;
|
||||
import java.lang.Object;
|
||||
import java.lang.Short;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Provides methods to help with reflective operations
|
||||
*
|
||||
* @author Graeme Rocher
|
||||
* @since 1.0
|
||||
*/
|
||||
public class ReflectionUtils {
|
||||
|
||||
public static final Map<Class<?>, Class<?>> PRIMITIVE_TYPE_COMPATIBLE_CLASSES = new HashMap<Class<?>, Class<?>>();
|
||||
private static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
|
||||
|
||||
/**
|
||||
* Just add two entries to the class compatibility map
|
||||
*
|
||||
* @param left
|
||||
* @param right
|
||||
*/
|
||||
private static void registerPrimitiveClassPair(Class<?> left, Class<?> right) {
|
||||
PRIMITIVE_TYPE_COMPATIBLE_CLASSES.put(left, right);
|
||||
PRIMITIVE_TYPE_COMPATIBLE_CLASSES.put(right, left);
|
||||
}
|
||||
|
||||
static {
|
||||
registerPrimitiveClassPair(Boolean.class, boolean.class);
|
||||
registerPrimitiveClassPair(Integer.class, int.class);
|
||||
registerPrimitiveClassPair(Short.class, short.class);
|
||||
registerPrimitiveClassPair(Byte.class, byte.class);
|
||||
registerPrimitiveClassPair(Character.class, char.class);
|
||||
registerPrimitiveClassPair(Long.class, long.class);
|
||||
registerPrimitiveClassPair(Float.class, float.class);
|
||||
registerPrimitiveClassPair(Double.class, double.class);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make the given field accessible, explicitly setting it accessible if necessary.
|
||||
* The <code>setAccessible(true)</code> method is only called when actually necessary,
|
||||
* to avoid unnecessary conflicts with a JVM SecurityManager (if active).
|
||||
* <p/>
|
||||
* Based on the same method in Spring core.
|
||||
*
|
||||
* @param field the field to make accessible
|
||||
* @see java.lang.reflect.Field#setAccessible
|
||||
*/
|
||||
public static void makeAccessible(Field field) {
|
||||
if (!Modifier.isPublic(field.getModifiers()) ||
|
||||
!Modifier.isPublic(field.getDeclaringClass().getModifiers())) {
|
||||
field.setAccessible(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the given method accessible, explicitly setting it accessible if necessary.
|
||||
* The <code>setAccessible(true)</code> method is only called when actually necessary,
|
||||
* to avoid unnecessary conflicts with a JVM SecurityManager (if active).
|
||||
* <p/>
|
||||
* Based on the same method in Spring core.
|
||||
*
|
||||
* @param method the method to make accessible
|
||||
* @see java.lang.reflect.Method#setAccessible
|
||||
*/
|
||||
public static void makeAccessible(Method method) {
|
||||
if (!Modifier.isPublic(method.getModifiers()) ||
|
||||
!Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
|
||||
method.setAccessible(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Tests whether or not the left hand type is compatible with the right hand type in Groovy
|
||||
* terms, i.e. can the left type be assigned a value of the right hand type in Groovy.</p>
|
||||
* <p>This handles Java primitive type equivalence and uses isAssignableFrom for all other types,
|
||||
* with a bit of magic for native types and polymorphism i.e. Number assigned an int.
|
||||
* If either parameter is null an exception is thrown</p>
|
||||
*
|
||||
* @param leftType The type of the left hand part of a notional assignment
|
||||
* @param rightType The type of the right hand part of a notional assignment
|
||||
* @return True if values of the right hand type can be assigned in Groovy to variables of the left hand type.
|
||||
*/
|
||||
public static boolean isAssignableFrom(final Class<?> leftType, final Class<?> rightType) {
|
||||
if (leftType == null) {
|
||||
throw new NullPointerException("Left type is null!");
|
||||
}
|
||||
if (rightType == null) {
|
||||
throw new NullPointerException("Right type is null!");
|
||||
}
|
||||
if (leftType == Object.class) {
|
||||
return true;
|
||||
}
|
||||
if (leftType == rightType) {
|
||||
return true;
|
||||
}
|
||||
// check for primitive type equivalence
|
||||
Class<?> r = PRIMITIVE_TYPE_COMPATIBLE_CLASSES.get(leftType);
|
||||
boolean result = r == rightType;
|
||||
|
||||
if (!result) {
|
||||
// If no primitive <-> wrapper match, it may still be assignable
|
||||
// from polymorphic primitives i.e. Number -> int (AKA Integer)
|
||||
if (rightType.isPrimitive()) {
|
||||
// see if incompatible
|
||||
r = PRIMITIVE_TYPE_COMPATIBLE_CLASSES.get(rightType);
|
||||
if (r != null) {
|
||||
result = leftType.isAssignableFrom(r);
|
||||
}
|
||||
} else {
|
||||
// Otherwise it may just be assignable using normal Java polymorphism
|
||||
result = leftType.isAssignableFrom(rightType);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates an object catching any relevant exceptions and rethrowing as a runtime exception
|
||||
*
|
||||
* @param clazz The class
|
||||
* @return The instantiated object or null if the class parameter was null
|
||||
*/
|
||||
public static Object instantiate(Class clazz) throws InstantiationException {
|
||||
if (clazz == null) return null;
|
||||
try {
|
||||
return clazz.getConstructor(EMPTY_CLASS_ARRAY).newInstance();
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new InstantiationException(e.getClass().getName() + " error creating instance of class [" + e.getMessage() + "]: " + e.getMessage());
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new InstantiationException(e.getClass().getName() + " error creating instance of class [" + e.getMessage() + "]: " + e.getMessage());
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new InstantiationException(e.getClass().getName() + " error creating instance of class [" + e.getMessage() + "]: " + e.getMessage());
|
||||
} catch (java.lang.InstantiationException e) {
|
||||
throw new InstantiationException(e.getClass().getName() + " error creating instance of class [" + e.getMessage() + "]: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all the properties of the given class for the given type
|
||||
*
|
||||
* @param clazz The class to retrieve the properties from
|
||||
* @param propertyType The type of the properties you wish to retrieve
|
||||
* @return An array of PropertyDescriptor instances
|
||||
*/
|
||||
public static PropertyDescriptor[] getPropertiesOfType(Class<?> clazz, Class<?> propertyType) {
|
||||
if (clazz == null || propertyType == null) {
|
||||
return new PropertyDescriptor[0];
|
||||
}
|
||||
|
||||
Set<PropertyDescriptor> properties = new HashSet<PropertyDescriptor>();
|
||||
try {
|
||||
for (PropertyDescriptor descriptor : BeanUtils.getPropertyDescriptors(clazz)) {
|
||||
Class<?> currentPropertyType = descriptor.getPropertyType();
|
||||
if (isTypeInstanceOfPropertyType(propertyType, currentPropertyType)) {
|
||||
properties.add(descriptor);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// if there are any errors in instantiating just return null for the moment
|
||||
return new PropertyDescriptor[0];
|
||||
}
|
||||
return properties.toArray(new PropertyDescriptor[properties.size()]);
|
||||
}
|
||||
|
||||
private static boolean isTypeInstanceOfPropertyType(Class<?> type, Class<?> propertyType) {
|
||||
return propertyType.isAssignableFrom(type) && !propertyType.equals(Object.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -63,7 +63,8 @@ public abstract class MongoOperationsUnitTests {
|
||||
dbo.put("firstName", person.getFirstName());
|
||||
}
|
||||
|
||||
public <S extends Object> S read(Class<S> clazz, DBObject dbo) {
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public <S extends Object> S read(Class<S> clazz, DBObject dbo) {
|
||||
return (S) person;
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,8 @@ public class MongoNamespaceTests {
|
||||
|
||||
|
||||
|
||||
public static <T> T readField(String name, Object target) throws Exception {
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public static <T> T readField(String name, Object target) throws Exception {
|
||||
Field field = null;
|
||||
Class<?> clazz = target.getClass();
|
||||
do {
|
||||
|
||||
@@ -16,13 +16,13 @@
|
||||
|
||||
package org.springframework.data.document.mongodb.mapping;
|
||||
|
||||
import org.springframework.data.mapping.annotation.Id;
|
||||
import org.springframework.data.mapping.annotation.Persistent;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
@Persistent
|
||||
@Document
|
||||
public class Account {
|
||||
|
||||
@Id
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.springframework.data.document.mongodb.MongoTemplate;
|
||||
import org.springframework.data.document.mongodb.convert.MongoConverter;
|
||||
import org.springframework.data.document.mongodb.query.Criteria;
|
||||
import org.springframework.data.document.mongodb.query.Query;
|
||||
import org.springframework.data.mapping.BasicMappingContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@@ -33,6 +34,7 @@ import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
@@ -45,7 +47,7 @@ public class MappingTests {
|
||||
@Autowired
|
||||
MongoTemplate template;
|
||||
@Autowired
|
||||
MongoMappingContext mappingContext;
|
||||
BasicMappingContext mappingContext;
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
@@ -54,9 +56,9 @@ public class MappingTests {
|
||||
public void setUp() {
|
||||
template.dropCollection("person");
|
||||
template.dropCollection("account");
|
||||
//mappingContext.addPersistentEntity(Person.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
@Test
|
||||
public void testWrite() {
|
||||
Person p = new Person(123456789, "John", "Doe", 37);
|
||||
@@ -77,6 +79,8 @@ public class MappingTests {
|
||||
p.setAccounts(accounts);
|
||||
|
||||
template.insert("person", p);
|
||||
|
||||
assertNotNull(p.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -16,21 +16,23 @@
|
||||
|
||||
package org.springframework.data.document.mongodb.mapping;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.PersistenceConstructor;
|
||||
import org.springframework.data.annotation.Transient;
|
||||
import org.springframework.data.document.mongodb.index.CompoundIndex;
|
||||
import org.springframework.data.document.mongodb.index.CompoundIndexes;
|
||||
import org.springframework.data.document.mongodb.index.Indexed;
|
||||
import org.springframework.data.mapping.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
@Persistent
|
||||
@Document
|
||||
@CompoundIndexes({
|
||||
@CompoundIndex(name = "age_idx", def = "{'lastName': 1, 'age': -1}")
|
||||
})
|
||||
public class Person {
|
||||
public class Person<T extends Address> {
|
||||
|
||||
@Id
|
||||
private String id;
|
||||
@@ -42,9 +44,9 @@ public class Person {
|
||||
private Integer age;
|
||||
@Transient
|
||||
private Integer accountTotal;
|
||||
@Reference
|
||||
@DBRef
|
||||
private List<Account> accounts;
|
||||
private Address address;
|
||||
private T address;
|
||||
|
||||
public Person(Integer ssn, String firstName, String lastName, Integer age) {
|
||||
this.ssn = ssn;
|
||||
@@ -54,7 +56,7 @@ public class Person {
|
||||
}
|
||||
|
||||
@PersistenceConstructor
|
||||
public Person(Integer ssn, String firstName, String lastName, Integer age, Address address) {
|
||||
public Person(Integer ssn, String firstName, String lastName, Integer age, T address) {
|
||||
this.ssn = ssn;
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
@@ -114,11 +116,11 @@ public class Person {
|
||||
this.accounts = accounts;
|
||||
}
|
||||
|
||||
public Address getAddress() {
|
||||
public T getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(Address address) {
|
||||
public void setAddress(T address) {
|
||||
this.address = address;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
|
||||
@Test
|
||||
public void findsPersonById() throws Exception {
|
||||
|
||||
assertThat(repository.findById(dave.getId()), is(dave));
|
||||
assertThat(repository.findOne(dave.getId()), is(dave));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -31,8 +31,8 @@ public class MongoEntityMetadataUnitTests {
|
||||
@Test
|
||||
public void findsIdField() throws Exception {
|
||||
|
||||
MongoEntityInformation<Person> isNewAware =
|
||||
new MongoEntityInformation<Person>(Person.class);
|
||||
MongoEntityInformation<Person, Long> isNewAware =
|
||||
new MongoEntityInformation<Person, Long>(Person.class);
|
||||
|
||||
Person person = new Person();
|
||||
assertThat(isNewAware.isNew(person), is(true));
|
||||
@@ -44,7 +44,7 @@ public class MongoEntityMetadataUnitTests {
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsClassIfNoIdField() throws Exception {
|
||||
|
||||
new MongoEntityInformation<InvalidPerson>(InvalidPerson.class);
|
||||
new MongoEntityInformation<InvalidPerson, Long>(InvalidPerson.class);
|
||||
}
|
||||
|
||||
class Person {
|
||||
|
||||
@@ -20,8 +20,12 @@
|
||||
<constructor-arg ref="mongoConverter"/>
|
||||
</bean>
|
||||
|
||||
<bean id="mappingContext" class="org.springframework.data.document.mongodb.mapping.MongoMappingContext">
|
||||
<constructor-arg ref="mongoTemplate"/>
|
||||
<bean id="mappingContext" class="org.springframework.data.mapping.BasicMappingContext">
|
||||
<property name="mappingConfigurationBuilder">
|
||||
<bean class="org.springframework.data.document.mongodb.mapping.MongoMappingConfigurationBuilder">
|
||||
<constructor-arg ref="mongoTemplate"/>
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean class="org.springframework.data.document.mongodb.MongoExceptionTranslator"/>
|
||||
|
||||
@@ -7,11 +7,15 @@ Import-Package:
|
||||
Import-Template:
|
||||
org.springframework.beans.*;version="[3.0.0, 4.0.0)",
|
||||
org.springframework.core.*;version="[3.0.0, 4.0.0)",
|
||||
org.springframework.context.*;version="[3.0.0, 4.0.0)",
|
||||
org.springframework.dao.*;version="[3.0.0, 4.0.0)",
|
||||
org.springframework.util.*;version="[3.0.0, 4.0.0)",
|
||||
org.springframework.jmx.export.*;version="[3.0.0, 4.0.0)",
|
||||
org.springframework.transaction.*;version="[3.0.0, 4.0.0)",
|
||||
org.springframework.data.core.*;version="[1.0.0, 2.0.0)",
|
||||
org.springframework.data.annotation.*;version="[1.0.0, 2.0.0)",
|
||||
org.springframework.data.mapping.*;version="[1.0.0, 2.0.0)",
|
||||
org.springframework.data.mapping.model.*;version="[1.0.0, 2.0.0)",
|
||||
org.springframework.data.domain.*;version="[1.0.0, 2.0.0)",
|
||||
org.springframework.data.document.*;version="[1.0.0, 2.0.0)",
|
||||
org.springframework.data.repository.*;version="[1.0.0, 2.0.0)",
|
||||
|
||||
Reference in New Issue
Block a user