How to get Spring Data to read documents from Mongo that don't have _class attributes - spring

MongoTemplate inserts an attribute named "_class" into anything it puts into Mongo, and there are ways to turn that off.
However, it seems to be unwilling to read anything back out of it that doesn't have a _class attribute. Simply removing that from the mongo document appears to make it inaccessible. Since reading data you didn't write yourself is an obvious use case, I figure I must be missing something here.
I've been attempting to use this:
List<SomeClass> list = mongoTemplate.findAll(SomeClass.class, "someCollection");
...where SomeClass is annotated with #Id and #Document, and the documents in someCollection otherwise correctly map to the object. I can verify this by creating one of these objects in code, using insert to get it into Mongo, and then see that I can read it back out again.
This works just fine if _class is there but fails if it is not. I do not care about polymorphism or anything that might actually need this attribute. How can I get MongoTemplate to read data that it didn't itself write?

Okay, I found the answer...DavidA's advice was correct but missing the crucial part: when you set up the MappingMongoConverter dealio with the null type mapping business, it not only stops writing the "_class" pollution, but also stops attempting to read it. This causes it to fall back to the type you provide when attempting to retrieve your documents from Mongo.
I haven't seen anywhere that anyone actually mentions that. :)
So, for anybody else running into this issue, here's the XML configuration I used (adapted from something I found somewhere else here on StackOverflow, but I lost the link, sorry):
<mongo:db-factory id="mongoDbFactory" host="${mongo.host}" port="${mongo.port}" dbname="${mongo.dbname}"/>
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
<constructor-arg name="mongoConverter" ref="mongoConverter" />
<property name="writeResultChecking" value="EXCEPTION" />
</bean>
<bean id="mongoTypeMapper" class="org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper">
<constructor-arg name="typeKey"><null/></constructor-arg>
</bean>
<bean id="mongoMappingContext" class="org.springframework.data.mongodb.core.mapping.MongoMappingContext" />
<bean id="mongoConverter" class="org.springframework.data.mongodb.core.convert.MappingMongoConverter">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
<constructor-arg name="mappingContext" ref="mongoMappingContext" />
<property name="typeMapper" ref="mongoTypeMapper"></property>
</bean>
And then in the Java code:
//build query object
UnifiedProduct mpp = mongoTemplate.findOne(query, UnifiedProduct.class, "collection-name");
...which results in the UnifiedProduct class I wanted, and no annoying "_class" pollution.

Spring's MappingMongoConverter uses a MongoTypeMapper in order to figure out what type to use when reading in a DBObject from the database. The DefaultMongoTypeMapper uses the "_class" attribute to work as you have described.
You should be able to implement your own MongoTypeMapper and tell the MappingMongoConberter to use it. Your version can use other indicators instead of "_class" value to determine what type should be created when reading.

Related

Spring Data RedisTemplate: Serializing the Value and HashValue

I tried following this tutorial:
http://javakart.blogspot.in/2012/12/spring-data-redis-hello-world-example.html
My question is related to this:
Weird redis key with spring data Jedis
I was able to solve the keys and hashkeys using the StringRedisSerializer .
<bean
id="stringRedisSerializer"
class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
<bean
id="redisTemplate"
class="org.springframework.data.redis.core.RedisTemplate"
p:connection-factory-ref="jedisConnectionFactory"
p:keySerializer-ref="stringRedisSerializer"
p:hashKeySerializer-ref="stringRedisSerializer"
/>
However I found it a problem using a serializer for the value and hashvalue.
I tried adding this:
p:valueSerializer-ref="stringRedisSerializer"
p:hashValueSerializer-ref="stringRedisSerializer"
But an error prompted:
"User cannot be cast to java.lang.String"
Can anyone share how to use a serializer for the value/hashvalue?
Redis stores keys and values as string. It's up to your persistence layer to handle the parsing. In the example, User is a POJO and not a String. I suggest that you use JacksonJsonRedisSerializer instead of StringRedisSerializer. This way you're storing json as your value.
<bean id="userJsonRedisSerializer"
class="org.springframework.data.redis.serializer.JacksonJsonRedisSerializer">
<constructor-arg type="java.lang.Class" value="com.mycompany.redis.domain.User"/>
</bean>

Spring Configuration Query

I have one spring configuration file with entry like below...
<bean id="beanId" class="a.b.c.d.MyBean">
<property name="firstProperty" value="report_{date}.xls"/>
</bean>
Somewhere in my java code, I am fetching this bean and then its property "firstProperty" later.
I am little curious, when I get the value of property "firstProperty" I get report_.xls i.e report_20130307.xls
I have searched all my code including bundles, xmls but not clear that where we are setting {date} with todays timestamp.
Do you have any clue where we can do this?
Thanks
Jai
It is the property-placeholder mechanism.
Read more on http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/xsd-config.html#xsd-config-body-schemas-context-pphc.
In most of the cases, the values to property are set from properties file using expression language. Like
<bean id="dataSource" class="a.b.c.d.DataSource">
<property name="databaseUrl" value="{db.url}"/>
</bean>
Or if the property is a ref to another bean, e.g. Object B is member variable of Object A.
<bean id="refA" class="a.b.c.d.A">
<property name="b" ref="refB"/>
</bean>
<bean id="refB" class="a.b.c.d.B">
</bean>
Its quite simple guys...as we know setter are called for each property. So same in my case,
In bean we are setting variable "firstProperty" + today timestamp like below.
public void setfirstProperty(String firstProperty) {
this.firstProperty = firstProperty + <methodToReplaceDateStringWithTimeStamp>;
}
Thanks
Jai

Bean configuration for Apache Extras Cassandra-JDBC

I looked around their site http://code.google.com/a/apache-extras.org/p/cassandra-jdbc/ but couldn't find any documentation for configuring a data source bean such as
<spring:bean id="mySqlDataSource"
class="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource">
<spring:property name="url"
value="jdbc:mysql://${mysql.host}:${mysql.port}/${mysql.database}?user=${mysql.user}&password=${mysql.pwd}" />
</spring:bean>
Is there any documentation anywhere or does anyone know how to write one?
The CassandraDataSource doesn't seem to have a default constructor, so you'll need to pass everything in:
<spring:bean id="cassDataSource"
class="org.apache.cassandra.cql.jdbc.CassandraDataSource">
<constructor-arg value="host">
<constructor-arg type="int" value="port">
<constructor-arg value="keyspace">
<constructor-arg value="user">
<constructor-arg value="password">
<constructor-arg value="version">
</spring:bean>
(I haven't tested this, but it's basically what you'd need to do).
I found this on Google, and the info is slightly out of date, so I thought I'd update it. The arguments have changed. The best way I found to figure out what arguments it's expecting is to use the source, which you can get off of GitHub using an address like:
https://code.google.com/a/apache-extras.org/p/cassandra-jdbc/source/browse/src/main/java/org/apache/cassandra/cql/jdbc/CassandraDataSource.java?name=v1.2.5
There's a selector on that page where you can pick the version number you downloaded (that link brings you to 1.2.5). You need to satisfy all the constructor arguments.

How to refer to the returned type

I want to create a spring bean as below.
<bean id="qNameString" class="javax.xml.xpath.XPathConstants.STRING"/>
Here I want the reference to return type which is a QName but I understand the way I referred is wrong. Can someone please help on this.
That won't work, because class="javax.xml.xpath.XPathConstants.STRING" makes no sense, since what you're referring to isn't a class.
You can refer to static fields using <util:constant>, as documented here:
<property name="...">
<util:constant static-field="javax.xml.xpath.XPathConstants.STRING"/>
</property>
Spring can create a QName for you like this:
<bean id="qName" class="java.xml.namespace.QName">
<constructor index="0" value="localpart"/>
<constructor index="1" value="namespaceURI"/>
</bean>
Replace localpart and namespaceURI with the local name and namespace.
To reference a constant in a class, like javax.xml.xpath.XPathConstants.STRING
<bean id="qNameString" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
<property name="targetField" value="javax.xml.xpath.XPathConstants.STRING"/>
</bean>
A shorter version is available with the util schema:
<util:constant static-field="java.xml.xpath.XPathConstants.STRING"/>
Apart from being shorter, the id of the bean will be java.xml.xpath.XPathConstants.STRING rather than qNameString.
See FieldRetrievingFactoryBean and The util schema

How to do spring request parameter conversion

In a Spring 3 based web (portlet) application I have a controller with a method like this:
#RenderMapping
public ModelAndView handleRenderRequest(...,#RequestParam MyClass myObject)
{
...
}
Now I wonder: How do I tell spring how to convert the request parameter to MyClass. I found information about property editors and about the Converter interface and there seem to be some implications that Converter is the successor of the property editor, but nobody seems to like being explicit about it.
I implemented the converter interface for String to MyClass conversion. But how do I tell Spring about it? I am using annotation based configuration wherever possible, so I checked whether spring will detect the Converter from my classpath automatically, but it does not.
So thought that the part Configuring a ConversionService from the manual wants to tell me that I've got to add the following to my applicationContext.xml which I did:
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="some.package.MyConverter"/>
</list>
</property>
</bean>
Bit still:
org.springframework.beans.ConversionNotSupportedException: Failed to
convert value [...]
So what am I missing? And is there a way, to just configure a package and let spring scan this package for converters and register them automatically? And say that in one certain method I want to use a different converter than in all other methods. For example I want an integer that has a Luhn-Checksum to be checked and the checksum removed, how can I do that? Something like #RequestParam(converter=some.package.MyConverter.class) would be great.
EDIT
Ok, I just caught in the documentation:
Use the Formatter SPI when you're working in a client environment,
such as a web application, and need to parse and print localized field
values
So I guess that means I should use the Formatter SPI, yet a third possibility next to property editors and converters (I think I could really to with a comparison table or the like). I did implement the Parser interface as well and tried to register my converter using:
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="some.package.SortOrderEnumConverterSpring"/>
</set>
</property>
</bean>
As you can see I used "set" instead of "list" for specifying the converters. I set a debugging breakpoint in the FormattingConversionServiceFactoryBean.setConverters method which did not fire upon using list, but it did fire on using set.
Additionally I added
<mvc:annotation-driven conversion-service="conversionService"/>
And the namespace for the mvc-prefix to my applicationContext. But still I get the conversion not supported exception.
I also tried going back to the converter approach and changed in my applicationContext.xml file the parameter list for converters from list to set, but that did not change anything either.
EDIT2
As digitaljoel pointed out it is possible to set different converters per controller using an initBinder method. I applied this to my controller:
#Autowired
private ConversionService conversionService;
#InitBinder
public void initBinder(WebDataBinder binder)
{
binder.setConversionService(conversionService);
}
And in my applicationContext.xml:
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="some.package.with.MyConverter"/>
</set>
</property>
</bean>
And all suddenly the conversion works just fine :-). But I am not quite happy about having to apply this to each and every of my controllers. There must be a way to just set it in my applicationContext for everyone, is there not? Good to know that I can override default if I need to (after all I asked for that), but I still want to set defaults.
And what about the Formatter stuff. Shouldn't I be using that instead of Converter?
Spring Portlet MVC 3.0 does not support
<mvc:annotation-driven conversion-service="conversionService"/>
Visit https://jira.springsource.org/browse/SPR-6817 for more info about this.
However you can add this to your common applicationContext
<bean
class="org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="webBindingInitializer">
<bean
class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="conversionService">
<list>
<ref bean="conversionService" />
</list>
</property>
</bean>
</property>
</bean>
This way you do not need add #InitBinder to every single controller
and of course
<bean id="conversionService"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<list>
<!-- converter implementations here -->
</list>
</property>
</bean>
You are correct that Converter (and ConverterFactory) are the successors to property editors. Your problem may be that you are not accepting the appropriate type as a parameter to your converter, but that's hard to say without seeing the converter code. If you are expecting Long or Integer you may actually be getting a String from Spring and need to perform that key conversion yourself first.
As for configuration, I believe you need to list all of your converters in the bean configuration in your xml. If you annotate your converter implementation with #Component you might be able to reference it by the bean name instead of the fully qualified path, but I have only tried that for a ConverterFactory, not a Converter.
Finally, on specific converters, it looks like you may be able to configure the conversion service at the controller level (see Javi's answer on Setting up a mixed configuration for annotation-based Spring MVC controllers ) and then you could just place that method (and others that require that controller) into a controller that uses a secondary conversion service which you ought to be able to inject by name with the #Resource annotation.
Implement a WebArgumentResolver:
public class MyArgumentResolver implements WebArgumentResolver
{
#Override
public Object resolveArgument(MethodParameter methodParameter,
NativeWebRequest webRequest) throws Exception
{
Class<?> paramType = methodParameter.getParameterType();
if (paramType == MyClass.class)
{
String parameterName = methodParameter.getParameterName();
String stringParameter = webRequest.getParameter(parameterName);
return convert(stringParameter);
}
return UNRESOLVED;
}
}
And register it in your applicationContext.xml:
<bean class="org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="customArgumentResolver">
<bean class="com.dshs.eakte.util.MyArgumentResolver" />
</property>
</bean>
This works and even has the advantage of allowing parameter conversion that is based on multiple method parameters.
To achieve something similar to what you're doing, I found this blog entry useful.
i think you need to use something like
public ModelAndView handleRenderRequest(...,#ModelAttribute("myObject") MyClass myObject)

Resources