In older versions SDN we had following interface for repositories
org.springframework.data.neo4j.repository.RelationshipOperationsRepository;
public interface UserRelationRepository extends GraphRepository<UserEntity>, RelationshipOperationsRepository<UserEntity> {
MakeFriend rel = userRepository.getRelationshipBetween(startUser, endUser, MakeFriend.class, RelTypes.FRIEND.name());
if (rel != null) {
startUser.getFirstname() + " + " + endUser.getFirstname());
}
userRepository.createRelationshipBetween(startUser, endUser, MakeFriend.class, RelTypes.FRIEND.name());
userRepository.createRelationshipBetween(endUser, startUser, MakeFriend.class, RelTypes.FRIEND.name());
But current version does not support it. Which is the best way implement functionality like createRelationshipBetween or getRelationshipBetween in SDN?
SDN 4 does not support managing low-level graph operations using APIs.
Instead the graph operations to be performed are inferred from your domain model classes and what you do with them.
For example, create a User class as follows:
class User {
List<User> friends = new ArrayList();
}
If you now adding or remove Users in the friends list and save the User in the normal way via the standard Repository methods, this will achieve what you need automatically - the appropriate relationships will be added/removed. You don't have to tell SDN what to do because the point of an ORM/OGM is to hide you from the underlying data model and its implementation details and allow you to manipulate the domain model itself.
If you really need to perform these low-level operations directly on the graph, you use Cypher with a query method.
You can find out more about SDN 4.1 here
Related
I am working on a project Spring and Java, generated using JHipster.
I encounter a problem with the following items of Spring: Specification, Criteria, and filtering a list of items.
I have a functioning SQL query, than I want to "translate" as a Spring Specification. But, I don't find how to do it, as the query works on 3 tables, and has 2 conditions.
Basically, the work concerns 3 tables: contract, transfer & entry. A contract can be inside one or several transfers, and a transfer contains 1 to several contracts. An entry is the link between these two tables (an entry contains a contract_id and a transfer_id).
The needs is to use the specification to get a list of contracts which are linked to a not received transfer.
How can i write this?
I have already looked at several stackoverflow posts and questions, but I found answers for a join between only two tables, or how to write specifications on an entity.
Here, the query I want to translate:
SELECT c.*
FROM contract AS c
LEFT JOIN entry AS e ON e.contract_id = c.id
INNER JOIN transfer AS t ON t.id = e.transfer_id
AND t.status != 'RECEIVED'
Here, an example of the existing Contract Specification created by JHipster Here you can see how the JHipster specification are used as filter.
I want to add the new specification inside the already existing ones
private Specification<Contract> createSpecification(ContractCriteria criteria) {
Specification<Contract> specification = Specification.where(null);
if (criteria == null) {
return specification;
}
return specification.and(buildStringSpecification(criteria.getContractNumber(), Contract_.contractNumber))
.and(buildSpecification(criteria.getStatus(), Contract_.status))
.and(buildSpecification(
criteria.getStoreCode(),
root -> root.join(Contract_.store, JoinType.LEFT).get(Store_.code)));
Okay, so I think I understood more or less how your entities are designed. I created a quick project using the following JDL:
entity Contract {contractNumber String, status String}
entity Transfer {status String}
entity Entry {}
relationship OneToMany {
Transfer{entries} to Entry{transfer},
Contract{entries} to Entry{contract}
}
service all with serviceClass
filter all
This is not how I would have designed the entities, but this is how you have them on your project and also as succinct as I could manage.
After importing this JDL in a fresh jhipster project your requirement is to filter contracts by transfer status.
The first thing we need to do is create a new StringFilter in your ContractCriteria.java (my status is just a String for simplicity, if yours is an Enum then you need you create the corresponding enum filter).
ContractCriteria.java
public class ContractCriteria implements Serializable, Criteria {
// ...
private StringFilter transferStatus;
public ContractCriteria(ContractCriteria other){
// ...
this.transferStatus = other.transferStatus == null ? null : other.transferStatus.copy();
}
// ...
public StringFilter getTransferStatus() {
return transferStatus;
}
public void setTransferStatus(StringFilter transferStatus) {
this.transferStatus = transferStatus;
}
// ...
Remember to add your new filter to the hashCode() and equals() too. Once the new filter is implemented you just have to use it in your query service.
ContractQueryService.java
protected Specification<Contract> createSpecification(ContractCriteria criteria) {
Specification<Contract> specification = Specification.where(null);
if (criteria != null) {
// ...
if (criteria.getTransferStatus() != null) {
specification = specification.and(buildSpecification(criteria.getTransferStatus(),
root -> root.join(Contract_.entries, JoinType.LEFT)
.join(Entry_.transfer, JoinType.INNER)
.get(Transfer_.status)));
}
}
return specification;
}
The three relevant entities are as follow:
Contract has a property: Set<Entry> entries
Entry has properties: Transfer transfer and Contract contract
Transfer has a property: Set<Entry> entries
For quick development, jhipster comes with Swagger so you can test all your APIs live (/admin/docs with admin privileges). I leave the client side to you :)
I use spring data JPA. I need in my repository request to load only collection of concrete properties colors:
#Query(value = "SELECT cd.color FROM CalendarDetails cd where cd.userCalendar.userId = :userId")
List<String> findCalendarColorsByUserWithDuplicates(#Param("userId") Long userId);
Provided solution works correctly.
I want simplify it using spring approach to load collection of the repository objects I'd use (repository public interface CalendarDetailsRepository extends JpaRepository<CalendarDetails, Long>):
List<CalendarDetails> findByUserCalendarUserId(#Param("userId") Long userId);
But I need collection of colors! Trying
List<String> findColorByUserCalendarUserId(Long userId);
I get collection of CalendarDetails
Is it possible to improve my last request following spring data approaches to load list of colors?
You can try special Projection mechanisms that Spring Data provides. It will allow you not only to optimize your queries but also to make it with pure java without using #Query.
There are a lot of ways to
make it, but I would recommend the following.
You add an interface that contains getters for the properties that you need to take from entity:
public interface ColorOnly {
String getColor();
}
Then you return the list of this interface' objects:
List<ColorOnly> findColorByUserCalendarUserId(Long userId);
To use the colours from the interface, you just invoke getColor method. You may consider simplifying it with Java 8 streams and map conversions. BTW, this one will only query colour. No other fields will be included into the query Hibernate produces.
Try to add All
findAllByUserCalendarUserId(Long userId);
BTW, IntelliJ IDEA provide very deep support of JPA repositories, so it's prevent a lot of possible issues when you create queries like this one
I use a custom access layer (not relational, not standardized in any way) which is in the form of a jar file, like an API. This access layer provides a custom way to filter the results when I ask a
"query". I wrap this custom data access layer using the Repository design pattern, following a design similar to the one described here, in order to hide its complexity to the above layers.
Therefore, one of my repository's functions to execute queries would be something like the one following:
public class Repository {
// Other methods...
public Set<CustomEntity> get(Predicate<CustomEntity> predicate) {
// code using the predicate in order to create a custom filter string
String filterExpression = ...;
Set<CustomEntity> queryResults = CustomDataAccessLayer.query(filterExpression);
return queryResults;
}
// Other methods...
}
A typical call of the above method would be something like
Set<CustomEntity> queryResults = customEntitiesRepository.get(
p -> p.property1 == "criterio 1" && p.property2 == "criterio 2"
);
Obviously, the method get would translate the predicate with two criteria in the custom access layer's filtering mechanism. However, I don't know how to retrieve the content of the predicate in order to apply the filters to the custom access layer. Is there such a mechanism? Is it feasible to use reflection API or something like that in order to retrieve a predicate's conditions?
Edit: I could retrieve all the entities from the custom access layer API and then check if they validate the predicate criteria through the Predicate.test
method. However, this would require retrieving all data
from the custom access layer in prior (eager loading), which is exactly what I am trying to avoid. I am trying to convert the predicate into a custom access layer filter string (namely the string variable filterExpression) in order to build a "lazy loading" repository.
In my current project almost every entity has a field recordStatus which can have 2 values:
A for Active
D for Deleted
In spring data one can normally use:
repository.findByLastName(lastName)
but with the current data model we have to remember about the active part in every repository call, eg.
repository.findByLastNameAndRecordStatus(lastName, A)
The question is: is there any way to extend spring data in such a way it would be able to recognize the following method:
repository.findActiveByLastName(lastName)
and append the
recordStatus = 'A'
automatically?
Spring Data JPA provides 2 additional options for you dealing with circumstances that their DSL can't handle by default.
The first solution is custom queries with an #Query annotation
#Query("select s from MyTable s where s.recordStatus like 'A%'")
public MyObect findActiveByLastName(String lastName);
The second solution is to add a completely custom method the "Old Fashion Way" You can create a new class setup like: MyRepositoryImpl The Impl is important as it is How spring knows to find your new method (Note: you can avoid this, but you will have to manually link things the docs can help you with that)
//Implementation
public class MyRepositoryImpl implements MyCustomMethodInterface {
#PersistenceContext
EntityManager em;
public Object myCustomJPAMethod() {
//TODO custom JPA work similar to this
String myQuery = "TODO";
return em.createQuery(myQuery).execute();
}
}
//Interface
public interface MyCustomMethodInterface {
public Object myCustomJPAMethod();
}
//For clarity update your JPA repository as well so people see your custom work
public interface MySuperEpicRepository extends JPARepository<Object, String>, MyCustomMethodInterface {
}
These are just some quick samples so feel free to go read their Spring Data JPA docs if you would like to get a bit more custom with it.
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/
Finally just a quick note. Technically this isn't a built in feature from Spring Data JPA, but you can also use Predicates. I will link you to a blog on this one since I am not overly familiar on this approach.
https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/
You can use Spring Data's Specifications. Take a look at this article.
If you create a 'Base'-specification with the recordStatus filter, and deriving all other specifications form this one.
Of course, everybody in your team should use the specifactions api, and not the default spring data api.
I am not sure you can extend the syntax unless you override the base class (SimpleReactiveMongoRepository; this is for reactive mongo but you can find the class for your DB type), what I can suggest you is to extend the base methods and then make your method be aware of what condition you want to execute. If you check this post you get the idea that I did for the patch operation for all entities.
https://medium.com/#ghahremani/extending-default-spring-data-repository-methods-patch-example-a23c07c35bf9
In my db I have a number of entity classes and I run standart CRUD operations on them via Hibernate. Its not a problem to create generic dao class to make all main operations with classes. For example, in dao I have methods which look like this:
<T> List<T> loadAll(Class clazz)
Now I want to expose these methods to web-service client via Spring 3 operated web-service.
The only way I see is to implement web-methods for all entities i.e. write a class that looks like...
class BookResponse { List<BookEntity> books; }
... and return this in corresponding web-method "BookResponse getAllBooks()". This will ruin my attemts to make a code simplier by using dao with generics.
Is there are any other ways?
How can I do this without implementing web-methods for ALL my entities?
If generic web-service is not possible may be there are some other ways to resolve this task in a simple way?
UPDATE:
At the moment I am trying to implement a response class which should look like
public class ServiceResponse<T>{
#XmlElementWrapper( name = "data" )
#XmlElements( #XmlElement(name = "a", type = EntityA.class), #XmlElement(name = "b", type = EntityB.class) )
private List<T> data = new ArrayList<T>( );
//getters,setters
}
So I want to be able to insert a list of any entities mapped with annotations to this response. This produces no erros, but the response given me by web-service is empty.
I think you'll need a new POJO "GenericEntity" which can hold the information of any domain entity class instance.
It would hold a type string and an arbitrary/generic list of named attributes.
It can then be used to represent any of your real domain entities
e.g.
type = Book
attributes = (title=Order of the Phoenix, author=J K Rowling)
e.g.
type = Car
attributes = (make=Renault, model=Clio)
These examples show String attributes so you'll have to sort out if this is good enough or if you need strong typing - it's possible but harder.
You can then expose your "GenericEntity" via web services, allowing clients to make calls in and specify which domain entity they wish to search for, and even allow them to specify search criteria too.
Adds and deletes could be done in a similar way.
HTH,
David