How to allow a rest resource for logged in user only - spring-data-rest

I have following projection
#Projection(name="detail",types={Letter.class})
public interface LetterDetailProjection {
Integer getLetterId();
Date getInitiationDate();
Date getMaxResponseDate();
String getComments();
boolean isActive();
Set<RecipientLetterProjection> getRecipients();
Set<LetterDocumentMinProjection> getUploadedDocuments();
}
Letter class has a user object. how do i use this projection so that it returns only those letters which have 'user' equal to logged in user?

I did it using #Query and Spel as follows and its working:
public interface LetterRepo extends PagingAndSortingRepository<Letter, Integer> {
#Override
#Query("select l from Letter l where l.sender.appUserName = ?#{principal.username}")
Page findAll(Pageable pageable);
}
for more details visit https://spring.io/blog/2014/07/15/spel-support-in-spring-data-jpa-query-definitions

Related

LocalDate as where clause in Intellij's JPA Console

Let's say I have a Person with a name and a birth date. Entity model is as follow :
#Entity
public class Person {
private int id;
private String name;
private LocalDate birthDate;
// constructors, getters and setters...
}
With the JPA Console, I would like to write a query like this :
#Query("select p from Person p where p.birthDate between :start and :stop")
List<Person> getPeopleBornBetween(LocalDate start, LocalDate stop);
If I try the query as it is and write the date between ' , parameters do not match expected type.
I cannot use Local.parse(...) in the parameters popup window neither in the console.
I also cannot use p.birthDate.format(...) in the console.
What should I do ?
There might be a problem in you query, try this one:
select p from Person p where p.birthDate between :start and :stop
^

How to check collection for null in spring data jpa #Query with in predicate

I have this query in my spring data jpa repository:
#Query("SELECT table1 FROM Table1 table1 "
+ "INNER JOIN FETCH table1.error error"
+ "WHERE table1.date = ?1 "
+ "AND (COALESCE(?2) IS NULL OR (table1.code IN ?2)) "
+ "AND (COALESCE(?3) IS NULL OR (error.errorCode IN ?3)) ")
List<Table1> findByFilter(Date date, List<String> codes, List<String> errorCodes);
When I run this query, it shows me this error by console:
org.postgresql.util.PSQLException: ERROR: operator does not exist: character varying = bytea
Hint: No operator matches the given name and argument types. You might need to add explicit type casts.
Position: 1642
However if I run the query without the (COALESCE (?2) IS NULL OR part, just the table1.code IN ?2, it does work
Does anyone know what this error could be due to?
COALESCE with one parameter does not make sense. This is an abbreviated CASE expression that returns the first non-null operand. (See this)
I would suggest you to use named parameters instead of position-based parameters. As it's stated in the documentation this makes query methods a little error-prone when refactoring regarding the parameter position.
As it's stated in documentation related to the IN predicate:
The list of values can come from a number of different sources. In the constructor_expression and collection_valued_input_parameter, the list of values must not be empty; it must contain at least one value.
I would suggest you also avoid to use outdated Date and use instead java 8 Date/Time API.
So, taken into account all above, you should use a dynamic query as it was suggested also in comments by #SimonMartinelli. Particularly you can have a look at the specifications.
Assuming that you have the following mapping:
#Entity
public class Error
{
#Id
private Long id;
private String errorCode;
// ...
}
#Entity
public class Table1
{
#Id
private Long id;
private LocalDateTime date;
private String code;
#ManyToOne
private Error error;
// ...
}
you can write the following specification:
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.CollectionUtils;
public class TableSpecs
{
public static Specification<Table1> findByFilter(LocalDateTime date, List<String> codes, List<String> errorCodes)
{
return (root, query, builder) -> {
root.fetch("error", JoinType.LEFT);
Predicate result = builder.equal(root.get("date"), date);
if (!CollectionUtils.isEmpty(codes)) {
result = builder.and(result, root.get("code").in(codes));
}
if (!CollectionUtils.isEmpty(errorCodes)) {
result = builder.and(result, root.get("error").get("errorCode").in(errorCodes));
}
return result;
};
}
}
public interface TableRepository extends CrudRepository<Table1, Long>, JpaSpecificationExecutor<Table1>
{
default List<Table1> findByFilter(LocalDateTime date, List<String> codes, List<String> errorCodes)
{
return findAll(TableSpecs.findByFilter(date, codes, errorCodes));
}
}
and then use it:
List<Table1> results = tableRepository.findByFilter(date, Arrays.asList("TBL1"), Arrays.asList("ERCODE2")));

Spring Data JPA: CriteriaQuery to get entities with max value for each unique foreign key

There's an Event class:
#Entity
public class Event {
#Id
private Integer id;
#ManyToOne(cascade = CascadeType.ALL)
private Company company;
#Column
private Long time;
...
}
I want to have an EventFilter class (implementing Specification) which will produce CriteriaQuery to select entities the same way as the following SQL query:
SELECT *
FROM events e1
WHERE e1.time = (
SELECT MAX(time)
FROM events e2
WHERE e1.company_id = c2.company_id
)
Filtered result will contain only events with unique Company and max time value per company.
This is the EventFilter class with what I ended up with:
public class EventFilter implements Specification<Event> {
#Override
public Predicate toPredicate(Root<Event> root, CriteriaQuery<?> q, CriteriaBuilder cb) {
Subquery<Long> subquery = q.subquery(Long.class);
Root<Event> subRoot = subquery.from(Event.class);
subquery.select(cb.max(root.get("time")))
.where(cb.equal(root.get("company"), subRoot.get("company")));
return cb.equal(root.get("time"), subquery);
}
}
When EventRepository#findAll(EventFilter filter) is called, results are not filtered at all. Please help me to implement this logic correctly.
After inspecting SQL statement generated by Hibernate I've found an error: root was used instead of subRoot. The correct method body is:
Subquery<Long> sub = q.subquery(Long.class);
Root<Event> subRoot = sub.from(Event.class);
sub.select(cb.max(subRoot.get("time")))
.where(cb.equal(root.get("company"), subRoot.get("company")));
return cb.equal(root.get("time"), sub);

Ignite SqlQuery for complex java objects

In my cache I have a complex java object as below -
class Person{
private Department d;
....
}
class Department {
private Department code;
....
}
I am using below SQLQuery to read it -
SqlQuery<Short, BinaryObject> query = new SqlQuery<>(Person.class, "d.code = ?");
String args="101"; // department code
QueryCursor<Cache.Entry<Short, BinaryObject>> resultSet = personCache.query(query.setArgs(args))
I am getting below error -
Caused by: class org.apache.ignite.internal.processors.query.IgniteSQLException: Failed to parse query: SELECT "PERSON_CACHE"."PERSONENTITY"._KEY, "TPERSON_CACHE"."PERSONENTITY"._VAL FROM "PERSON_CACHE"."PERSONENTITY" WHERE id.code = ?
Am I doing anything wrong here ?
You can access nested fields, but only if they were configured with QuerySqlField annotation in advance:
class Person{
private Department d;
...
}
class Department {
#QuerySqlField
private Department code;
....
}
SqlQuery<Short, BinaryObject> query = new SqlQuery<>(Person.class, "code = ?");
Destructuring is not supported by Ignite SQL and there are no solid plans to implement it.
This means you can't peek into fields that are rich objects, maps, lists, etc. You should introduce a departmentId numeric field here.
Theoretically you could also try putting #QuerySqlField annotation on Department's field code, and then access it as CODE = ?. Your mileage may vary. I for one would like to hear about the result of such experiment.
I resolved it by using predicate.
IgniteBiPredicate<Long, BinaryObject> predicate = new IgniteBiPredicate<Long, BinaryObject>() {
#Override
public boolean apply(Long e1, BinaryObject e2) {
Person p= e2.deserialize();
short s = (short) args[0];
return p.getId().getCode == s;
}
};

Hibernate search boolean filter

I have book entry:
#Entity
#Indexed
public class Book extends BaseEntity {
#Field
private String subtitle;
#DateBridge(resolution = Resolution.DAY)
private Date publicationDate;
#Field
private int score;
#IndexedEmbedded
#ManyToMany(fetch = FetchType.EAGER)
#Cascade(value = {CascadeType.ALL})
private List<Author> authors = new ArrayList<Author>();
#Field
#FieldBridge(impl = BooleanBridge.class)
private boolean prohibited;
And filter by boolean field "phohibited"
public class BFilter extends Filter {
#Override
public DocIdSet getDocIdSet(IndexReader indexReader) throws IOException {
OpenBitSet bitSet = new OpenBitSet(indexReader.maxDoc());
TermDocs termDocs = indexReader.termDocs(new Term("prohibited","false"));
while (termDocs.next()) {
bitSet.set(termDocs.doc());
}
return bitSet;
}
}
Search method
public List<T> findByQuery(Class c, String q) throws InterruptedException {
FullTextSession fullTextSession = Search.getFullTextSession(session);
fullTextSession.createIndexer().startAndWait();
QueryBuilder qb = fullTextSession.getSearchFactory().buildQueryBuilder().forEntity(c).get();
Query luceneQuery = qb
.keyword()
.fuzzy()
.onFields("title", "subtitle", "authors.name", "prohibited", "score")
.matching(q)
.createQuery();
FullTextQuery createFullTextQuery = fullTextSession.createFullTextQuery(luceneQuery, Book.class, BaseEntity.class);
createFullTextQuery.setFilter(new BFilter());
return createFullTextQuery.list();
}
if I apply that filter - search result is empty. Entries in the database 100% there. What am I doing wrong? If you replace the filter field to "score" that all works, and the result is not empty. Do not search it on a Boolean field
The basic approach looks ok. A couple of comments. You are calling the indexer for each findByQuery call. Not sure whether this is just some test code, but you should index before you search and only once or when things change (you can also use automatic index updates). It might also be that depending on your transaction setup, your search cannot see the indexed data. However, you seem to say that all works if you don't use a filter at all. In this case I would add some debug to the filter or debug it to see what's going on and if it gets called at all. Last but not least, you don't need to explicitly set explicitly #FieldBridge(impl = BooleanBridge.class).