Where is a listing of named hibernate queries? - spring-boot

I want to find the highest-value id in my entity table. Reading through other posts I found you can construct query methods such as:
public interface ArticleRepository extends CrudRepository<Article, Integer> {
public List<Article> findByOrderByIdDesc();
}
These method gets constructed out of keyword modules, and I saw someone else had made one that returned the top 10 results only. I'd like to find a listing of the base keywords, so I can try to optimize the call (it seems to be running a bit slow). I don't know if it will help, but I'd first like to try something like findFirstByOrderByIdDesc()

the documentation is in Spring Data JPA: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods and I see the FindFirst mentioned here: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repository-query-keywords
For your needs, how about findFirst5OrderByIdDesc()?

Related

2022-07-20: Repository pattern - Why exactly do we need Interfaces?

I know this has been asked quite a long time.
This was answered at 2012-03-16.
Repository pattern - Why exactly do we need Interfaces?
I never used repository one month ago. I use laravel: Controller, Service, Model, View.
Several months ago, I start to use trait. So many articles talk about interface, ok, one month ago, I start to use repository with interfaces. Now I feels that I'm doing things that seems not necessary.
There is Order model I forgot to draw. And I have to bind interface with repository in RepositoryServiceProvider
public function register()
{
$this->app->bind(RepositoryInterface::class, Repository::class);
$this->app->bind(MemberRepositoryInterface::class, MemberRepository::class);
$this->app->bind(OrderRepositoryInterface::class, OrderRepository::class);
$this->app->bind(OrderItemRepositoryInterface::class, OrderItemRepository::class);
//...
}
Now go back to that question's answer in 2012. Why we need to use interfaces? Because:
public class EmployeeRepositoryEF: IEmployeeRepository
{
public Employee[] GetAll()
{
//here you will return employees after querying your EF DbContext
}
}
public class EmployeeRepositoryXML: IEmployeeRepository
{
public Employee[] GetAll()
{
//here you will return employees after querying an XML file
}
}
public class EmployeeRepositoryWCF: IEmployeeRepository
{
public Employee[] GetAll()
{
//here you will return employees after querying some remote WCF service
}
}
But with the bindings in RepositoryServiceProvider, we can not use these different repositories at the same time. I cannot imaging how. We have to change the bindings. But if so, why not just change the type hint in service layer?
Ok, I saw many articles actually use:
Controller > SomeRepositoryInterface $someRepository > Model
They don't have service layer.
Does it mean, since I have service layer, So I don't need interface?
Controller > SomeService $someService> SomeRepository $someRepository > Model
If we want to change repository, just do:
In SomeService:
use App\Repositories\Abc\SomeRepository;
or
use App\Repositories\Xyz\SomeRepository;
Then
use App\Repositories\Eloquent\Sale\OrderRepository;
use App\Repositories\Eloquent\Sale\OrderItemRepository;
use App\Repositories\Eloquent\Sale\RmaRepository;
use App\Repositories\Eloquent\Member\MemberRepository;
use App\Repositories\Eloquent\Member\MemberGroupRepository;
or
use App\Repositories\MSSQL\Sale\OrderRepository;
use App\Repositories\MSSQL\Member\MemberRepository;
or
use App\Repositories\Oracle\Sale\OrderRepository;
use App\Repositories\Oracle\Member\MemberRepository;
Eloquent can change driver to use mssql or oracle. Then...
use App\Repositories\DbBuilder\Sale\OrderRepository;
use App\Repositories\DbBuilder\Member\MemberRepository;
or
use App\Repositories\RawSql\Sale\OrderRepository;
use App\Repositories\RawSql\Member\MemberRepository;
or
use App\Repositories\AnyOtherKind\Sale\OrderRepository;
use App\Repositories\AnyOtherKind\Member\MemberRepository;
Can someone give me some suggestion?
Because we need a contract for the classes that people create over time. They should implement the same thing, and the customer should be able to use them interchangeably. If you don't have an interface, the next developer might forget implementing some methods and this means bad!
For example we have implemented our repositories to work with MySQL, and we exit the company. After a year, they plan for using something else rather than MySQL, so they have to implement new repositories that are compatible with the previous repositories and this is why we need an interface.
I hope my answer is simple and clear.
To begin with, using Repository pattern is bad with Active Record (which is Eloquent). AR (active record) models already have all the methods to all possible CRUD operations and scopes to encapsulate logic within. Using repositories over them is a good example of overengineering, so I'd recommend not using them at all in Laravel.
When working with DM (data-mapper) models, repositories are used to switch between different databases (like in your example in RepositoryServiceProvider). So, in case there is a need to change database over the project, you just create another implementation of repository for different database type. And again, in Laravel this is already done at query builder level, so you just don't need to do that by yourself.

In spring data elasticsearch, how can I project or add a calculated field on all results?

I am using spring-data-elasticsearch (latest version) along with a docker instance of elasticsearch (latest version), and I want to calculate a field on all results that are returned from my repository after a query. I do not want this information in the repository, because it is sometimes query dependent, and sometimes environment dependent. For example, if we perform a query, I want to generate a URL that includes the query terms as query parameters in the URL that I want to enrich the result with. There are some other cases, too. I have tried creating a spring data custom reading converter that accepts the whole document object. I can see that it is recognized when the application starts, but it is never invoked. How can I either project a field with a custom value, or enrich the returned documents with a contextually calculated value?
I first thought about AfterConvertCallback as well like Chin commented, but in a callback you have no context of the query that was run to get the entity, so you cannot use things like query terms to build something.
I would add the property - let's name it url of type String here - to the entity and mark it with the org.springframework.data.annotation.Transient annotation to prevent it from being stored.
Then in the method where you do the search, either using ElasticsearchOperations or a repository, postprocess the returned entites (code not tested, just written down here):
SearchHits<Entity> searchHits = repository.findByFoo(String fooValue);
searchHits.getSearchHits().forEach(searchHit -> {
searchHit.getContent().setUrl(someValueDerivedFromEnvironemtAndQuery);
});
After that proceed using the SearchHits.
I like a hybrid approach of combining the answers from both #ChinHuang and #PJMeisch. Both answers have their applicability, depending on the context or situation. I like Chin Huang's suggestion for instance-based information, where you would need things like configuration values. I also agree that PJ Meisch is correct in his concern that this does not give you access to the immediate query, so I like his idea of intercepting/mapping the values when the data is being returned from the data store. I appreciate the great information from both people, because this combination of both approaches is a solution that I am happy with.
I prefer to use a repository interface wherever possible, because many people incorrectly mix business logic into their repositories. If I want custom implementation, then I am forced to really think about it, because I have to create an "Impl" class to achieve it. This is not the gravest of errors, but I always accompany a repository with a business service that is responsible for any data grooming, or any programmatic action that is not strictly retrieval, or persistence, of data.
Here is the part of my module configuration where I create the custom AfterConvertCallback. I set the base URL in the onAfterConvert method:
#Bean
AfterConvertCallback<BookInfo> bookInfoAfterConvertCallback() {
return new BookInfoAfterConvertCallback(documentUrl);
}
static class BookInfoAfterConvertCallback implements AfterConvertCallback<BookInfo> {
private final String documentUrl;
public BookInfoAfterConvertCallback(String documentUrl) {
this.documentUrl = documentUrl;
}
#Override
public BookInfo onAfterConvert(final BookInfo entity, final Document document, final IndexCoordinates indexCoordinates) {
entity.setUrl(String.format("%s?id=%d", documentUrl, entity.getId()));
return entity;
}
}
In the data service that invokes the repository query, I wrote a pair of functions that creates the query param portion of the URL so that I can append it in any applicable method that uses the auto-wired repository instance:
/**
* Given a term, encode it so that it can be used as a query parameter in a URL
*/
private static final Function<String, String> encodeTerm = term -> {
try {
return URLEncoder.encode(term, StandardCharsets.UTF_8.name());
} catch (UnsupportedEncodingException e) {
log.warn("Could not encode search term for document URL", e);
return null;
}
};
/**
* Given a list of search terms, transform them into encoded URL query parameters and append
* them to the given URL.
*/
private static final BiFunction<List<String>, String, String> addEncodedUrlQueryParams = (searchTerms, url) ->
searchTerms.stream()
.map(term -> String.format("term=%s", encodeTerm.apply(term)))
.filter(Objects::nonNull)
.collect(Collectors.joining("&", url + "&", ""));
This absolutely can all be done in a repository instance, or in its enclosing service. But, when you want to intercept all data that is retrieved, and do something with it that is not specific to the query, then the callback is a great option because it does not incur the maintenance cost of needing to introduce it in every data layer method where it should apply. At query time, when you need to reference information that is only available in the query, it is clearly a matter of introducing this type of code into your data layer (service or repo) methods.
I am adding this as an answer because, even though I didn't realize it at the time that I posted my question, this is two concerns that are separate enough to warrant both approaches. I do not want to claim credit for this answer, so I will not select it as the answer unless you both comment on this, and tell me that you want me to do that.

Difference between CrudRepository findOne() and JpaRepository getOne()

I read that getOne() is lazy loaded and findOne() fetches the whole entity right away. I've checked the debugging log and I even enabled monitoring on my sql server to see what statements gets executed, I found that both getOne() and findOne() generates and executes the same query. However when I use getOne() the values are initially null (except for the id of course).
So could anyone please tell me, if both methods executes the same query on the database, why should I use one over the other? I'm basically looking for a way to fetch an entity without getting all of its children/attributes.
EDIT1:
Entity code
Dao code:
#Repository
public interface FlightDao extends JpaRepository<Flight, Long> {
}
Debugging log findOne() vs getOne()
EDIT2:
Thanks to Chlebik I was able to identify the problem. Like Chlebik stated, if you try to access any property of the entity fetched by getOne() the full query will be executed. In my case, I was checking the behavior while debugging, moving one line at a time, I totally forgot that while debugging the IDE tries to access object properties for debugging purposes (or at least that's what I think is happening), so debugging triggers the full query execution. I stopped debugging and then checked the logs and everything appears to be normal.
getOne() vs findOne() (This log is taken from MySQL general_log and not hibernate.
Debugging log
No debugging log
It is just a guess but in 'pure JPA' there is a method of EntityManager called getReference. And it is designed to retrieve entity with only ID in it. Its use was mostly for indicating reference existed without the need to retrieve whole entity. Maybe the code will tell more:
// em is EntityManager
Department dept = em.getReference(Department.class, 30); // Gets only entity with ID property, rest is null
Employee emp = new Employee();
emp.setId(53);
emp.setName("Peter");
emp.setDepartment(dept);
dept.getEmployees().add(emp);
em.persist(emp);
I assume then getOne serves the same purpose. Why the queries generated are the same you ask? Well, AFAIR in JPA bible - Pro JPA2 by Mike Keith and Merrick Schincariol - almost every paragraph contains something like 'the behaviour depends on the vendor'.
EDIT:
I've set my own setup. Finally I came to conclusion that if You in any way interfere with entity fetched with getOne (even go for entity.getId()) it causes SQL to be executed. Although if You are using it only to create proxy (eg. for relationship indicator like shown in a code above), nothing happens and there is no additional SQL executed. So I assume in your service class You do something with this entity (use getter, log something) and that is why the output of these two methods looks the same.
ChlebikGitHub with example code
SO helpful question #1
SO helpful question #2
Suppose you want to remove an Entity by id. In SQL you can execute a query like this :
"delete form TABLE_NAME where id = ?".
And in Hibernate, first you have to get a managed instance of your Entity and then pass it to EntityManager.remove method.
Entity a = em.find(Entity.class, id);
em.remove(a);
But this way, You have to fetch the Entity you want to delete from database before deletion. Is that really necessary ?
The method EntityManager.getReference returns a Hibernate proxy without querying the database and setting the properties of your entity. Unless you try to get properties of the returned proxy yourself.
Method JpaRepository.getOne uses EntityManager.getReference method instead of EntityManager.find method. so whenever you need a managed object but you don't really need to query database for that, it's better to use JpaRepostory.getOne method to eliminate the unnecessary query.
If data is not found the table for particular ID, findOne will return null, whereas getOne will throw javax.persistence.EntityNotFoundException.
Both have their own pros and cons. Please see example below:
If data not found is not failure case for you (eg. You are just
verifying if data the data is deleted and success will be data to be
null), you can use findOne.
In another case, you can use getOne.
This can be updated as per your requirements, if you know outcomes.

How do I implement fine-grained access control in Spring-Data-Rest?

TL;DR: How do I implement fine-grained access control in the flattened REST api approach that Spring-Data-Rest gives us?
So - I'm making an API using Spring-Data-Rest where there's three main access levels:
1) The admin - can see/update all groups
2) An owner of a group - can see/update the group and everything under it
3) An owner of a sub-group - can see/update only his group. No recursive nesting, just one sub-level allowed.
And 'group' is exposed as a resource (has a crud repository).
So far so good - and I've implemented some access control for modification using a Repository Event Handler - so on the create/write/delete side I think I'm fine.
Now I need to get to the point of limiting visibility of some of the items. This is ok for getting a single item since I can use Pre/Post Authorize annotations and reference the principal.
The problem lies in the findAll() methods - I don't have an easy hook to filter out the specific instances I don't want exposed based on the current principal. For example - a sub-group owner could see all groups by doing GET /groups. They should ideally have the items they don't have access to not even be visible at all.
To me this sounds like writing custom #Query() annotations on the repository interfaces, but that doesn't seem doable because:
I need to reference the principal in the query. SPeL is supposed to be supported, but doesn't seem to work at all with ?# expressions (despite this blog post suggesting otherwise: https://spring.io/blog/2014/07/15/spel-support-in-spring-data-jpa-query-definitions). I am using spring-boot with 1.1.8.RELEASE and the Evans-RELEASE train for spring-data generally.
The kind of query I need to write is going to be different depending on the access level, which can't realistically be encompassed in a single JPQL statement (if admin select all groups, else get all (sub)groups associated with the principal's user).
Therefore it sounds like I need to write some custom repository implementations for that and just reference the principal in code. Well that's ok - but it seems like a lot of work for each repository that I need to control the access to (I think this will be almost all of them). This applies to findAll and various custom search methods.
Am I approaching this wrong? Is there another approach to dynamically limiting item visibility based on the currently logged-in user that would work better? In a flat namespace like spring-data-rest exposes, I would imagine this would be a common problem.
In a prior design I just solved it by exposing everything under /api/groups/{groupId}/... and had a sub-resource locator act as a single pinch-point to control access to anything under it. No such luck in spring-data-rest.
Update: now stumbling with a custom method overriding findAll() (this works for other methods defined on my custom interface). Though this might be a separate question - I'm blocked right now. Spring-data is just not calling this when I do a GET /groups, but calling the original. Oddly enough it does use my query if I define one on the interface and mark it with #Query (perhaps custom overrides of built-in methods aren't supported anymore?).
public interface GroupRepository extends JpaRepository<Group, Long>, GroupCustomRepository {}
public interface GroupCustomRepository {
Page<Group> findAll(Pageable pageable);
}
public class GroupCustomRepositoryImpl extends SimpleJpaRepository<Group, Long> implements GroupCustomRepository {
#Inject
public GroupCustomRepositoryImpl(EntityManager em) {
super(Group.class, em);
}
#Override
public Page<Group> findAll(Pageable pageable) {
MyPrincipal principal = (MyPrincipal) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Page<Group> result;
if (principal.isAdmin()) {
result = findAll(pageable);
} else {
Specification<Group> spec = (root, query, cb) -> cb.or(
cb.equal(root, principal.getGroup()),
cb.and(cb.isNotNull(root.get(Group_.parentGroup)), cb.equal(root.get(Group_.parentGroup), principal.getGroup()))
);
result = findAll(spec, pageable);
}
return result;
}
}
Update 2: Since I can't access the principal in the #Query, and I can't override it with a custom method, I'm at a brick wall. #PostFilter doesn't work either because the return object is a Page rather than a collection.
I've decided to just wall-off the /groups to admins only, and have everyone else use different approaches (/groups/search/somethingSpecific) with #PostFilters/#PostAuthorizations.
This doesn't seem like it meshes very well with the HAL approach though. Interested in how other people are solving these kinds of issues with Spring-data-rest.
We ended up approaching this as follows:
We created a custom aspect which sits in front of the CRUD methods on a repository. It then looks up and calls an associated 'authorization handler' which is annotated on the repository that dynamically manages authorization details.
We had to be pretty heavy-handed when it came to limiting results in a findAll() query (eg: looking at /users) - essentially, only admins could list all of anything sensitive. Otherwise limited users had to use query methods for specific items.
We created some reusable authorization-related classes, and use those in certain scenarios - particularly custom queries, eg:
#PreAuthorize("#authorizations.systemAdminRead()")
#Query("select u FROM User r where ...")
List findAll();
#PostAuthorize("#otherAuthorizationHandler.readAllowed(returnObject)") ResponseObject someQuery();
All in all, it works - but it feels very clunky, and it's easy to miss things. I do wish this was baked-in to the framework more, even being able to dynamically adjust the default queries would be useful (when I was attempting this, I wasn't able to have the queries updated appropriately with #Query).
We happen to be using PostgreSQL, so the upcoming row level security (http://michael.otacoo.com/postgresql-2/postgres-9-5-feature-highlight-row-level-security/) would have fit the bill nicely, assuming we could feed it the proper authorization details via the DB connection.

Native select...in() clause with Spring Data JPA

I have found that using a very common query like this:
#Query(nativeQuery=true, value="select * from questions where id in (22,45,65,777,444)") results in a list of objects instead of a list of questions object.
[[Ljava.lang.Object;#49fd8d8f, [Ljava.lang.Object;#2558cd3c, [Ljava.lang.Object;#690a314b]
How is this situation going to be fixed?
How can I manage this right now?
Thanks!
Edit:
#RooJpaRepository(domainType = Challenge.class)
public interface ChallengeRepository extends ChallengeRepositoryCustom
{
#Query(nativeQuery=true, value="select * from questions where id in (22,45,65,777,444)")
List<Questions> chooseRandomOldQuestions();
}
I strongly feel like this has nothing to do with the in clause but the types you use.
Your repository is typed to the Challenge domain class. Question (I guess you mean that rather than Questions) is probably not a sub type of Challenge right? If this is the case we do not hand the domain type to the EntityManager to accommodate scenarios where people execute projections to Long etc. and the query execution would fail if we handed a non-managed type to the query execution.
The workaround is to create a dedicated repository managing Question(s) instances (still not sure this is a typo or a separate class) should do the trick.

Resources