How to add a Timestamp in Firestore with Android? - java

I am trying to add a timestamp field in an Android client with Firebase Firestore.
According to the documentation:
Annotation used to mark a Date field to be populated with a server
timestamp. If a POJO being written contains null for a
#ServerTimestamp-annotated field, it will be replaced with a
server-generated timestamp.
But when I try it:
#ServerTimestamp
Date serverTime = null; // I tried both java.util.Date and java.sql.Date
//...
Map<String, Object> msg = new HashMap<>();
// ... more data
msg.put("timestamp", serverTime);
On the Cloud Firestore database this field is always null.

That is not the correct way of how to add the time and date to a Cloud Firestore database. The best practice is to have a model class in which you can add a date field of type Date together with an annotation. This is how your model class should look like:
import java.util.Date;
public class YourModelClass {
#ServerTimestamp
private Date date;
YourModelClass() {}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
When you create on object of YourModelClass class, there is no need to set the date. Firebase servers will read your date field, as it is a ServerTimestamp (see the annotation), and it will populate that field with the server timestamp accordingly.
Another approach would be to use FieldValue.serverTimestamp() method like this:
Map<String, Object> map = new HashMap<>();
map.put("date", FieldValue.serverTimestamp());
docRef.update(map).addOnCompleteListener(new OnCompleteListener<Void>() {/* ... */}

use FieldValue.serverTimestamp() get server timestamp
Map<String, Object> msg = new HashMap<>();
msg.put("timestamp", FieldValue.serverTimestamp());

I have similar problem, and I found this at my catlog and solved my problem
firebaseFirestore = FirebaseFirestore.getInstance();
FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder()
.setTimestampsInSnapshotsEnabled(true)
.build();
firebaseFirestore.setFirestoreSettings(settings);

I had a similar problem,
I was getting the exception ...has type java.sql.Timestamp, got java.util.Date ..., so I just replace the type from Timestamp to Date ( from java.util.Date) and worked fine.

Related

#Convert is not getting called for the respository.save

I am trying to format the datetime by using #Convert(converter = MyConverter.class).
This is working as expected while saving and data is properly saved in the DB.
The issue I am facing is the object that's being returned while responseEntity = myrepository.save(myEntity) is not having the formated date. The field in the responseEntity is still returning old format. Am I missing anything?
My converter class:
public class DateTimeConverter implements
AttributeConverter<LocalDateTime, String> {
#Override
public String convertToDatabaseColumn(LocalDateTime attribute) {
if(Objects.isNull(attribute)) {
return null;
}
attribute = attribute.atZone(ZoneId.systemDefault()).withZoneSameInstant(ZoneOffset.UTC).toLocalDateTime();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return attribute.format(formatter);
}
#Override
public LocalDateTime convertToEntityAttribute(String dbData) {
if(Objects.isNull(dbData)) {
return null;
}
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return LocalDateTime.parse(dbData, formatter);
}
}
Ran into a similar problem and discovered that the attribute conversion wasn't done by save() but it was done by saveAndFlush(). Breakpoints within the conversion weren't hit during save() but were by saveAndFlush(). If save() was used the conversion was hit on a subsequent find... query. This was also an issue if save should have thrown an exception as it got delayed until flush or subsequent query.
repository.save() does return the converted value, entity value converted before flush time. It only attached to the persistence context.
But you want converted value using convertToEntityAttribute. convertToEntityAttribute only called when you fetch from the database.
Do this operation in service
entity.setProperty(entity.getProperty().atZone(ZoneId.systemDefault()).withZoneSameInstant(ZoneOffset.UTC).toLocalDateTime());

Bizzare Issue With Java Timestamp

I am working on an application which sends an object to a server for processing. The object is sent in JSON format using Spring.
My issue is that all the fields are passed correctly - EXCEPT for the Date variables. They show up as a completely different value, and I am stumped as to why.
Here is an abbreviated version of the object that is being passed:
public class TransactionParameters {
public Date startDate;
public Date endDate;
public List<String> transactionCodes;
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public Date getEndDate() {
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
public List<String> getTransactionCodes() {
return transactionCodes;
}
public void setTransactionCodes(List<String> transactionCodes) {
this.transactionCodes = transactionCodes;
}
}
Here is the JSON created:
{"transactionCodes":["195"],"startDate":1524456000000,"endDate":1524456000000}
Here is the client code:
String responseString =
restTemplate.postForObject("http://localhost:9080/app/transaction"
+ "testUser123", transactionParameters, String.class);
Here is the server code:
#ApiOperation(value="Get Transactions for Customer")
#POST
#Produces({ MediaType.APPLICATION_JSON })
#Consumes(MediaType.APPLICATION_JSON)
#Path("/customerAccountTransactions/{customerCode: [a-zA-Z0-9]+}")
#RequestMapping(value ="/transaction/{customerCode: [a-zA-Z0-9]+}", method=RequestMethod.POST, produces=MediaType.APPLICATION_JSON, consumes=MediaType.APPLICATION_JSON)
#ApiImplicitParams(#ApiImplicitParam(name = AUTHORIZATION, value = AUTHORIZATION, required = true, dataType = STRING, paramType = HEADER))
public Response getAccountTransactionsForCustomer(#PathVariable(CUSTOMER_CODE) #PathParam(CUSTOMER_CODE) final String customerCode, TransactionParameters transactionParameters) throws IntegrationException {
LOGGER.info("getAccountTransactionsForCustomer()");
Response response = null;
try {
final AccountTransactionsBean atb = getTransactions(customerCode, transactionParameters)
response = ResponseBuilder.buildSuccessResponse(atb);
} catch (final NotAuthorizedException nae) {
response = ResponseBuilder.buildNotAuthorizedResponse();
}
return response;
}
But here's my issue - When I put a breakpoint at where the client calls the endpoint, the date is correct.
However, the date is wildly incorrect as it enters the server's endpoint.
All the the other variables in the TransactionParameters bean are correct. I have also replicated this call using SOAP UI, to rule out any issues with the client, and the issue still persists.
Can anyone offer any suggestions?
Thanks in advance for any help.
The reason for this issue is that Date and String are two different data types. When you are converting your Object to JSON, it is directly converting the date to String and in that process losing its essence.
In order to solve this, you need to tell the code that those particular fields are dates and thus, need to be retained as it is. You can do that by using annotations in your POJO:
Example:
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss.SSSZ")
private Date changeTimestamp;
You can use the above syntax and then change the pattern as per your need.
Disclaimer admittedly I don't know much about Spring REST so I can only give you general pointers, but this really does seem like a de-serialization issue.
Some general things to consider:
Make sure the server and client have the same settings for serializing/de-serializing.
Make sure they are running the same versions of Spring REST and Jackson.
Set the JVM arg -Djavax.net.debug=all and run again to look at what is really being sent/recieved.
Being Spring REST this uses Jackson under the hood right?
Try explicitly annotating your dates and see if that helps:
public class TransactionParameters {
#JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
public Date startDate;
#JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
public Date endDate;
// ...
}
You probably have to either add or remove the milliseconds to get the conversion to work correctly. 000

Joda DateTime in OrientDB Object API

I am using JDK 8, JodaTime 2.9.9 and OrientDB version 2.2.26.
Is there a way to use DateTime objects with OrientDB Object API?
Example class:
class Entity {
private DateTime date;
public Entity(DateTime date){
this.date = date
}
public DateTime getDate(){
return date;
}
public void setDate(DateTime newDate){
this.date = newDate;
}
}
Registering in OrientDB:
database.getEntityManager().registerEntityClass(Entity)
If I try to save it:
database.save(new Entity(DateTime.now()))
Then I get:
com.orientechnologies.orient.core.exception.OSerializationException:
Linked type [class org.joda.time.DateTime:2017–09–12T11:50:25.709–03:00]
cannot be serialized because is not part of registered entities.
If I try to register DateTime:
database.getEntityManager().registerEntityClass(DateTime)
And I try to save entity again:
database.save(new Entity(DateTime.now()))
Since it is a final class, javassist cannot proxy it, so I got a:
java.lang.RuntimeException: org.joda.time.DateTime is final
I don’t wanna to change my class to store a long instead of a DateTime. Is there a way to implement and register some sort of serializer and deserializer for DateTime or something that, similarly, would not interfere with my entity?
Ok, I find out how to do it (code in Groovy):
def orientDbServer = OServerMain.create()
System.setProperty("ORIENTDB_HOME", new File("").getAbsolutePath());
orientDbServer.startup(new OServerConfiguration().with { cfg ->
location = "memory"
network = new OServerNetworkConfiguration(this)
users = [new OServerUserConfiguration(name: "root",
password: "root",
resources: "*")] as OServerUserConfiguration[]
cfg
})
orientDbServer.activate()
addShutdownHook {
orientDbServer.shutdown()
}
new OServerAdmin("localhost").connect("root", "root")
.createDatabase("test", "document", "memory").close()
OObjectDatabaseTx database =
new OObjectDatabaseTx("memory:localhost/test").open("admin", "admin")
OObjectSerializerContext serializerContext = new OObjectSerializerContext();
serializerContext.bind(new OObjectSerializer<DateTime, Long>() {
#Override
Object serializeFieldValue(Class<?> iClass, DateTime iFieldValue) {
return iFieldValue.getMillis()
}
#Override
Object unserializeFieldValue(Class<?> iClass, Long iFieldValue) {
return new DateTime(iFieldValue)
}
}, database)
OObjectSerializerHelper.bindSerializerContext(null, serializerContext)
It is important that the serializerContext is registered before any entity class registration

How to compare Google Appengine DateTime against a Date object in Java?

I am new to Google Appengine. In my application, the datastore entities store a java.util.Date object. I want to query the datastore to return all the entities before a particular time. My code for this is :
Date date = new Date();
Query<Event> query = ofy().load().type(Event.class).order("date");
query = query.filter("date <", date);
which upon execution, gives the error : Invalid date/time format: Sat Apr 04 00:40:22 IST 2015
If this format is invalid, which format do I need to use to query?
Here is a simple code
import java.util.Date;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Index;
#Entity
public class EntityDate {
public EntityDate() {
// Objectify needed
}
#Id
private Long id;
#Index
public Date date;
}
and here the code which makes the query
Date date = new Date();
Objectify ofy = ObjectifyService.ofy();
ObjectifyService.register(EntityDate.class);
EntityDate entityDate = new EntityDate();
entityDate.date = date;
ofy.save().entities(entityDate);
Query<EntityDate> ofyQuery = ofy.load().type(EntityDate.class).order("date");
ofyQuery = ofyQuery.filter("date <", date);
List<EntityDate> list = ofyQuery.list();
Logger.getLogger("EntityDate").info(list.toString());
The entities are correctly saved
and the query provides the 4 results
[EntityDate#6780874d, EntityDate#27330551, EntityDate#6a21cf2, EntityDate#7d1a5744]
The default toString() of the class is a bit ugly, but it makes the point about the query correctly executed.
Can you provide the source of your Event class and the continuation of your code which execute the query?

Using joda DateTime Range Key with AWS DynamoDB Object Persistence model

I've got a dynamodb table with a timestamp ("creationDate") as a range. The model is using a joda DateTime to make it easy to use (compatibility with the rest of the code). To be able to make between queries on this range, I used a numeric type for the attribute in the table, and planned to store it as a java timestamp (milliseconds since epoch). Then, I added a marshaller to convert a joda DateTime to a String representing a long and vice-versa.
The table structure (creation):
void CreateTable()
{
CreateTableRequest createTableRequest = new CreateTableRequest().withTableName(LinkManager.TABLE_NAME);
ProvisionedThroughput pt = new ProvisionedThroughput()
.withReadCapacityUnits(LinkManager.READ_CAPACITY_UNITS)
.withWriteCapacityUnits(LinkManager.WRITE_CAPACITY_UNITS);
createTableRequest.setProvisionedThroughput(pt);
ArrayList<AttributeDefinition> ad = new ArrayList<AttributeDefinition>();
ad.add(new AttributeDefinition().withAttributeName("creationDate").withAttributeType(ScalarAttributeType.N));
ad.add(new AttributeDefinition().withAttributeName("contentHash").withAttributeType(ScalarAttributeType.S));
createTableRequest.setAttributeDefinitions(ad);
ArrayList<KeySchemaElement> ks = new ArrayList<KeySchemaElement>();
ks.add(new KeySchemaElement().withAttributeName("contentHash").withKeyType(KeyType.HASH));
ks.add(new KeySchemaElement().withAttributeName("creationDate").withKeyType(KeyType.RANGE));
createTableRequest.setKeySchema(ks);
this.kernel.DDB.createTable(createTableRequest);
}
The model:
#DynamoDBTable(tableName="Link")
public class Link {
private String ContentHash;
private DateTime CreationDate;
#DynamoDBHashKey(attributeName = "contentHash")
public String getContentHash() {
return ContentHash;
}
public void setContentHash(String contentHash) {
ContentHash = contentHash;
}
#DynamoDBRangeKey(attributeName = "creationDate")
#DynamoDBMarshalling(marshallerClass = DateTimeMarshaller.class)
public DateTime getCreationDate() {
return CreationDate;
}
public void setCreationDate(DateTime creationDate) {
CreationDate = creationDate;
}
}
The marshaller:
public class DateTimeMarshaller extends JsonMarshaller<DateTime>
{
public String marshall(DateTime dt)
{
return String.valueOf(dt.getMillis());
}
public DateTime unmarshall(String dt)
{
long ldt = Long.parseLong(dt);
return new DateTime(ldt);
}
}
I get the following error:
Exception in thread "main" com.amazonaws.AmazonServiceException: Type of specified attribute inconsistent with type in table (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ValidationException; Request ID: 8aabb703-cb44-4e93-ab47-c527a5aa7d52)
I guess this is because the marshaller returns a String, and dynamoDB wants a numeric type, as the attribute type is N. I don't know what people do in this case, I searched for a solution but couldn't find it. I only tested this on a local dynamodb instance, which I don't think makes any difference (this is a validation check failing, there's no request even made).
The obvious workaround is to use long type for the dates in the model and add special getters and setters to work with DateTime. Still, is there a cleaner way ? I am sure I'm not the only one using DatTime range in a model.
What I would do is re-create the table with the Range key as String itself.
Even if it's going to be populated with long numbers, making it type: S will ensure compatibility with the Marshaller

Categories