Skip to content

Commit f21f25f

Browse files
author
Thomas Risberg
committed
DATADOC-48 simplified cross-store persistence of @document annotated fields in a JPA entity
1 parent 74df434 commit f21f25f

File tree

17 files changed

+547
-574
lines changed

17 files changed

+547
-574
lines changed

spring-data-mongodb-cross-store/pom.xml

+14-4
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,30 @@
2121
<groupId>org.springframework</groupId>
2222
<artifactId>spring-tx</artifactId>
2323
</dependency>
24+
<dependency>
25+
<groupId>org.springframework</groupId>
26+
<artifactId>spring-aspects</artifactId>
27+
<version>${org.springframework.version}</version>
28+
</dependency>
2429
<dependency>
2530
<groupId>org.springframework</groupId>
2631
<artifactId>spring-orm</artifactId>
2732
</dependency>
33+
<dependency>
34+
<groupId>org.springframework</groupId>
35+
<artifactId>spring-test</artifactId>
36+
<scope>test</scope>
37+
</dependency>
2838

2939
<!-- Spring Data -->
3040
<dependency>
3141
<groupId>org.springframework.data</groupId>
3242
<artifactId>spring-data-commons-core</artifactId>
3343
</dependency>
34-
<dependency>
44+
<!-- <dependency>
3545
<groupId>org.springframework.data</groupId>
3646
<artifactId>spring-data-commons-aspects</artifactId>
37-
</dependency>
47+
</dependency> -->
3848
<dependency>
3949
<groupId>org.springframework.data</groupId>
4050
<artifactId>spring-data-mongodb</artifactId>
@@ -197,10 +207,10 @@
197207
<groupId>org.springframework</groupId>
198208
<artifactId>spring-aspects</artifactId>
199209
</aspectLibrary>
200-
<aspectLibrary>
210+
<!-- <aspectLibrary>
201211
<groupId>org.springframework.data</groupId>
202212
<artifactId>spring-data-commons-aspects</artifactId>
203-
</aspectLibrary>
213+
</aspectLibrary> -->
204214
</aspectLibraries>
205215
<source>1.6</source>
206216
<target>1.6</target>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.springframework.data.persistence.document;
2+
3+
import org.springframework.data.support.ChangeSetBacked;
4+
5+
public interface DocumentBacked extends ChangeSetBacked {
6+
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package org.springframework.data.persistence.document;
2+
3+
//public class DocumentBackedTransactionSynchronization {
4+
5+
import org.apache.commons.logging.Log;
6+
import org.apache.commons.logging.LogFactory;
7+
import org.springframework.data.support.ChangeSetBacked;
8+
import org.springframework.data.support.ChangeSetPersister;
9+
import org.springframework.transaction.support.TransactionSynchronization;
10+
11+
public class DocumentBackedTransactionSynchronization implements TransactionSynchronization {
12+
13+
protected final Log log = LogFactory.getLog(getClass());
14+
15+
private ChangeSetPersister<Object> changeSetPersister;
16+
17+
private ChangeSetBacked entity;
18+
19+
private int changeSetTxStatus = -1;
20+
21+
public DocumentBackedTransactionSynchronization(ChangeSetPersister<Object> changeSetPersister, ChangeSetBacked entity) {
22+
this.changeSetPersister = changeSetPersister;
23+
this.entity = entity;
24+
}
25+
26+
@Override
27+
public void afterCommit() {
28+
log.debug("After Commit called for " + entity);
29+
changeSetPersister.persistState(entity.getClass(), entity.getChangeSet());
30+
changeSetTxStatus = 0;
31+
}
32+
33+
@Override
34+
public void afterCompletion(int status) {
35+
log.debug("After Completion called with status = " + status);
36+
if (changeSetTxStatus == 0) {
37+
if (status == STATUS_COMMITTED) {
38+
// this is good
39+
log.debug("ChangedSetBackedTransactionSynchronization completed successfully for " + this.entity);
40+
}
41+
else {
42+
// this could be bad - TODO: compensate
43+
log.error("ChangedSetBackedTransactionSynchronization failed for " + this.entity);
44+
}
45+
}
46+
}
47+
48+
@Override
49+
public void beforeCommit(boolean readOnly) {
50+
}
51+
52+
@Override
53+
public void beforeCompletion() {
54+
}
55+
56+
@Override
57+
public void flush() {
58+
}
59+
60+
@Override
61+
public void resume() {
62+
throw new IllegalStateException("ChangedSetBackedTransactionSynchronization does not support transaction suspension currently.");
63+
}
64+
65+
@Override
66+
public void suspend() {
67+
throw new IllegalStateException("ChangedSetBackedTransactionSynchronization does not support transaction suspension currently.");
68+
}
69+
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package org.springframework.data.persistence.document.mongo;
2+
3+
import com.mongodb.BasicDBObject;
4+
import com.mongodb.DBCollection;
5+
import com.mongodb.DBObject;
6+
import com.mongodb.MongoException;
7+
import org.apache.commons.logging.Log;
8+
import org.apache.commons.logging.LogFactory;
9+
import org.springframework.beans.factory.annotation.Autowired;
10+
import org.springframework.dao.DataAccessException;
11+
import org.springframework.dao.DataIntegrityViolationException;
12+
import org.springframework.data.document.mongodb.CollectionCallback;
13+
import org.springframework.data.document.mongodb.MongoTemplate;
14+
import org.springframework.data.support.ChangeSet;
15+
import org.springframework.data.support.ChangeSetBacked;
16+
import org.springframework.data.support.ChangeSetPersister;
17+
import org.springframework.util.ClassUtils;
18+
19+
public class MongoChangeSetPersister implements ChangeSetPersister<Object> {
20+
21+
private static final String ENTITY_CLASS = "_entity_class";
22+
23+
private static final String ENTITY_ID = "_entity_id";
24+
25+
private static final String ENTITY_FIELD_NAME = "_entity_field_name";
26+
27+
private static final String ENTITY_FIELD_CLASS = "_entity_field_class";
28+
29+
protected final Log log = LogFactory.getLog(getClass());
30+
31+
private MongoTemplate mongoTemplate;
32+
33+
public void setMongoTemplate(MongoTemplate mongoTemplate) {
34+
this.mongoTemplate = mongoTemplate;
35+
}
36+
37+
@Override
38+
public void getPersistentState(Class<? extends ChangeSetBacked> entityClass,
39+
Object id, final ChangeSet changeSet) throws DataAccessException,
40+
NotFoundException {
41+
String collName = getCollectionNameForEntity(entityClass);
42+
43+
final DBObject dbk = new BasicDBObject();
44+
dbk.put(ENTITY_ID, id);
45+
dbk.put(ENTITY_CLASS, entityClass.getName());
46+
mongoTemplate.execute(collName, new CollectionCallback<Object>() {
47+
@Override
48+
public Object doInCollection(DBCollection collection)
49+
throws MongoException, DataAccessException {
50+
for (DBObject dbo : collection.find(dbk)) {
51+
String key = (String) dbo.get(ENTITY_FIELD_NAME);
52+
String className = (String) dbo.get(ENTITY_FIELD_CLASS);
53+
if (className == null) {
54+
throw new DataIntegrityViolationException(
55+
"Unble to convert property " + key
56+
+ ": Invalid metadata, " + ENTITY_FIELD_CLASS + " not available");
57+
}
58+
Class<?> clazz = null;
59+
try {
60+
clazz = Class.forName(className);
61+
} catch (ClassNotFoundException e) {
62+
throw new DataIntegrityViolationException(
63+
"Unble to convert property " + key + " of type " + className, e);
64+
}
65+
Object value = mongoTemplate.getConverter().read(clazz, dbo);
66+
changeSet.set(key, value);
67+
}
68+
return null;
69+
}
70+
});
71+
}
72+
73+
@Override
74+
public Object getPersistentId(Class<? extends ChangeSetBacked> entityClass,
75+
ChangeSet cs) throws DataAccessException {
76+
log.debug("getPersistentId called on " + entityClass);
77+
if (cs == null) {
78+
return null;
79+
}
80+
if (cs.getValues().get(ChangeSetPersister.ID_KEY) == null) {
81+
// Not yet persistent
82+
return null;
83+
}
84+
Object o = cs.getValues().get(ChangeSetPersister.ID_KEY);
85+
return o;
86+
}
87+
88+
@Override
89+
public Object persistState(Class<? extends ChangeSetBacked> entityClass,
90+
ChangeSet cs) throws DataAccessException {
91+
log.debug("Flush: changeset: " + cs.getValues().keySet());
92+
93+
String collName = getCollectionNameForEntity(entityClass);
94+
DBCollection dbc = mongoTemplate.getCollection(collName);
95+
if (dbc == null) {
96+
dbc = mongoTemplate.createCollection(collName);
97+
}
98+
for (String key : cs.getValues().keySet()) {
99+
if (key != null && !key.startsWith("_") && !key.equals(ChangeSetPersister.ID_KEY)) {
100+
Object value = cs.getValues().get(key);
101+
final DBObject dbQuery = new BasicDBObject();
102+
dbQuery.put(ENTITY_ID, cs.getValues().get(ChangeSetPersister.ID_KEY));
103+
dbQuery.put(ENTITY_CLASS, entityClass.getName());
104+
dbQuery.put(ENTITY_FIELD_NAME, key);
105+
dbQuery.put(ENTITY_FIELD_CLASS, value.getClass().getName());
106+
DBObject dbId = mongoTemplate.execute(collName,
107+
new CollectionCallback<DBObject>() {
108+
@Override
109+
public DBObject doInCollection(DBCollection collection)
110+
throws MongoException, DataAccessException {
111+
return collection.findOne(dbQuery);
112+
}
113+
});
114+
final DBObject dbDoc = new BasicDBObject();
115+
mongoTemplate.getConverter().write(value, dbDoc);
116+
dbDoc.putAll(dbQuery);
117+
if (dbId != null) {
118+
dbDoc.put("_id", dbId.get("_id"));
119+
}
120+
mongoTemplate.execute(collName, new CollectionCallback<Object>() {
121+
@Override
122+
public Object doInCollection(DBCollection collection)
123+
throws MongoException, DataAccessException {
124+
collection.save(dbDoc);
125+
return null;
126+
}
127+
});
128+
}
129+
}
130+
return 0L;
131+
}
132+
133+
private String getCollectionNameForEntity(
134+
Class<? extends ChangeSetBacked> entityClass) {
135+
return ClassUtils.getQualifiedName(entityClass);
136+
}
137+
138+
}

0 commit comments

Comments
 (0)