How to map domain objects to CQL Tables using Spring Data Cassandra? - java

I have two model classes:
public class AlertMatchesDTO implements Serializable
{
private static final long serialVersionUID = -3704734448105124277L;
#PrimaryKey
private String alertOid;
#Column("matches")
private List<HotelPriceDTO> matches;
...
}
public class HotelPriceDTO implements Serializable
{
private static final long serialVersionUID = -8751629882750913707L;
private Long hotelOid;
private double priceByNight;
private Date checkIn;
private Date checkOut;
...
}
and I want to persist instances of the first class in a Cassandra column family using Spring Data. In particular using Cassandra template like this:
...
cassandraTemplate.insert(dto, writeOptions);
...
and Spring Data have problems serializing List<HotelPriceDTO>. What I think I need is a way to tell cassandraTemplate how to convert the type. In the official documentation, there is a chapter telling that I have to use CassandraMappingConverter and MappingCassandraConverter, but they do not provide an example yet.
My question is: is there an example of how to register a converter like this (in the test code of the project, may be?) or any other example I can use while the official documentation is completed? Thanks in advance.

Hate to say this, but you should RTFM at http://docs.spring.io/spring-data/cassandra/docs/1.1.0.RELEASE/reference/html/.
Having said that, I noticed the DTO suffixes on your class names, which implies to me that you may not have a domain model, only a service layer with DTOs. If that's the case, you might consider defining the mappings yourself as RowMapper implementations and simply use CqlTemplate without the bells & whistles of Spring Data Cassandra. If you choose to fuse the architectural concepts of DTO and entity (entity being a persistent domain object), you're free to use Spring Data Cassandra along with the mapping metadata required (#Table, #PrimaryKeyColumn, etc). Your choice.
See http://goo.gl/gPBFpu for more reading on the subject of entities v. DTOs.

Related

Spring boot - How to convert DTO to entity that is part of another DTO

I have entities that represent my database - User, Recipe and Tag.
For data manipulation I use DTO. So UserDTO, RecipeDTO, TagDTO. When I define a relationship between entities, I use its basic User, Recipe, Tag form, but when I define these relationships in a DTO class, I use its DTO form.
For example:
DTO Class looks like this
public class UserDTO{
private String name;
private String email
private List<RecipeDTO>
}
public class RecipeDTO{
private String title;
private String description;
private UserDTO user;
}
I know how to map a DTO to an entity so that I can perform operations (CRUD) on the data in the database.
private Recipe convertToEntity(RecipeDTO recipeDTO){
Recipe recipe = new Recipe();
recipe.setTitle(recipeDTO.getTitle);
recipe.setDescription(recipeDTO.getDescription);
}
But the RecipeDTO also has a UserDTO in it, which I also need to map to an entity. How do I do this?
So I am trying to achieve a mapping inside the mapping .... (??)
I can think of the following solution.
Create method that converts UserDTO to User:
private User convertUser(UserDTO userDTO){
User user = new User();
user.setName(userDTO.getName());
user.setEmail(userDTO.getEmail());
}
And then use it while mapping RecipeDTO to Recipe.
private Recipe convertToEntity(RecipeDTO recipeDTO){
Recipe recipe = new Recipe();
recipe.setTitle(recipeDTO.getTitle());
recipe.setDescription(recipeDTO.getDescription());
//Convert UserDTO
recipe.setUser(convertUser(recipeDTO.getUser()));
}
I'm not sure if this is the right solution, as there will be more and more mappings as the code gets bigger.
The approach you described is not wrong and will work, but doing it that way will indeed involve a lot of hard work.
The way this is usually done in the industry is by letting a library do that work for you.
The two most popular mapping libraries for java are:
https://mapstruct.org/ (which uses annotation processing at compile time and auto-generates basically the same mapping code as in your example)
and
http://modelmapper.org/ (which uses black magic and reflection)
They are both easy to setup/learn and either will do the job (including mapping nested objects as in your example), so take a look at the “getting started“ section and pick the one you find more intuitive to use.
My personal recommendation would be to pick Mapstruct, as it has way fewer gotchas, generates clean human-readable code and avoids using reflection.

DDD implementation with Spring Data and JPA + Hibernate problem with identities

So I'm trying for the first time in a not so complex project to implement Domain Driven Design by separating all my code into application, domain, infrastructure and interfaces packages.
I also went with the whole separation of the JPA Entities to Domain models that will hold my business logic as rich models and used the Builder pattern to instantiate. This approach created me a headache and can't figure out if Im doing it all wrong when using JPA + ORM and Spring Data with DDD.
Process explanation
The application is a Rest API consumer (without any user interaction) that process daily through Scheduler tasks a fairly big amount of data resources and stores or updates into MySQL. Im using RestTemplate to fetch and convert the JSON responses into Domain objects and from there Im applying any business logic within the Domain itself e.g. validation, events, etc
From what I have read the aggregate root object should have an identity in their whole lifecycle and should be unique. I have used the id of the rest API object because is already something that I use to identify and track in my business domain. I have also created a property for the Technical id so when I convert Entities to Domain objects it can hold a reference for the update process.
When I need to persist the Domain to the data source (MySQL) for the first time Im converting them into Entity objects and I persist them using the save() method. So far so good.
Now when I need to update those records in the data source I first fetch them as a List of Employees from data source, convert Entity objects to Domain objects and then I fetch the list of Employees from the rest API as Domain models. Up until now I have two lists of the same Domain object types as List<Employee>. I'm iterating them using Streams and checking if an objects are not equal() between them if yes a collection of List items is created as a third list with Employee objects that need to be updated. Here I've already passed the technical Id to the domain objects in the third list of Employees so Hibernate can identify and use to update the records that are already exists.
Up to here are all fairly simple stuff until I use the saveAll() method to update the records.
Questions
I alway see Hibernate using INSERT instead of updating the list of
records. So If Im correct Hibernate session is not recognising the
objects that Im throwing into it because I have detached them when I
used the convert to domain object?
Does anyone have a better idea how can I implement this differently or fix
this problem?
Or should I stop using this approach as two different objects and continue use
them as rich Entity models?
Simple classes to explain it with code
EmployeeDO.java
#Entity
#Table(name = "employees")
public class EmployeeDO implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
public EmployeeDO() {}
...omitted getter/setters
}
Employee.java
public class Employee {
private Long persistId;
private Long employeeId;
private String name;
private Employee() {}
...omitted getters and Builder
}
EmployeeConverter.java
public class EmployeeConverter {
public static EmployeeDO serialize(Employee employee) {
EmployeeDO target = new EmployeeDO();
if (employee.getPersistId() != null) {
target.setId(employee.getPersistId());
}
target.setName(employee.getName());
return target;
}
public static Employee deserialize(EmployeeDO employee) {
return new Country.Builder(employee.getEmployeeId)
.withPersistId(employee.getId()) //<-- Technical ID setter
.withName(employee.getName())
.build();
}
}
EmployeeRepository.java
#Component
public class EmployeeReporistoryImpl implements EmployeeRepository {
#Autowired
EmployeeJpaRepository db;
#Override
public List<Employee> findAll() {
return db.findAll().stream()
.map(employee -> EmployeeConverter.deserialize(employee))
.collect(Collectors.toList());
}
#Override
public void saveAll(List<Employee> employees) {
db.saveAll(employees.stream()
.map(employee -> EmployeeConverter.serialize(employee))
.collect(Collectors.toList()));
}
}
EmployeeJpaRepository.java
#Repository
public interface EmployeeJpaRepository extends JpaRepository<EmployeeDO, Long> {
}
I use the same approach on my project: two different models for the domain and the persistence.
First, I would suggest you to don't use the converter approach but use the Memento pattern. Your domain entity exports a memento object and it could be restored from the same object. Yes, the domain has 2 functions that aren't related to the domain (they exist just to supply a non-functional requirement), but, on the other side, you avoid to expose functions, getters and constructors that the domain business logic never use.
For the part about the persistence, I don't use JPA exactly for this reason: you have to write a lot of code to reload, update and persist the entities correctly. I write directly SQL code: I can write and test it fast, and once it works I'm sure that it does what I want. With the Memento object I can have directly what I will use in the insert/update query, and I avoid myself a lot of headaches about the JPA of handling complex tables structures.
Anyway, if you want to use JPA, the only solution is to:
load the persistence entities and transform them into domain entities
update the domain entities according to the changes that you have to do in your domain
save the domain entities, that means:
reload the persistence entities
change, or create if there're new ones, them with the changes that you get from the updated domain entities
save the persistence entities
I've tried a mixed solution, where the domain entities are extended by the persistence ones (a bit complex to do). A lot of care should be took to avoid that domain model should adapts to the restrictions of JPA that come from the persistence model.
Here there's an interesting reading about the splitting of the two models.
Finally, my suggestion is to think how complex the domain is and use the simplest solution for the problem:
is it big and with a lot of complex behaviours? Is expected that it will grow up in a big one? Use two models, domain and persistence, and manage the persistence directly with SQL It avoids a lot of caos in the read/update/save phase.
is it simple? Then, first, should I use the DDD approach? If really yes, I would let the JPA annotations to split inside the domain. Yes, it's not pure DDD, but we live in the real world and the time to do something simple in the pure way should not be some orders of magnitude bigger that the the time I need to to it with some compromises. And, on the other side, I can write all this stuff in an XML in the infrastructure layer, avoiding to clutter the domain with it. As it's done in the spring DDD sample here.
When you want to update an existing object, you first have to load it through entityManager.find() and apply the changes on that object or use entityManager.merge since you are working with detached entities.
Anyway, modelling rich domain models based on JPA is the perfect use case for Blaze-Persistence Entity Views.
Blaze-Persistence is a query builder on top of JPA which supports many of the advanced DBMS features on top of the JPA model. I created Entity Views on top of it to allow easy mapping between JPA models and custom interface defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure the way you like and map attributes(getters) via JPQL expressions to the entity model. Since the attribute name is used as default mapping, you mostly don't need explicit mappings as 80% of the use cases is to have DTOs that are a subset of the entity model.
The interesting point here is that entity views can also be updatable and support automatic translation back to the entity/DB model.
A mapping for your model could look as simple as the following
#EntityView(EmployeeDO.class)
#UpdatableEntityView
interface Employee {
#IdMapping("persistId")
Long getId();
Long getEmployeeId();
String getName();
void setName(String name);
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
Employee dto = entityViewManager.find(entityManager, Employee.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features and it can also be saved back. Here a sample repository
#Repository
interface EmployeeRepository {
Employee findOne(Long id);
void save(Employee e);
}
It will only fetch the mappings that you tell it to fetch and also only update the state that you make updatable through setters.
With the Jackson integration you can deserialize your payload onto a loaded entity view or you can avoid loading alltogether and use the Spring MVC integration to capture just the state that was transferred and flush that. This could look like the following:
#RequestMapping(path = "/employee/{id}", method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> updateEmp(#EntityViewId("id") #RequestBody Employee emp) {
employeeRepository.save(emp);
return ResponseEntity.ok(emp.getId().toString());
}
Here you can see an example project: https://github.com/Blazebit/blaze-persistence/tree/master/examples/spring-data-webmvc

Specify MongoDb collection name at runtime in Spring boot

I am trying to reuse my existing EmployeeRepository code (see below) in two different microservices to store data in two different collections (in the same database).
#Document(collection = "employee")
public interface EmployeeRepository extends MongoRepository<Employee, String>
Is it possible to modify #Document(collection = "employee") to accept runtime parameters? For e.g. something like #Document(collection = ${COLLECTION_NAME}).
Would you recommend this approach or should I create a new Repository?
This is a really old thread, but I will add some better information here in case someone else finds this discussion, because things are a bit more flexible than what the accepted answer claims.
You can use an expression for the collection name because spel is an acceptable way to resolve the collection name. For example, if you have a property in your application.properties file like this:
mongo.collection.name = my_docs
And if you create a spring bean for this property in your configuration class like this:
#Bean("myDocumentCollection")
public String mongoCollectionName(#Value("${mongo.collection.name}") final String collectionName) {
return collectionName
}
Then you can use that as the collection name for a persistence document model like this:
#Document(collection = "#{#myDocumentCollection}")
public class SomeModel {
#Id
private String id;
// other members and accessors/mutators
// omitted for brevity
}
It shouldn't be possible, the documentation states that the collection field should be collection name, therefore not an expression:
http://docs.spring.io/spring-data/data-mongodb/docs/current/api/org/springframework/data/mongodb/core/mapping/Document.html
As far as your other question is concerned - even if passing an expression was possible, I would recommend creating a new repository class - code duplication would not be bad and also your microservices may need to perform different queries and the single repository class approach would force you to keep query methods for all microservices within the same interface, which isn't very clean.
Take a look at this video, they list some very interesting approaches: http://www.infoq.com/presentations/Micro-Services
I used #environment.getProperty() to read from my application.yml. Like so :
application.yml:
mongodb:
collections:
dwr-suffix: dwr
Model:
#Document("Log-#{#environment.getProperty('mongodb.collections.dwr-suffix')}")
public class Log {
#Id
String logId;
...

Why is this field not being serialized?

My Issue entity was created from a DB table that has several fields (id, etc...). Each issue has as a field a list of Articles, which are stored in a separate DB table. Articles have a int issueID field, which is used to map them to the appropriate Issue (there is no corresponding column in the issues table): Ultimately, when an Issue object is constructed, I'm going to have it pull all of the articles whose issueID matches its ID, so that I can return a single serialized object that contains the issue data as well as a JSONArray representing its list of articles.
At this point, though, I'm just doing some testing - creating a few dummy Article objects and adding them to the articles collection. The problem is that, when I test GET requests on the Issue object, the JSONObject returned includes only the fields stored in the database (id, etc...) - no sign of the Article collection. Why is that?
I'm equally interested to know what other code you would need to see to answer this question: I've just begun teaching myself how to write web services and am still in the phase of wrapping my head around the broad concepts, so figuring out which of the moving parts has affects which behaviors - and which annotations are needed where - is ultimately what I'm trying to do.
That being the case, broader-based advice is welcomed.
#Entity
#Table(name = "issues")
#XmlRootElement
public class Issue implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Column(name = "id")
private Integer id;
....//other fields
#OneToMany(mappedBy = "issueID")
private Collection<Articles> articlesCollection;
public Issue() {
articlesCollection = new ArrayList<Articles>();
Articles a = new Articles();
a.setHeadline("butt cheese");
articlesCollection.add(a);
Articles b = new Articles();
articlesCollection.add(b);
Articles c = new Articles();
articlesCollection.add(c);
}
By default the relationship initialization is lazy so when the Issue object is loaded the articlesCollection is not fetched unless used.
In your case seems its the same situation.
Explore OpenEntityManagerInViewFilter if you do not intend to explicitly load articlesCollection. When your object serializes the articlesCollection will be loaded if you have configured OpenEntityManagerInViewFilter.
Does your Articles Class also has #XmlType or #XMLRootElement Tag?
Onany generic class like List<T> jaxb expects that T is annotated with #XMLType or #XMLRootElelemt

From JPA to NOSQL

I am trying to add a NOSQL data into my JPA-based application, following this tutorial.
The entity I want to add, was befored modeled without NOSQL in this way:
Triple.java
#Entity
#IdClass(ConceptPk.class)
#Table(name = "triple")
public class TripleDBModel {
protected List<Annotation> annotations;
public Concept conceptUriSubject;
public Concept conceptUriObject;
public Concept conceptUriPredicate;
#ManyToMany(
cascade={CascadeType.ALL },
fetch=FetchType.LAZY
)
#JoinTable(name = "triple_has_annotation",
joinColumns={#JoinColumn(name="uri_concept_subject"), #JoinColumn(name="uri_concept_object"), #JoinColumn(name="uri_concept_predicate") },
inverseJoinColumns=#JoinColumn(name="annotation_id") )
public List<Annotation> getAnnotations() {
return annotations;
}
public void setAnnotations(List<Annotation> annotations) {
this.annotations = annotations;
}
ConceptPk.java
#Embeddable
public class ConceptPk implements java.io.Serializable {
private static final long serialVersionUID = 1L;
public Concept conceptUriSubject;
public Concept conceptUriObject;
public Concept conceptUriPredicate;
#Id
#ManyToOne(cascade=CascadeType.MERGE)
#JoinColumn(name="uri_concept_subject")
public Concept getConceptUriSubject() {
return conceptUriSubject;
}
public void setConceptUriSubject(Concept conceptUriSubject) {
this.conceptUriSubject = conceptUriSubject;
}
I am omiting repetitions, but the 3 Concepts are part of the primary key, of the #Id.
Adapting this entity to NOSQL:
#Entity
#IdClass(ConceptPk.class)
#Table(name = "triple")
#NodeEntity(partial = true)
public class TripleDBModel {
//Fields referring other entities shouldn't be initialized
protected List<Annotation> annotations;
//public Concept conceptUriSubject;
//public Concept conceptUriObject;
//public Concept conceptUriPredicate;
#RelatedTo(type = "conceptUriSubject", elementClass = Concept.class)
Set<Concept> conceptUriSubject;
Now the question, which actually are two questions:
A) #RelatedTo(type = "conceptUriSubject", elementClass = Concept.class) gives me error on Eclipse, and advises me to add a cast, but this doesn't solve the error. I don't know if I must an annotation or any other additional thing to Class.java
B) As I have specified, the primery key is composed by 3 concepts, and ConceptPK.java is required. JPA modelling is ok, but I don't know how to do the same in NOSQL
Mujer,
your domain looks like it would be much easier modelled in the graph database itself. As it is RDF like triplets that are annotated here.
You are right in that Spring Data Graph right now does not support compound keys. We will look into that in the future, but I can't promise anything.
In the graph you could model your nodes being Concepts (URIs) and the type of relationship representing what you want to represent with that Concept.
(TripleDBModel) - SUBJECT -> (Concept [URI = ""])
(TripleDBModel) - PREDICATE -> (Concept [URI = ""])
(TripleDBModel) - OBJECT -> (Concept [URI = ""])
(TripleDBModel) - HAS_ANNOTATION -> (Annotation)
This could be easily modelled with Spring Data Graph (or also with the pure Neo4j API)
#NodeEntity
class Concept {
private URI uri;
}
#NodeEntity
class Triple {
// will be automatically mapped to a relationship with the name "subject"
private Concept subject;
// or provide explicit mapping
#RelatedTo(elementClass=Concept.class, type = "PREDICATE")
private Concept predicate;
private Concept object;
#RelatedTo(elementClass=Annotation.class, type = "HAS_ANNOTATION")
private Set<Annotation> annotations;
}
The eclipse error is annoying but just a wrong visualization, the AspectJ team is in the process of fixing that.
Hope that helps, if you need further advice just ask
Michael
Well, you haven't said which NoSQL engine you're going to, which is pretty important. Most NoSQL data stores don't support the concept of a composite primary key - and some of them won't allow you unique columns in the first place.
First, note that I work for a NoSQL vendor, http://gigaspaces.com/ - I'm not unbiased.
However, going from JPA to NoSQL is not hard, no matter what your engine is. For GigaSpaces, you can use JPA to talk to the data grid with very few changes, for example, although then you're still stuck with JPA.
To really think about JPA, you need to think about your data as data and not organizational stuff; you have a triplet, basically, which means your NoSQL data items consist of three data items (predicate, subject, object, like you've used.) For most NoSQL engines you'll probably want an id there, too, just for efficiency's sake.
The ID is the "primary key," and enforcing unique triplets after that is going to be on your end more than the NoSQL engine's end; this is one area where NoSQL "suffers" compared to SQL, but it's also where you find the greatest speed and storage improvements.
For some NoSQL engines, then, you'll build a document, consisting of the three data items, and you'd just query for that document before writing it into the database.
I could give you an example for many NoSQL engines (and certainly can for GigaSpaces) but I don't know which one you're targeting or why.
Have a look at Kundera.
http://mevivs.wordpress.com/2012/02/13/how-to-crud-and-jpa-association-handling-using-kundera/
Use JPA over NoSql
PlayOrm is another solution which is JPA-like but with noSQL specific features like you can do #NoSqlEmbedded on a List of Strings and it is embedded in that row leading to a table where each row is a different length than the other rows...this is a noSql common pattern which is why NoSql ORM's need a slight break from JPA.
PlayOrm also supports joins and S-SQL (scalable SQL).
later,
Dean

Categories