Page 1 of 2 12 LastLast
Results 1 to 10 of 12

Thread: Spring not using my custom converter

  1. #1
    Join Date
    Apr 2010
    Posts
    12

    Default Spring not using my custom converter

    I have been trying to register my own converter for reads and unfortunately it is not getting used. I can see it getting registered but it never actually calls the reader. Here are the corresponding files:
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mongo="http://www.springframework.org/schema/data/mongo"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
       	http://www.springframework.org/schema/data/mongo
        http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd">
    
    	<mongo:repositories base-package="com.tot.model.repositories" />
    	<mongo:mongo host="localhost" port="27017" />
    	<mongo:db-factory dbname="database" mongo-ref="mongo" />
    
    	<mongo:mapping-converter id="mappingConverter" base-package="com.tot.model">
    		<mongo:custom-converters>
    			<mongo:converter ref="readConverter" />
    		</mongo:custom-converters>
    	</mongo:mapping-converter>
    
    
    	<bean class="org.springframework.data.document.mongodb.MongoExceptionTranslator" />
    	<!-- To translate any MongoExceptions thrown in @Repository annotated classes -->
    	<bean id="mongo" class="org.springframework.data.document.mongodb.MongoFactoryBean">
    		<property name="host" value="localhost" />
    		<property name="port" value="27017" />
    	</bean>
    	<bean class="org.springframework.data.document.mongodb.repository.MongoRepositoryFactoryBean">
    		<property name="template" ref="mongoTemplate" />
    		<property name="repositoryInterface" value="com.tot.model.dao.repository.FeedItemDaoRepository" />
    	</bean>
    	<bean id="mongoTemplate" class="org.springframework.data.document.mongodb.MongoTemplate">
    		<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
    		<constructor-arg name="mongoConverter" ref="mappingConverter" />
    
    	</bean>
    	<bean id="readConverter" class="com.tot.model.dao.repository.util.UserPointsReadConverter" />
    	<bean class="org.springframework.data.document.mongodb.mapping.event.LoggingEventListener" />
    
    
    </beans>


    Code:
    package com.tot.model.dao.repository.util;
    
    import org.springframework.core.convert.converter.Converter;
    
    import com.mongodb.DBObject;
    import com.tot.model.UserPoints;
    
    public class UserPointsReadConverter implements Converter<DBObject, UserPoints> {
    
    	@Override
    	public UserPoints convert(DBObject source) {
    		UserPoints userPoints = new UserPoints();
    		userPoints.setId((Long) source.get("_id"));
    		userPoints.setPoints((Integer) source.get("value.points"));
    		return userPoints;
    	}
    }
    Code:
    package com.tot.model;
    
    import javax.persistence.Transient;
    
    import org.springframework.data.document.mongodb.index.Indexed;
    import org.springframework.data.document.mongodb.mapping.Document;
    import org.springframework.data.annotation.Id;
    
    @Document
    public class UserPoints {
    
    	@Id
    	private Long id;
    	@Transient
    	private User user;
    	@Indexed
    	private Integer points;
    
    	public User getUser() {
    		return user;
    	}
    
    	public void setUser(User user) {
    		this.user = user;
    	}
    
    	public Integer getPoints() {
    		return points;
    	}
    
    	public void setPoints(Integer points) {
    		this.points = points;
    	}
    
    	public Long getId() {
    		return id;
    	}
    
    	public void setId(Long id) {
    		this.id = id;
    	}
    
    }
    Code:
     INFO [2011-06-03 13:50:05,228] [TP-Processor3] (LoggingEventListener.java:50) - onAfterLoad: { "_id" : 13849 , "value" : { "count" : 1402.0 , "points" : 1498.0}}
     INFO [2011-06-03 13:50:05,229] [TP-Processor3] (LoggingEventListener.java:55) - onAfterConvert: { "_id" : 13849 , "value" : { "count" : 1402.0 , "points" : 1498.0}}, com.tot.model.UserPoints@7c02a5e1
    I can see this data coming back but my reader never gets called. Spring just does it's best to convert it.

  2. #2

    Default You need Read AND Write Converter

    You cant't only declare on part. It needs to be pair.
    But if you want to change something after read try to use the "MappingEventListener"

    Code:
    public class DoSomethingAfterReadListener extends AbstractMappingEventListener<MongoMappingEvent<UserPoints>, UserPoints> {
        
        @Override
       public void onAfterConvert(DBObject dbo, UserPoints source) {
           // TODO Auto-generated method stub
       }
       
       @Override
       public void onBeforeConvert(UserPoints source) {
           // TODO Auto-generated method stub
       }
        
    }
    define this bean simply in your application context. done.

  3. #3
    Join Date
    Aug 2011
    Posts
    4

    Default

    Has anyone gotten this working? Running into this when trying to convert BigDecimals when read/writing to mongo.

  4. #4
    Join Date
    Apr 2010
    Posts
    12

    Default

    My problem was that I didn't define both the read and write conversion strategy. Since I only needed read I tried to write that but it needs both to work. Let me know if you need more information. If it's still not working post an example.

  5. #5
    Join Date
    Aug 2011
    Posts
    4

    Default

    Did you use a MappingEventListener like was suggested above? The Java side of it is pretty straightforward so I'm thinking I have a Spring config issue.

    Here's my appContext.xml:
    Code:
        <mongo:mongo 
            host="localhost" 
            port="1234" />
    
        <mongo:db-factory 
            dbname="solar" 
            mongo-ref="mongo"/>
    
        <mongo:mapping-converter>
          <mongo:custom-converters>
            <mongo:converter>
              <bean class="com.mine.BigDecimalReadConverter"/>
            </mongo:converter>
            <mongo:converter>
              <bean class="com.mine.BigDecimalWriteConverter"/>
            </mongo:converter>
          </mongo:custom-converters>
        </mongo:mapping-converter>
    
        <!-- Use this post processor to translate any MongoExceptions thrown in @Repository annotated classes -->
        <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
    Here's how I've defined my converters:
    Code:
    public class BigDecimalReadConverter implements Converter<DBObject, BigDecimal> {
    	public BigDecimal convert(DBObject source) {
    		if(source == null) return null;	
    		BigDecimal bd = new BigDecimal((String)source.get("stringValue"));
    		return bd;
    	}
    }
    
    public class BigDecimalWriteConverter implements Converter<BigDecimal, DBObject> {
    	public DBObject convert(BigDecimal source) {
    		if(source == null) return null;		
    		DBObject dbo = new BasicDBObject();
    	    dbo.put("stringValue", source.toPlainString());
    	    return dbo;
    	}
    }
    And here's my Mongo Java code:
    Code:
    	@Autowired
    	protected MongoTemplate mongoTemplate;
    
    	@Test //running this as a junit test
    	public void testMongoConfig() {
    		save(mongoTemplate);
    	}
    	
    	private void save(MongoOperations mongoOps) {	
    	    MongoTestEntity item = new MongoTestEntity();  //dummy test class
    	    item.setSomeBigDecimal(new BigDecimal(123.321));
    	    mongoOps.insert(item);
    	}
    The stack trace I'm getting is:

    Code:
    java.lang.IllegalArgumentException: Multiple constructors with arguments found in class java.math.BigDecimal! Annotate one with @PreferedConstructor explicitly to select it to be used in persistence operations.
    	at org.springframework.data.mapping.PreferredConstructorDiscoverer.<init>(PreferredConstructorDiscoverer.java:81)
    	at org.springframework.data.mapping.BasicPersistentEntity.<init>(BasicPersistentEntity.java:49)
    	at org.springframework.data.document.mongodb.mapping.BasicMongoPersistentEntity.<init>(BasicMongoPersistentEntity.java:47)
    	at org.springframework.data.document.mongodb.mapping.MongoMappingContext.createPersistentEntity(MongoMappingContext.java:59)
    	at org.springframework.data.document.mongodb.mapping.MongoMappingContext.createPersistentEntity(MongoMappingContext.java:33)
    	at org.springframework.data.mapping.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:164)
    	at org.springframework.data.mapping.AbstractMappingContext$1.doWith(AbstractMappingContext.java:201)
    	at org.springframework.util.ReflectionUtils.doWithFields(ReflectionUtils.java:513)
    	at org.springframework.data.mapping.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:176)
    	at org.springframework.data.mapping.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:133)
    	at org.springframework.data.mapping.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:114)
    	at org.springframework.data.mapping.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:58)
    	at org.springframework.data.document.mongodb.MongoTemplate.determineCollectionName(MongoTemplate.java:1459)
    	at org.springframework.data.document.mongodb.MongoTemplate.determineEntityCollectionName(MongoTemplate.java:1445)
    	at org.springframework.data.document.mongodb.MongoTemplate.insert(MongoTemplate.java:636)
    I'd appreciate any help you can provide.
    Last edited by ebaizel; Aug 20th, 2011 at 12:03 PM.

  6. #6
    Join Date
    Aug 2011
    Posts
    4

    Default

    Or if it's easier, can you post your app.xml config file?

  7. #7
    Join Date
    Aug 2011
    Posts
    4

    Default

    I solved it. The trick is that you have to define the beans in order that they are needed. Here is the app.xml that got working for me:

    Code:
    <bean id="mappingContext" class="org.springframework.data.document.mongodb.mapping.MongoMappingContext"/>
    
    <bean id="readConverter"  class="com.mine.BigDecimalReadConverter"/>
    <bean id="writeConverter"  class="com.mine.BigDecimalWriteConverter"/>
    
    <mongo:mapping-converter id="mappingConverter">
        <mongo:custom-converters>
            <mongo:converter ref="readConverter" />
            <mongo:converter ref="writeConverter" />
        </mongo:custom-converters>
    </mongo:mapping-converter>
    
    <!-- Factory bean that creates the Mongo instance -->
    <mongo:mongo 
        host="${${environment}.mongodb.host}" 
        port="${${environment}.mongodb.port}" />
    
    <mongo:db-factory 
        dbname="${${environment}.mongodb.databaseName}" 
        mongo-ref="mongo"/>   
    
    <bean id="mongoTemplate" class="org.springframework.data.document.mongodb.MongoTemplate">
        <constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
        <constructor-arg name="mongoConverter" ref="mappingConverter"/>
    </bean>
    
    <!-- Use this post processor to translate any MongoExceptions thrown in @Repository annotated classes -->
    <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
    Last edited by ebaizel; Aug 23rd, 2011 at 02:36 AM. Reason: posting the solution

  8. #8
    Join Date
    Apr 2006
    Location
    Dresden, Germany
    Posts
    492

    Default

    There's been major overhauls of custom converter handling since M3. Order shouldn't matter anymore and we have Converters for BigDecimal pre-registered. Feel free to try the latest snapshots.

    Cheers,
    Ollie

  9. #9
    Join Date
    Feb 2012
    Posts
    1

    Default

    I'm using the AbstractMappingEventListener mentioned above, but it currently only hits the "onAfterLoad" method in my listener.

    Code:
    public class ConfigPropertyValueListener extends AbstractMappingEventListener<MongoMappingEvent<ConfigPropertyValue>, ConfigPropertyValue> {
    
       @Override
       public void onAfterConvert(DBObject dbo, ConfigPropertyValue source){
           String name = source.getName().replace("__", ".");
           source.setName(name);
       }
    
       @Override
       public void onBeforeSave(ConfigPropertyValue source, DBObject dbo) {
           String name = source.getName().replace(".", "__");
           source.setName(name);
       }
    
       @Override
       public void onBeforeConvert(ConfigPropertyValue source) {
           System.out.print("test");
       }
    
       @Override
       public void onAfterSave(ConfigPropertyValue source, DBObject dbo) {
           System.out.print("test");
       }
    
       @Override
       public void onAfterLoad(DBObject dbo) {
           System.out.print("test");
       }
    }
    Any thoughts?

  10. #10
    Join Date
    Apr 2006
    Location
    Dresden, Germany
    Posts
    492

    Default

    Please upgrade to a recent version of Spring Data MongoDB. AbstractMappingEventListener is AbstractMongoEventListener for quite a while already. Beyond that, don't type it to the event but rather the domain class your interested in only. So it should be "extends AbstractMongoEventListener<ConfigPropertyValue> {…".

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •