Search code examples
mongodbspring-dataspring-data-jpaspring-data-document

Spring Data Mongodb Cross store:: With MySQL @RelatedDocument(s) not updating


I am using Spring Data Mongodb and Spring data JPA module for MySQL.

I have successfully configured as suggested in the reference document. I am able to save but not able to update the mongodb "RelatedDocuments" though I am successfully able to update the MYSQL fields.

The entity relationship is like

User(Mysql) has Addresses(Mongo) > Has list of Address (quite similar to the survey example in reference.

My situation is exactly similar to http://forum.springsource.org/showthread.php?126897-Using-Spring-Data-with-MongoDB-and-MySQL

The versions I am using are as follows

spring-data.mongodb.version = 1.1.0.M1
spring.version = 3.1.2.RELEASE
    spring.data.jpa.version = 1.1.0.RELEASE

hibernate.entitymanager.version = 4.1.4.Final
hibernate.jpa-api.version = 1.0.1.Final (using JPA 2.0)

    aspectj.version = 1.6.12

Please could somebody point out what I might be missing, config files are as below

<!-- Activate Spring Data JPA repository support -->
<jpa:repositories base-package="com.domain.domain.*.repo" factory-class="com.mydomainit.domain.repo.BaseJpaRepositoryFactoryBean"/>

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" p:persistenceXmlLocation="classpath*:META-INF/persistence.xml"
    p:persistenceUnitName="spring-jpa" p:dataSource-ref="dataSource" p:jpaVendorAdapter-ref="hibernateVendor" />

<bean id="hibernateVendor" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" p:showSql="true" p:generateDdl="false"
    p:database="MYSQL" />

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" p:entityManagerFactory-ref="entityManagerFactory" />

<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager" />

The MongoDB config is as below

<mongo:repositories base-package="com.domain.*.mongorepo" repository-impl-postfix="CustomImpl"
    factory-class="com.domain.mongorepo.CommonMongoRepoFactoryBean" />

<mongo:mongo id="mongoRef" host="${mongo.host.name}" port="${mongo.host.port}">
    <mongo:options connections-per-host="8" threads-allowed-to-block-for-connection-multiplier="4" connect-timeout="1000" max-wait-time="1500"
        auto-connect-retry="true" socket-keep-alive="true" socket-timeout="1500" slave-ok="true" write-number="${mongo.db.w}" write-timeout="${mongo.db.wtimeout}"
        write-fsync="${mongo.db.fsync}" />
</mongo:mongo>

<mongo:db-factory id="mongoDbFactory" dbname="${mongo.db.name}" username="${mongo.db.username}" password="${mongo.db.password}"
    mongo-ref="mongoRef" />

<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate" c:mongoDbFactory-ref="mongoDbFactory" c:mongoConverter-ref="mappingConverter" />

<bean class="org.springframework.data.mongodb.crossstore.MongoDocumentBacking" factory-method="aspectOf">
    <property name="changeSetPersister" ref="mongoChangeSetPersister" />
</bean>
<bean id="mongoChangeSetPersister" class="org.springframework.data.mongodb.crossstore.MongoChangeSetPersister">
    <property name="mongoTemplate" ref="mongoTemplate" />
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<bean class="org.springframework.data.mongodb.core.MongoExceptionTranslator" />

Solution

  • The documentation in Internet is quite old, which makes it is difficult to configure; there are many dependencies between libraries and some apis changed. I managed to make it work as follows. I hope it helps:

    pom.xml

        <spring.version>3.2.0.RELEASE</spring.version>
        <querydsl.version>2.9.0</querydsl.version>
        <spring-data-jpa.version>1.2.0.RELEASE</spring-data-jpa.version>
        <mongodb.version>1.1.1.RELEASE</mongodb.version>
    
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-mongodb</artifactId>
            <version>${mongodb.version}</version>
        </dependency>
        <dependency>
            <groupId>com.mysema.querydsl</groupId>
            <artifactId>querydsl-mongodb</artifactId>
            <version>2.9.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-mongodb-cross-store</artifactId>
            <version>${mongodb.version}</version>
            <exclusions>
                <exclusion>
                    <artifactId>jcl-over-slf4j</artifactId>
                    <groupId>org.slf4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>
    
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.7.0</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjtools</artifactId>
            <version>1.7.0</version>
        </dependency>
    

    databaseContext.xml

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
    <mongo:mongo host="localhost" port="27017" />
    <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
        <constructor-arg ref="mongo" />
        <constructor-arg name="databaseName" value="databasename" />
    </bean>
    <mongo:repositories base-package="com.yourcompany.repository.mongodb" />
    
    <bean class="org.springframework.data.mongodb.core.MongoExceptionTranslator" />
    <bean class="org.springframework.data.mongodb.crossstore.MongoDocumentBacking" factory-method="aspectOf">
        <property name="changeSetPersister" ref="mongoChangeSetPersister" />
    </bean>
    <bean id="mongoChangeSetPersister" class="org.springframework.data.mongodb.crossstore.MongoChangeSetPersister">
        <property name="mongoTemplate" ref="mongoTemplate" />
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
    

    Product Object (to be stored in MySQL)

    @Entity
    public class Product {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id; // MySQL + MongoDB
        private double value; // MySQL
        @RelatedDocument
        private ProductInfo info; // MongoDB
    }
    

    ProductInfo Object (to be stored in MongoDB)

    @Document
    public class ProductInfo {
        @Id
        public ObjectId id;
        private String name;
        private String description;
    }
    

    Product Repository

    @Repository
    public interface ProductRepository extends JpaRepository<Product, Long>{}
    

    JUnit Test

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = { TestApplicationContext.class })
    @TestExecutionListeners({ TransactionalTestExecutionListener.class,
            DependencyInjectionTestExecutionListener.class })
    @TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = false)
    public class JournalMongoDBServiceIT  {
        @Resource
        ProductRepository productRepository;
    
        @Test
        @Transactional
        public void crossStoreProduct(){
            Product product = new Product();
            ProductInfo info = new ProductInfo();
            info.setName("Test");
            info.setDescription("Test Product");
            product.setInfo(info);
            productRepository.save(product);
        }
    
        @Test
        @Transactional
        public void crossStoreProductFindAndUpdate(){
            Product product = productRepository.findOne(32L);
            product.setValue(999L);
            product.getInfo().setDescription("Updated description");
            productRepository.save(product);
        }
    }