Spring Data Rest with Spring Security - find all by current user - rest

Is it possible to use Spring Data Rest and Spring Security to return current user related entities, using the findAll() method without specifying this user in the GET query parameter?
My only solution is to pass user as a parameter, but maybe it's another option to get him from SpringSecurityContext
public interface InvoiceRepository extends CrudRepository<Invoice, Long> {
#RestResource
#PreAuthorize("hasRole('ROLE_ADMIN') or user?.username == authentication.name")
List<Invoice> findAllByUser(#Param("user") User user);

You can use SpEL EvaluationContext extension that makes security properties and expressions available in SpEL expressions in the #Query annotations. This allows you to get only those business objects that relate to the current user:
interface SecureBusinessObjectRepository extends Repository<BusinessObject, Long> {
#Query("select o from BusinessObject o where o.owner.emailAddress like ?#{hasRole('ROLE_ADMIN') ? '%' : principal.emailAddress}")
List<BusinessObject> findBusinessObjectsForCurrentUser();
}
More details are here.

Related

Is possible to return a projection when query-by-example?

I need to write a query with many optional parameters. I don't want to write my query with many 'is-null' checking.Is that possible to return a projection when we use query-by-example in spring data jpa?
User user = new User();
user.setName("John");
Example<User> ex = Example.of(name);
repository interface
public interface EnglishNameRepos extends JpaRepository<User,Long>{
//how to custom method?
Page<UserProjection> findXXX(Example<User> ex, Pageable pageable);
}

JPA - How to add same query to a repository twice

I am trying to use the same query more than once, with different options set by annotations. Similar to:
#EntityGraph(attributePaths = {"books"})
Optional<User> findById(long id);
#EntityGraph(attributePaths = {"courses"})
Optional<User> findById(long id);
Optional<A_Projection> findById(long id);
Is there a way to add a prefix or a postfix to the method name, such that I can have different function signatures but it would be interpreted as same JPA query to avoid using #Query? Such as:
#EntityGraph(attributePaths = {"books"})
Optional<User> findByIdQ1(long id); //Q1,Q2,Q3 prefixes help distinguish these methods
#EntityGraph(attributePaths = {"courses"})
Optional<User> findByIdQ2(long id); //but they mess up the JPA syntax
Optional<A_Projection> findByIdQ3(long id);
As an example, for Projection case, this is a valid syntax:
Optional<A_Projection> findByIdByProjection(long id);
You can use EntityGraphJpaSpecificationExecutor to pass different entitygraph based on your method.
#Repository
public interface UserRepository extends JpaSpecificationExecutor<User>, JpaRepository<User, Long>, EntityGraphJpaSpecificationExecutor<User> {
}
In your service class, you can call findOne with entity graph.
List<User> users = userRepository.findOne(specification, new NamedEntityGraph(EntityGraphType.FETCH, "graphName"))
I found what I was looking for. Apparently, JPA searches for find....By in derived queries. Therefore, you can add whatever between these clauses:
Optional<User> findWithBooksById(long id); //Q1,Q2,Q3 prefixes help distinguish these methods
#EntityGraph(attributePaths = {"courses"})
Optional<User> findWithWhateverById(long id); //but they mess up the JPA syntax
Optional<A_Projection> findById(long id);
From Spring documentation:
Any text between find (or other introducing keywords) and By is considered to be descriptive unless using one of the result-limiting keywords such as a Distinct to set a distinct flag on the query to be created or Top/First to limit query results.

How to shorten names of query methods in Spring Data JPA Repositories?

Consider a Spring Data Jpa Repository:
public interface UserRepository extends JpaRepository<User, Long> {
User findOneByDeletedIsFalseAndActivationKey(String activationKey);
List<User> findAllByDeletedIsFalseAndActivatedIsFalseAndCreatedDateBefore(DateTime dateTime);
User findOneByDeletedIsFalseAndLogin(String login);
User findOneByDeletedIsFalseAndEmail(String email);
}
Notice each method has "DeletedIsFalse" in it. Is there a simple way to make method names shorter? Like i.e.:
#FullMethodName("findOneByDeletedIsFalseAndEmail")
User findOneByEmail(String email);
Use default Java 8 feature for wrapping, just like so:
interface UserInterface extends JpaRepository<User, Long> {
// use findOneByEmail instead
User findOneByDeletedIsFalseAndEmail(String email);
default User findOneByEmail(String email) {
return findOneByDeletedIsFalseAndEmail(email);
}
}
See an example.
With Kotlin, you can use extension functions, for example:
interface UserRepository : JpaRepository<User, Long> {
// use findOneByEmail instead
fun findOneByDeletedIsFalseAndEmail(email: String): User
}
fun UserRepository.findOneByEmail(email: String) =
findOneByDeletedIsFalseAndEmail(email)
Now you can use Java 8 default interface methods as #Maksim Kostromin described. But there is no such a feature in Spring.
-- Old answer
There is no such a way. You can specify any name for a method and add an annotation #Query with parameter value which holds desired query to database like this:
#Query(value="select u from User u where u.deleted=false and u.email=:email")
User findOneByEmail(#Param("email")String email);
or, with native sql query:
#Query(value="SELECT * FROM users WHERE deleted=false AND email=?1", nativeQuery=true)
User findOneByEmail(String email);
You can also use names that follow the naming convention for queries since #Query annotation will take precedence over query from method name.
#Query docs
Upd:
from Spring docs:
Although getting a query derived from the method name is quite convenient, one might face the situation in which ... the method name would get unnecessarily ugly. So you can either use JPA named queries through a naming convention ... or rather annotate your query method with #Query.

Override findAll() Spring Data Gemfire Repo Queries

I have millions of objects populated in GemFire regions. I don't want default findAll() SDR query to be executed to retrieve the millions of objects in one shot. I am trying to figure out if there is a way to override the default findAll query and provide the LIMIT param to restrict the number of objects retrieved from GemFire Regions. Here is an example of what I want to do:
NoRepositoryBean
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {
/**
* Returns all instances of the type.
*
* #return all entities
*/
Iterable<T> findAll();
}
public interface MyRepository extends CrudRepository<MyRepoObject, String> {
#Query("SELECT * FROM MyRegion LIMIT $1")
Iterable<CellTower> findAll(#Param("limit") String limit);
}
Currently, I am on Spring Data Gemfire 1.4.0.BUILD-SNAPSHOT and Spring Data REST 2.0.0.BUILD-SNAPSHOT version
This handy getting started guide on Accessing GemFire Data with REST (https://spring.io/guides/gs/accessing-gemfire-data-rest/) was written not long ago and may help with your particular use case.
The following worked for me. Try using an Integer instead of a String as your parameter to findAll
#Query("SELECT * FROM /Customer LIMIT $1")
List<Customer> findAll(#Param("limit") Integer max);

Spring MVC custom editor and select-options bad performance

Im using custom editor in Spring MVC to map string valuest to my domain objects. Simple case: User object refers to Company (User.company -> Company). In User form I register data binder:
protected void initBinder(WebDataBinder binder) throws Exception {
binder.registerCustomEditor(Company.class, new CompanyEditor(appService));
}
Editor is defined as folows:
class CompanyEditor extends PropertyEditorSupport {
private AppService appService;
public CompanyEditor(AppService appService) {
this.appService = appService;
}
public void setAsText(String text) {
Company company = appService.getCompany(text);
setValue(company);
}
public String getAsText() {
Company company = (Company) this.getValue();
if (company != null)
return company.getId();
return null;
}
}
When I use dropdown in my form
<form:select path="company">
<form:options items="${companies}" itemLabel="name" itemValue="id"/>
</form:select>
I experience severe performance problems because (to check if company is selected, I suppose) fires setAsText and getAsText for each option, which makes it to run a SQL query for each company.
I thought that setAsText is used when I commit form to make application know how to translate compnany id to Company (persisted) object. Why should it fire it in dropdowns. Any ideas how to fix it?
If your form backing object is stored as session attribute(i.e. you have something like #SessionAttributes("command") in your controller), so you can try to modify your setAsText(String text) method
public void setAsText(String text) {
Company currentCompany = (Company) this.getValue();
if ((currentCompany != null) && (currentCompany.getId().equals(text)))
return;
Company company = appService.getCompany(text);
setValue(company);
}
but I think that Spring 3.1 #Cacheable abstraction was introduced exactly for the such kind of things and is preferable
see examples in documentation
#Cacheable("books")
public Book findBook(ISBN isbn) {...}
P.S. Consider using new Converter SPI instead of Property Editors.
In general, it's possible to implement a generic converter for your look-up entities, so it will automatically convert entities from text using id if they have some specific attribute, for example, in one of my projects all #Entity types are being automatically converted using a global ConditionalGenericConverter implementation, so I neither register custom property editors during binding nor implement specific converters for types which are simple #Entity classes with #Id annotated primary keys.
Also it's very convenient when Spring automatically converts textual object ids to the actual entities when they are specified as #RequestParam annotated controller method arguments.