I have the following mapping
#Document(indexName = "some-index")
#Data
public class ElasticDocument {
#Id
#Field(type = FieldType.Text)
private String id;
#Field(type = FieldType.Date, format = DateFormat.custom)
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "uuuu-MM-dd'T'HH:mm:ss.SSS")
private LocalDateTime issuedTimestamp;
}
The following repository
#Repository
public interface DocumentRepository extends ElasticsearchRepository<ElasticDocument, String> {
}
But the following query from spring data elastic search 4.0.3.RELEASE throws a conversion error:
Page<ElasticDocument> elasticDocuments = documentRepository.findAll(PageRequest.of(0, 10));
[MappingElasticsearchConverter.java:290] [Type LocalDateTime of
property ElasticDocument.issuedTimestamp is a TemporalAccessor
class but has neither a #Field annotation defining the date type nor a
registered converter for reading! It cannot be mapped from a complex
object in Elasticsearch!
[No converter found capable of converting from type [java.lang.Long]
to type [java.time.LocalDateTime]]
[org.springframework.core.convert.ConverterNotFoundException: No
converter found capable of converting from type [java.lang.Long] to
type [java.time.LocalDateTime]
I'm using elasticsearch 7.9.1 and spring data elasticsearch 4.0.3.RELEASE and from what i understood starting with spring data elasticsearch 4.x we don't need to create a custom conversion as long as i added the Field annotation at mapping
You need to add the pattern for your custom format in the #Field annotation
#Field(type = FieldType.Date, format = DateFormat.custom, pattern = "uuuu-MM-dd'T'HH:mm:ss.SSS")
Since spring-data-elastic version 4.2, dateformat.custom is deprecated. You can use any of the enum date_hour_minute_second_xxx in org.springframework.data.elasticsearch.annotations.DateFormat. E.g
#Field(type = FieldType.Date, format = DateFormat.date_hour_minute_second_millis)
private LocalDateTime createdDate;
In grails 3 is there a way to bind time of format "11:00 AM" to java.sql.Time?
import java.sql.Time
Time startTime
There are several ways to do it. One is to supply an appopriate ValueConverter as described in the Custom Data Converters section at http://docs.grails.org/3.3.8/guide/theWebLayer.html#dataBinding.
That could look something like this...
package com.myapp.converters
import grails.databinding.converters.ValueConverter
import java.sql.Time
/**
* A custom converter which will convert String into a Time object.
*/
class TimeValueConverter implements ValueConverter {
boolean canConvert(value) {
value instanceof String
}
def convert(value) {
// return a new Time instance initialized with the `value` parameter
}
Class<?> getTargetType() {
java.sql.Time
}
}
Then add an instance of that class the Spring application context:
beans = {
timeConverter com.myapp.converters.TimeValueConverter
// ...
}
I initially had my #NodeEntity class having a #GraphProperty of java.util.Date type. The graph repository APIs automatically convert that to long / string value and stores it based on my property type.
As I wanted to import huge chunk of data from csv, I wanted this date field in my custom or pre-defined string format, say "yyyyMMdd". So I wrote my spring converter factories for both String -> Date and DAte -> String and registered them in the spring xml for conversion service. But unfortunately my converter was never called. Only the standard converter was called.
Later, when I wrap the date object inside MyDate class and changed the converter to String -> MyDAte and vice versa instead of java.util.Date, converter was called and the required serialization was done.
I dont want this MyDate wrapper around Date just for the sake of converter. Now, my question is, how to override the default date converter with my converters?
try this:
#Configuration
#EnableNeo4jRepositories
static class TestConfig extends Neo4jConfiguration {
#Bean
GraphDatabaseService graphDatabaseService() {
return new ImpermanentGraphDatabase();
}
#Bean
protected ConversionService neo4jConversionService() throws Exception {
ConversionService conversionService = super.neo4jConversionService();
ConverterRegistry registry = (ConverterRegistry) conversionService;
registry.removeConvertible(Date.class, String.class);
registry.removeConvertible(String.class, Date.class);
//add your own converters like this
registry.addConverter(new MyDateToStringConverter());
registry.addConverter(new MyStringToDateConverter());
return conversionService;
}
}
I have the following classes
#XmlRootElement(name = "ExecutionRequest")
#XmlAccessorType(XmlAccessType.FIELD)
public class ExecutionRequest {
#XmlElement(name="Command")
private String command;
#XmlElementWrapper(name="ExecutionParameters")
#XmlElement(name="ExecutionParameter")
private ArrayList<ExecutionParameter> ExecutionParameters;
}
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class ExecutionParameter {
#XmlElement(name = "Key")
private String key;
#XmlElement(name = "Value")
private String value;
}
and when I marshall the ExecutionRequest object, I get the following XML -
<ExecutionRequest>
<Command>RetrieveHeader</Command>
<ExecutionParameters>
<ExecutionParameter>
<Key>tid</Key>
<Value>ASTLGA-ALTE010220101</Value>
</ExecutionParameter>
<ExecutionParameter>
<Key>ctag</Key>
<Value>dq</Value>
</ExecutionParameter>
</ExecutionParameters>
</ExecutionRequest>
It is working fine as per JAXB binding.
But I want the XML to have all key value collection within one ExecutionParameter like -
<ExecutionRequest>
<Command>RetrieveHeader</Command>
<ExecutionParameters>
<ExecutionParameter>
<Key>tid</Key>
<Value>ASTLGA-ALTE010220101</Value>
<Key>ctag</Key>
<Value>dq</Value>
</ExecutionParameter>
</ExecutionParameters>
</ExecutionRequest>
Is there any way to obtain xml like this by changing annotation.
Let me know in case of clarifications.
Thanks in advance.
There isn't metadata for that. You could get a compact XML representation (that is easily parseable) by mapping key and value with #XmlAttribute.
<ExecutionParameters>
<ExecutionParameter Key="a" Value="b"/>
<ExecutionParameter Key="c" Value="d"/>
</ExecutionParameters>
UPDATE
If you have to support this XML format, then you could use JAXB with XSLT to get the desired result:
// Create Transformer
TransformerFactory tf = TransformerFactory.newInstance();
StreamSource xslt = new StreamSource(
"src/example/stylesheet.xsl");
Transformer transformer = tf.newTransformer(xslt);
// Source
JAXBContext jc = JAXBContext.newInstance(ExecutionRequest.class);
JAXBSource source = new JAXBSource(jc, request);
// Result
StreamResult result = new StreamResult(System.out);
// Transform
transformer.transform(source, result);
For More Information
http://blog.bdoughan.com/2012/11/using-jaxb-with-xslt-to-produce-html.html
Is there any way I can override the value of dateCreated field in my domain class without turning off auto timestamping?
I need to test controller and I have to provide specific domain objects with specific creation date but GORM seems to override values I provide.
Edit
My classes look like this:
class Message {
String content
String title
User author
Date dateCreated
Date lastUpdated
static hasMany = [comments : Comment]
static constraints = {
content blank: false
author nullable: false
title nullable: false, blank: false
}
static mapping = {
tablePerHierarchy false
tablePerSubclass true
content type: "text"
sort dateCreated: 'desc'
}
}
class BlogMessage extends Message{
static belongsTo = [blog : Blog]
static constraints = {
blog nullable: false
}
}
I'm using console to shorten things up. The problem which I encountered with Victor's approach is, when I write:
Date someValidDate = new Date() - (20*365)
BlogMessage.metaClass.setDateCreated = {
Date d ->
delegate.#dateCreated = someValidDate
}
I get following exception:
groovy.lang.MissingFieldException: No such field: dateCreated for class: pl.net.yuri.league.blog.BlogMessage
When I tried
Message.metaClass.setDateCreated = {
Date d ->
delegate.#dateCreated = someValidDate
}
Script goes well, but unfortunately dateCreated is not being altered.
I was having a similar issue, and was able to overwrite dateCreated for my domain (in a Quartz Job test, so no #TestFor annotation on the Spec, Grails 2.1.0) by
Using the BuildTestData plugin (which we use regularly anyway, it is fantastic)
Double-tapping the domain instance with save(flush:true)
For reference, my test:
import grails.buildtestdata.mixin.Build
import spock.lang.Specification
import groovy.time.TimeCategory
#Build([MyDomain])
class MyJobSpec extends Specification {
MyJob job
def setup() {
job = new MyJob()
}
void "test execute fires my service"() {
given: 'mock service'
MyService myService = Mock()
job.myService = myService
and: 'the domains required to fire the job'
Date fortyMinutesAgo
use(TimeCategory) {
fortyMinutesAgo = 40.minutes.ago
}
MyDomain myDomain = MyDomain.build(stringProperty: 'value')
myDomain.save(flush: true) // save once, let it write dateCreated as it pleases
myDomain.dateCreated = fortyMinutesAgo
myDomain.save(flush: true) // on the double tap we can now persist dateCreated changes
when: 'job is executed'
job.execute()
then: 'my service should be called'
1 * myService.someMethod()
}
}
Getting a hold of the ClosureEventListener allows you to temporarily disable grails timestamping.
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes
import org.codehaus.groovy.grails.commons.spring.GrailsWebApplicationContext
import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration
import org.codehaus.groovy.grails.orm.hibernate.support.ClosureEventTriggeringInterceptor
import org.codehaus.groovy.grails.orm.hibernate.support.ClosureEventListener
class FluxCapacitorController {
def backToFuture = {
changeTimestamping(new Message(), false)
Message m = new Message()
m.dateCreated = new Date("11/5/1955")
m.save(failOnError: true)
changeTimestamping(new Message(), true)
}
private void changeTimestamping(Object domainObjectInstance, boolean shouldTimestamp) {
GrailsWebApplicationContext applicationContext = servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
GrailsAnnotationConfiguration configuration = applicationContext.getBean("&sessionFactory").configuration
ClosureEventTriggeringInterceptor interceptor = configuration.getEventListeners().saveOrUpdateEventListeners[0]
ClosureEventListener listener = interceptor.findEventListener(domainObjectInstance)
listener.shouldTimestamp = shouldTimestamp
}
}
There may be an easier way to get the applicationContext or Hibernate configuration but that worked for me when running the app. It does not work in an integration test, if anyone figures out how to do that let me know.
Update
For Grails 2 use eventTriggeringInterceptor
private void changeTimestamping(Object domainObjectInstance, boolean shouldTimestamp) {
GrailsWebApplicationContext applicationContext = servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
ClosureEventTriggeringInterceptor closureInterceptor = applicationContext.getBean("eventTriggeringInterceptor")
HibernateDatastore datastore = closureInterceptor.datastores.values().iterator().next()
EventTriggeringInterceptor interceptor = datastore.getEventTriggeringInterceptor()
ClosureEventListener listener = interceptor.findEventListener(domainObjectInstance)
listener.shouldTimestamp = shouldTimestamp
}
I got this working by simply setting the field. The trick was to do that after the domain object has been saved first. I assume that the dateCreated timestamp is set on save and not on object creation.
Something along these lines
class Message {
String content
Date dateCreated
}
// ... and in test class
def yesterday = new Date() - 1
def m = new Message( content: 'hello world' )
m.save( flush: true )
m.dateCreated = yesterday
m.save( flush: true )
Using Grails 2.3.6
As of Grails 3 and GORM 6 you can tap into AutoTimestampEventListener to execute a Runnable that temporarily ignores all or select timestamps.
The following is a small snippet I use in my integration tests where this is necessary:
void executeWithoutTimestamps(Class domainClass, Closure closure){
ApplicationContext applicationContext = Holders.findApplicationContext()
HibernateDatastore mainBean = applicationContext.getBean(HibernateDatastore)
AutoTimestampEventListener listener = mainBean.getAutoTimestampEventListener()
listener.withoutTimestamps(domainClass, closure)
}
Then in your case you could do the following:
executeWithoutTimestamps(BlogMessage, {
Date someValidDate = new Date() - (20*365)
BlogMessage message = new BlogMessage()
message.dateCreated = someValidDate
message.save(flush: true)
})
I'm using something like this for an initial import/migration.
Taking gabe's post as a starter (which didn't work for me Grails 2.0), and looking at the old source code for ClosureEventTriggeringInterceptor in Grails 1.3.7, I came up with this:
class BootStrap {
private void changeTimestamping(Object domainObjectInstance, boolean shouldTimestamp) {
Mapping m = GrailsDomainBinder.getMapping(domainObjectInstance.getClass())
m.autoTimestamp = shouldTimestamp
}
def init = { servletContext ->
changeTimestamping(new Message(), false)
def fooMessage = new Message()
fooMessage.dateCreated = new Date("11/5/1955")
fooMessage.lastUpdated = new Date()
fooMessage.save(failOnError, true)
changeTimestamping(new Message(), true)
}
}
You can try to disable it by setting autoTimestamp = false in the domain class mapping. I doubt about global overriding because the value is taken directly from System.currentTimeMillis() (I'm looking at org.codehaus.groovy.grails.orm.hibernate.support.ClosureEventListener.java).
So I can only suggest that you override a setter for dateCreated field in your class, and assign your own value. Maybe even metaclass access will work, like
Date stubDateCreated
...
myDomainClass.metaClass.setDateCreated =
{ Date d -> delegate.#dateCreated = stubDateCreated }
I couldn't get the above techniques to work, the call to GrailsDomainBinder.getMapping always returned null???
However...
You can use the fixtures plugin to set the dateCreated property on a domain instance
The initial loading will not do it...
fixture {
// saves to db, but date is set as current date :(
tryDate( SomeDomain, dateCreated: Date.parse( 'yyyy-MM-dd', '2011-12-25') )
}
but if you follow up with a post handler
post {
// updates the date in the database :D
tryDate.dateCreated = Date.parse( 'yyyy-MM-dd', '2011-12-01')
}
Relevant part of the fixtures docs here
AFAIK fixtures don't work for unit testing, although the plugin authors may add unit testing support in the future.
A simpler solution is to use a SQL query in your integration test to set it as you please after you initialize your object with the other values you want.
YourDomainClass.executeUpdate(
"""UPDATE YourDomainClass SET dateCreated = :date
WHERE yourColumn = :something""",
[date:yourDate, something: yourThing])
As of grails 2.5.1, getMapping() method of GrailsDomainBinder class is not static,non of the above method works as is. However, #Volt0's method works with minor tweaking. Since all of us are trying to do so to make our tests working, instead of placing it in BootStrap, I placed it in actual integration test. Here is my tweak to Volt0's method:
def disableAutoTimestamp(Class domainClass) {
Mapping mapping = new GrailsDomainBinder().getMapping(domainClass)
mapping.autoTimestamp = false
}
def enableAutoTimestamp(Class domainClass) {
Mapping mapping = new GrailsDomainBinder().getMapping(domainClass)
mapping.autoTimestamp = true
}
And simply call these methods in tests like
disableAutoTimestamp(Domain.class)
//Your DB calls
enableAutoTimestamp(Domain.class)
The above code can also be placed in src directory and can be called in tests however I placed this in actual test as there was only one class in my app where I needed this.
The easy solution is to add a mapping:
static mapping = {
cache true
autoTimestamp false
}