Announcement Announcement Module
Collapse
No announcement yet.
Spring not using my custom converter Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • 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
    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.

    Comment


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

      Comment


      • #4
        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.

        Comment


        • #5
          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, 12:03 PM.

          Comment


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

            Comment


            • #7
              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, 02:36 AM. Reason: posting the solution

              Comment


              • #8
                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

                Comment


                • #9
                  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?

                  Comment


                  • #10
                    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> {…".

                    Comment


                    • #11
                      Is anyone using base-package to register converter?

                      I have a workable version for converter registion:
                      Code:
                      	<mongo:mapping-converter >
                      		<mongo:custom-converters>
                      			<mongo:converter>
                      				<bean class="com.abc.Converter.mongo.DBObjectToDateTime" />
                      			</mongo:converter>
                      			<mongo:converter>
                      				<bean class="com.abc.Converter.mongo.DateTimeToDBObject" />
                      			</mongo:converter>
                      		</mongo:custom-converters>
                      	</mongo:mapping-converter>
                      
                      	<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
                      		<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
                      		<constructor-arg name="mongoConverter" ref="mappingConverter" />
                      	</bean>
                      but if I am trying scanning style for converter registration, it doesn't work until now:
                      Code:
                      	<mongo:mapping-converter base-package="com.abc.*.entities">
                      		<mongo:custom-converters base-package="com.abc.Converter.mongo" />
                      	</mongo:mapping-converter>
                      
                      	<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
                      		<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
                      		<constructor-arg name="mongoConverter" ref="mappingConverter" />
                      	</bean>
                      where com.abc.*.entities is the place for domain class, and the package com.abc.Converter.mongo is the place for converters like DateTimeToDBObject and DBObjectToDateTime.
                      anything I missed here?

                      Comment


                      • #12
                        Can we register converter factory?

                        for example, if we want mongodb only store the id of some part of entities, the converter seems like:
                        Code:
                        public final class DBObjectToEntityFactory implements ConverterFactory<DBObject, BaseEntity> {
                        	
                        	public <T extends BaseEntity> Converter<DBObject, T> getConverter(Class<T> targetType) {
                        		return new DBObjectToManagedEntityConverter<T>();
                        	}
                        
                        	private final class DBObjectToManagedEntityConverter<T extends BaseEntity> implements Converter<DBObject, T> {
                        		@Autowired CrudRepository<T, Long> repo;
                        
                        		public T convert(DBObject source){
                        			return (T) repo.findOne((Long) source.get("id"));
                        		}
                        	}
                        
                        }
                        
                        public class EntityToDBObject implements Converter<BaseEntity, DBObject> {
                        
                        	public DBObject convert(BaseEntity source) {
                        		DBObject dbo = new BasicDBObject();
                        		dbo.put("id", source.getId());
                        		return dbo;
                        	}
                        
                        }

                        Comment

                        Working...
                        X