Does Spring Data intentionally allow for unknown tokens when generating queries from method names? - spring-data-jpa

In one of my Spring Data repositories I wanted to have two autogenerated methods for finding a single instance by ID, one returning the full entity (Student) and the other a slimmer projection (StudentView). Now obviously Java won't let me have two methods that differ only in return type:
Optional<Student> findById(String id);
Optional<StudentView> findById(String id);
I could have named one of the methods to whatever else I wanted and manually provided it with the required query via a #Query annotation, but I wanted to make use of method-name autogeneration. Now Spring Data does allow for some flexibility in how it parses the method name, and so I could have named one of the methods as (say) findOneById() to distinguish it from the other, but that didn't feel right. However in the course of experimentation I discovered that I could name the second method as findViewById(), that is containing a token View that is not one of the keywords known to the method-name parser:
Optional<Student> findById(String id);
Optional<StudentView> findViewById(String id);
Does the method-name parser intentionally allow for this (I cannot find mention of it in the documentation), or am I merely exploiting an accident of implementation?

This looks bizarre, but it looks like in Spring-Boot 2.2.2.RELEASE you can use whatever word you want between find and ById.
Optional<Student> findWhateverById(Long Id) will return record of student table with provided id.
Either I'm missing something or it's indeed a bug.

Related

Simple Tagging Implementation with Spring Data JPA/Rest

I am trying to come up with a way of implementing tags for my entity that works well for me and need some help in the process. Let me write down some requirements I have in mind:
Firstly, I would like tags to show in entities as a list of strings like this:
{
"tags": ["foo", "bar"]
}
Secondly, I need to be able to retrieve a set of available tags across all entities so that users can easily choose from existing tags.
The 2nd requirement could be achieved by creating a Tag entity with the value of the Tag as the #Id. But that would make the tags property in my entity a relation that requires an extra GET operation to fetch. I could work with a getter method that resolves all the Tags and returns only a list of strings, but I see two disadvantages in that: 1. The representation as a list of strings suggests you could store tags by POSTing them in that way which is not the case. 2. The process of creating an entity requires to create all the Tags via a /tags endpoint first. That seem rather complicated for such a simple thing.
Also, I think I read somewhere that you shouldn't create a repository for an entity that isn't standalone. Would I create a Tag and only a Tag at any point in time? Nope.
I could store the tags as an #ElementCollection in my entity. In this case I don't know how to fulfill the 2nd requirement, though.
#ElementCollection
private Set<String> tags;
I made a simple test via EntityManager but it looks like I cannot query things that are not an #Entity in a result set.
#RestController
#RequestMapping("/tagList")
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class TagListController implements RepresentationModelProcessor<RepositoryLinksResource> {
#PersistenceContext
private final #NonNull EntityManager entityManager;
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<EntityModel<TagList>> get() {
System.out.println(entityManager.createQuery("SELECT t.tags FROM Training t").getFirstResult());
EntityModel<TagList> model = EntityModel.of(new TagList(Set.of("foo", "bar")));
model.add(linkTo(methodOn(TagListController.class).get()).withSelfRel());
return ResponseEntity.ok(model);
}
}
org.hibernate.QueryException: not an entity
Does anyone know a smart way?
The representation as a list of strings suggests you could store tags by POSTing them in that way which is not the case
This is precisely the issue with using entities as REST resource representations. They work fine until it turns out the internal representation (entity) does not match the external representation (the missing DTO).
However, it would probably make most sense performance-wise to simply use an #ElementCollection like you mentioned, because you then don't have the double join with a join table for the many-to-many association (you could also use a one-to-many association where the parent entity and the tag value are both part of the #Id to avoid a join table, but I'm not sure it's convenient to work with. Probably better to just put a UNIQUE(parent_id, TAG) constraint on the collection table, if you need it). Regarding the not an entity error, you would need to use a native query. Assuming that you have #ElementCollection #CollectionTable(name = "TAGS") #Column(name = "TAG") on tags, then SELECT DISTINCT(TAG) FROM TAGS should do the job.
(as a side note, the DISTINCT part of the query will surely introduce some performance penalty, but I would assume the result of that query is a good candidate for caching)

$select and $expand exposes sensitive data in Web API

I am trying to hide my PasswordHash field from User table by marking it with [JsonIgnore] attribute. localhost/User query gets me a list of users and excludes PasswordHash from the results, but if I write my query as such: localhost/User?$select=PasswordHash, it returns data, even though it should ignore it!!!
I am exposing my Entity Framework table User using an ApiController with [EnableQuery] GetAll(); method, which returns IQueryable<User> as result.
Please help!
I fully agree with the Brian Rogers' suggestion to use a DTO that exposes only necessary fields. It's cheep and secure.
If you decide to keep the existing model, you may look at EDM Security guide which suggests to use the [IgnoreDataMember] attribute.

How to find entity with any value of argument

I'm using Spring JPA with hibernate and have an entity with a lot of properties, let's say it has five; as illustrated below:
#Entity
#Table
public class MyEntity{
Object properties1;
Object properties2;
Object properties3;
Object properties4;
Object properties5;
}
Spring provides a very nice feature; it generates JPQL query based on method name in the repository. For example:
List<MyEntity> findByProperties3(Object properties3);
In my situation, users have an html form to search for a MyEntity. This html form has five fields respectively which correspond to the five properties on the MyEntity class. User also can leave any field empty so that the search will include all values of this property in the query.
I have idea of how to implement that but it would break away from the Spring convenience methods and need a lot of coding. My idea is to create a method on the repository interface for all possibilities: user leaving all field empty, filling one field, two fields, etc; up to five fields. Unfortunately, that means that there would be:
possibilities. How can I avoid this path? Ideally, I would create just one method:
List<MyEntity> findByProperties1andProperties2andProperties3andProperties4andProperties5(Object p1,Object p2,Object p3,Object p4,Object p5)
But, if some of the pXs are null, then Spring JPA will explicitly find MyEntitys with propertiesX equal to null, as opposed to all possible values of, say, 1, 2, 3, 4, 5 and null.
===========EDIT===============
I am still hoping to get an answer from someone about a Spring JPA solution, but I've used javax.persistence.criteria.CriteriaBuilder in the mean time for my solution.

Attempting to use EF/Linq to Entities for dynamic querying and CRUD operations

(as advised re-posting this question here... originally posted in msdn forum)
I am striving to write a "generic" routine for some simple CRUD operations using EF/Linq to Entities. I'm working in ASP.NET (C# or VB).
I have looked at:
Getting a reference to a dynamically selected table with "GetObjectByKey" (But I don't want anything from cache. I want data from database. Seems like not what this function is intended for).
CRM Dynamic Entities (here you can pass a tablename string to query) looked like the approach I am looking for but I don't get the idea that this CRM effort is necessarily staying current (?) and/or has much assurance for the future??
I looked at various ways of drilling thru Namespaces/Objects to get to where I could pass a TableName parameter into the oft used query syntax var query = (from c in context.C_Contacts select c); (for example) where somehow I could swap out the "C_Contacts" TEntity depending on which table I want to work with. But not finding a way to do this ??
Slightly over-simplyfing, I just want to be able to pass a tablename parameter and in some cases some associated fieldnames and values (perhaps in a generic object?) to my routine and then let that routine dynamically plug into LINQ to Entity data context/model and do some standard "select all" operations for parameter table or do a delete to parameter table based on a generic record id. I'm trying to avoid calling the various different automatically generated L2E methods based on tablename etc...instead just trying to drill into the data context and ultimately the L2E query syntax for dynamically passed table/field names.
Has anyone found any successful/efficient approaches for doing this? Any ideas, links, examples?
The DbContext object has a generic Set() method. This will give you
from c in context.Set<Contact>() select c
Here's method when starting from a string:
public void Test()
{
dynamic entity = null;
Type type = Type.GetType("Contract");
entity = Activator.CreateInstance(type);
ProcessType(entity);
}
public void ProcessType<TEntity>(TEntity instance)
where TEntity : class
{
var result =
from item in this.Set<TEntity>()
select item;
//do stuff with the result
//passing back to the caller can get more complicated
//but passing it on will be fine ...
}

possible to return only one column using JPA

I have an Open JPA entity and it successfully connects a many-to-many relationship. Right now I successfully get the entire table, but I really only want the ID's from that tables. I plan on calling the database later to reconstruct the entities that I need (according to the flow of my program).
I need only the ID's (or one column from that table).
1) Should I try and restrict this in my entity beans, or in the stateless session beans that I will be using to call the entity beans
2) If I try and do this using JPA, how can I specify that I only get back the ID's from the table, instead of the whole table? So far looking online, I don't see a way that you can do this. So I am guessing there is no way to do this.
3) If I simply just manipulate the return values, should I create a separate class that I will be returning to the user that will return only the required id list to the user?
I could be completely wrong here, but from the looks of it, I don't think there is a simple way to do this using JPA and I will have to return a custom object instead of the entity bean to the user (this custom object would only hold the id's as opposed to the whole table as it currently does)
Any thoughts... I don't think this is really relevant, but people are always asking for code, so here you go...
#ManyToMany(fetch=FetchType.EAGER)
#JoinTable(name="QUICK_LAUNCH_DISTLIST",
joinColumns=#JoinColumn(name="QUICK_LAUNCH_ID"),
inverseJoinColumns=#JoinColumn(name="LIST_ID"))
private List<DistributionList> distributionlistList;
Currently how I get the entire collection of records. Remember I only want the id...
try
{
//int daSize = 0;
//System.out.println("Testing 1.2..3...! ");
qlList = emf.createNamedQuery("getQuickLaunch").getResultList();
}
This is how I call the Entity beans. I am thinking this is where I will have to programatically go through and create a custom object similar to the entity bean (but it just has the ID's and not the whole table, and attempt to put the id's in there somewhere.
What are your thoughts?
Thanks
I believe I just figured out the best solution to this problem.
This link would be the answer:
my other stack overflow answer post
But for the sake of those too lazy to click on the link I essentially used the #ElementCollection attribute...
#ElementCollection(fetch=FetchType.EAGER)
#CollectionTable(name="QUICK_LAUNCH_DISTLIST",joinColumns=#JoinColumn(name="QUICK_LAUNCH_ID"))
#Column(name="LIST_ID")
private List<Long> distListIDs;
That did it.
Sounds like you want something like this in your quickLaunch class:
#Transient
public List<Integer> getDistributionListIds () {
List<Integer> distributionListIds = new LinkedList<Integer>();
List<DistributionList> distributionlistList = getDistributionlistList();
if (distributionlistList != null) {
for (DistributionList distributionList : distributionlistList)
distributionListIds.add(distributionList.getId());
}
return distributionListIds;
}
I had to guess a little at the names of your getters/setters and the type of DistributionList's ID. But basically, JPA is already nicely handling all of the relationships for you, so just take the values you want from the related objects.