Spring MongoRepository : Convert to binary before inserting into MongoDB - spring-data

This might be a simple question, but I am not an expert with Spring.
I have a class Message that represents documents in a collection called "messages" in mongoDB.
#Document(collection = "archive")
public class Message{
#Id
private String messageId;
private String from;
private String to;
// more stuff
}
The interface MessagesRepository extends MongoRepository<Message,String>:
public interface MessagesRepository extends MongoRepository<Message, String>{
}
I have MongoRepository.findOne(ID) method calls where ID is a string, across different modules in my code.
It all works fine. The ID, called messageId is a String. But I want to insert it into Mongo in binary encoded format. My first rough guess was to do
#Id
private byte[] messageId;
in the Message class
But it neither felt right to me, nor worked because I found out I was getting different byte[] for different String objects even with the same values.
I've seen MongoMappingConverter, but not sure if it works. Can someone throw some light?
Thanks a ton!

I found no way to do that automatically as of now. However, to achieve this I've only changed the messageId from String to byte[].
That seems to have worked.
And correction : the find() method of Spring-Data doesn't work with a byte[] but Mongo's java driver works so I'm going to adjust the Spring-Data methods.

Related

Understanding of #AggregateIdentifier & #TargetAggregateIdentifier

so I'm learning about the axon framework and just want to solidify my understanding of the #TargetAggregateIdentifier annotation.
My Command:
public class IssueCardCommand {
private String cardId;
private String versionNumber;
private Integer amount;
#TargetAggregateIdentifier
private String getAggregateIdentifier() {
return (null != versionNumber) ? cardId + "V" + versionNumber : cardId;
}
}
My Aggregate:
#Aggregate
#Slf4j
public class GiftCard {
private String giftCardId;
private String versionNumber;
private Integer amount;
#AggregateIdentifier
private String getAggregateIdentifier() {
return (null != versionNumber) ? giftCardId + "V" + versionNumber : giftCardId;
}
public GiftCard() {
log.info("empty noargs constructor");
}
#CommandHandler
public GiftCard(IssueCardCommand cmd) {
log.info("handling {}",cmd);
//this.giftCardId = cmd.getCardId();
//this.versionNumber = cmd.getVersionNumber();
apply(new CardIssuedEvent(cmd.getCardId(),cmd.getVersionNumber(),cmd.getAmount()));
}
#EventSourcingHandler
public void onCardIssuedEvent(CardIssuedEvent evt) {
log.info("applying {}",evt);
this.giftCardId = evt.getCardId();
this.versionNumber = evt.getVersionNumber();
this.amount = evt.getAmount();
}
}
So this all works as expected and the events are being stored correctly. However, I just want to make sure that I understand the #TargetAggregateIdentifier & #AggregateIdentifier annotations correctly.
So, the
#TargetAggregateIdentifier - a command goes to a specific instance of the aggregate and so it is needed to tell the framework which instance it is, so this annotation on a field/method is used to load the events for that particular aggregate?
I noticed that when I didn't have #TargetAggregateIdentifier in the command for the constructor, the code still works. But if it was missing from any subsequent commands it gave the error 'Invalid command, It does not identify the target aggregate', which I feel like confirmed my understanding above?
#AggregateIdentifier - this tells the axon framework that this is the identifier, so that when more commands are introduced it will need to store the events for that particular aggregate?
I would really appreciate it if someone could point out if my understanding is correct and comment where it isn't, so that I can use the framework in the correct way.
Thanks.
I already provided an answer on AxonIQ's discuss platform where the question was originally posted.
Since answering with a link only goes against SO's etiquette I'm coping my answer here too:
Your understanding is correct I guess. But let me give you a
simplified explanation of the process so you can compare it to your
current understanding.
When you issue a command that creates new aggregate (one that is
handled by the Aggregate's constructor) there is no need for
identifier. The reason is you are creating a new instance, not loading
an existing one. In that case the framework only needs the FQCN of the
aggregate in order to create an instance of it. It can easily find
the FQCN because it knows (from inspection during aggregate
registration) which aggregate can handle such create command. That's
why create commands without #TargetAggregateIdentifier work just
fine.
Once the create command is processed the aggregate's state needs to be
stored somewhere.
for event sourced aggregates all state changing events are stored in a event store
for state stored aggregates the entire state is stored in a repository
In both cases the framework needs to know how to identify this data.
That's what #AggregateIdentifier is for. It tells the framework to
store the data in way that it is identifiable by specific identifier.
You can think of it as primary key in DB terms.
When you send a command to an existing instance of an aggregate you
need to tell which instance that is. You do so by providing a
#TargetAggregateIdentifier . The framework will then create a new
empty instance of the respective aggregate and then try to load the
data into it
for event sourced aggregates by reading all the past events related to that instance
for state stored aggregates by reading current state from a repository
In both cases the framework will search for data that's identifiable
by the value of #TargetAggregateIdentifier. Once the aggregate data
is loaded it will proceed with handling the command.

Using #Id on methods

I would like to annotate a method with Spring Data #Id but it only works with fields, despite the fact that the annotation can be used on methods.
Is there a way to use #Id on methods too?
I'm using Spring Boot 1.3.0.RELEASE
EDIT
Actually I have this interface that will have an instance being created at runtime.
import org.springframework.data.annotation.Id;
#Document(indexName = "index", type = "document")
public interface Document {
#Id
Integer getId();
}
And this repository.
public interface DocumentRepository extends ElasticsearchCrudRepository<Document, Integer> {
}
Problem is that SimpleElasticsearchPersistentProperty from spring-data-elasticsearch 1.3.0.RELEASE always look for fields:
https://github.com/spring-projects/spring-data-elasticsearch/blob/1.3.0.RELEASE/src/main/java/org/springframework/data/elasticsearch/core/mapping/SimpleElasticsearchPersistentProperty.java
That way if I create an asbtract class instead and put #Id on a field, everything works fine.
The #Id annotation does work on properties, i.e. you can put it on getters, setters or fields. If this does not work something is wrong. Possible reasons are:
the names don't fit the property conventions
you are using the wrong #Id annotation
It does not work on arbitrary methods because Spring Data wouldn't be able to determine a name for that non-property, which in turn is required for many features.

could not get a field value by reflection hibernate JPA + GF 4

I am using JPA 2.1 with Hibernate 4.3.x on Glassfish 4, also tried the suggestion listed at https://coderwall.com/p/e5fxrw still get the below error. Could some one let me know what might be the issue ?
javax.persistence.PersistenceException: org.hibernate.PropertyAccessException: could not get a field value by reflection getter of com.dstar.entity.PurchaseOrder.idpurchaseorder
Below is the entity code, skipped getter and setter methods:
#Entity
#Table(name="purchaseorder")
#PersistenceUnit(name="dstarwarehouse",unitName="dstarwarehouse")
public class PurchaseOrder implements Serializable{
#Id #GeneratedValue(strategy=GenerationType.AUTO)
private int idpurchaseorder;
private boolean cash;
private boolean credit;
private String supplier;
private String orderedBy;
private String submittedBy;
private String approvedBy;
private Date expectedDate;
private Date creationDate;
private Date submittedDate;
private Date approvalDate;
private String purchaserName;
private double total;
#JoinColumn(name="idpurchaseorder", referencedColumnName="idpurchaseorder")
private List<Part> parts;
}
I had the same problem, using glassfish 4.1, hibernate 4.3.6, and injecting entity manager thru #PersistenceContext in a Stateless Session Bean, and saw some things interesting.
First, if I get the entity manager direct from the Persistence.createEntityManagerFactory("xxxxxx").createEntityManager() the problem disappear. Obviously, I don't like get things right in this way.
Change the server from glassfish 4.1 to glassfish 4, seems solve the problem too. So, at this moment, the problem looks like for me something wrong in glassfish 4.1.

Form validation in Play 2.0

OK, So I am having some issues with getting data from a form to bind to a model class I have.
I have a class Question that basically looks like this:
#Entity
public class Question extends Model {
#Id #Required public int id;
public String title;
public String body;
...methods...
}
So I want to use this as a template for a form for a user to create a question, so I create a static instance (as they do in the samples)
final static Form<Question> question_form = form(Question.class);
So far so good, everything compiles. The problem comes when I actually submit the form:
Form<Question> filled_form = new Form<Question>(Question.class).bindFromRequest();
Here I get the error:
[UnexpectedTypeException: No validator could be found for type: java.lang.Integer]
My thinking on how to proceed is to use a design pattern that goes like this:
1.) Create template classes specifically for Forms, that don't include things like foreign keys, IDs, and information that isn't in a format designed for the user. (i.e. if the Question has a foreign key for Topic, the QuestionForm class would have a String topic field.
2.) Create a methods in the Question model that goes something like getFormForQuestion(Question) and getQuestionForForm(Form<Question>) and then use these methods to do CRUD functions.
So basically the User and controller interact using Forms, and then the Model knows how to take these forms and turn them into entries in the database.
Is this a reasonable way to proceed? Or is there a better way of doing this?
UPDATE:
Seems to be fixed when using #GeneratedValue annotation rather than the #Required annotation, but I am still curious regarding my proposed Form Design pattern.
Also just removing #Required appears to fix the problems. Still looking for comments on the mentioned design pattern!
id field doesn't need any validation, ORM will care about it. Of course you should not place id in form (it shouldn't be edited at all - it's common AUTO_INCREMENT) And better make it Long, just:
#Id
public Long id;

How do I store HashMaps in mongodb using morphia?

How do i store HashMaps in mongodb using morphia?
I'm not sure if this is a bug in morphia, or if i'm just doing it wrong.
say i have this domain model
#Entity("person")
public class Person {
private String property1;
private String property2;
private HashMap<String, Thing> things;
}
when i try to save this using a class that extends BasicDAO repo.save(personInstance) i get this error: (UsedView is the equivalent of Thing in the example above)
java.lang.IllegalArgumentException: can't serialize class com.model.designed.UsedView
at org.bson.BSONEncoder._putObjectField(BSONEncoder.java:205)
at org.bson.BSONEncoder.putMap(BSONEncoder.java:245)
at org.bson.BSONEncoder._putObjectField(BSONEncoder.java:177)
at org.bson.BSONEncoder.putObject(BSONEncoder.java:121)
at org.bson.BSONEncoder.putObject(BSONEncoder.java:67)
at com.mongodb.OutMessage.putObject(OutMessage.java:189)
at com.mongodb.DBApiLayer$MyCollection.insert(DBApiLayer.java:245)
at com.mongodb.DBApiLayer$MyCollection.insert(DBApiLayer.java:209)
at com.mongodb.DBCollection.insert(DBCollection.java:66)
at com.mongodb.DBCollection.save(DBCollection.java:622)
at com.google.code.morphia.DatastoreImpl.save(DatastoreImpl.java:731)
at com.google.code.morphia.DatastoreImpl.save(DatastoreImpl.java:793)
at com.google.code.morphia.DatastoreImpl.save(DatastoreImpl.java:787)
at com.google.code.morphia.dao.BasicDAO.save(BasicDAO.java:109)...
Then if i changed:
"private HashMap<String, Thing> things"
to
"private HashMap<String, String> things"
it saves it fine.
any thoughts?
much appreciated!
Is Thing mapped as a Morphia entity? If not, morphia won't know how to save it to mongo.
old question I realize, but I came across this: (untested)
"...This could contain any basic types supported by the MongoDB driver including Lists and Maps, but no complex objects unless you have registered converters with Morphia (e.g. morphia.getMapper().getConverters().addConverter(new MyCustomTypeConverter())."
From:
http://www.carfey.com/blog/using-mongodb-with-morphia/
As said: untested.
hth.