Disable query creation from method name - use of projections - spring-data

I would like to use the Spring Data Projection technique in order to extract from a table only some fields (and not all fields of the table).
As described in the documentation (https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections) I created a simple interface, for example:
interface NamesOnly {
String getFirstname();
String getLastname();
}
But I have some problems to use it.
Problem 1:
First of all, I would like to use the name findAll() to create a query that finds all rows with only two fields (firstName and lastName):
#Repository
public interface PersonaRepository extends JpaRepository<Persona, Long> {
List<NamesOnly> findAll();
}
But in this case I have these errors (maybe because findAll() is a method of the JpaRepository):
implements org.springframework.data.jpa.repository.JpaRepository.findAll
The return type is incompatible with JpaRepository.findAll()
Problem 2:
Ok, so I try to change the name of the method to findAllOnlyNames():
#Repository
public interface PersonaRepository extends JpaRepository<Persona, Long> {
List<NamesOnly> findAllOnlyNames();
}
But now I have this error:
Caused by:
org.springframework.data.mapping.PropertyReferenceException: No
property findAllOnlyNames found for type Persona!
Because Spring tries to create a query from the name.
1) Could it be possible to reuse the method name findAll() without having problems with JpaRepository?
2) Could it be possible to turn off the query creation from the method name (only for some queries, not for all projects or repositories)?

You are on the right track, your findAll() is in conflict with the ones specified on the existing Spring Data interfaces and you can rename it (as you tried) but it still has to be a name that is compatible with the query derivation mechanism. Try this instead:
#Repository
public interface PersonaRepository extends JpaRepository<Persona, Long> {
List<NamesOnly> findAllOnlyNamesBy();
}
This part of the Spring Data JPA documentation explains how the query creation process works:
The mechanism strips the prefixes find…By, read…By, query…By, count…By, and get…By from the method and starts parsing the rest of it.
So you just need to add the By keyword in the method name, anything after that keyword is treated as a condition, in this case there is no condition so it fetches everything.
To disable the query derivation from the method name you would need to add an #Query(...) annotation to the method and specify either a JPA or native query instead.

You can specify an explicit query rather than rely on it being derived from the method name.
#Repository
public interface PersonaRepository extends JpaRepository<Persona, Long> {
#Query("select p from Persona p")
List<NamesOnly> findAllOnlyNames();
}
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.at-query
Overriding findAll() (even in the unlikely event it is possible) is probably a bad idea.

Related

How to completely customize the way that type information gets written to a document by Spring Data MongoDB?

Is it possible to use different type attribute (instead of _class) for each polymorphic collection like it's implemented in Doctrine(PHP) or Jackson libraries? Current solution allows to store type information in document field. By default it is a full class name stored in the field named _class.
We can easy change it to save some custom string (alias) instead of full class name and change default discriminator field name from _class to something else.
In my situation I'm working on legacy database while legacy application is still in use. Legacy application uses Doctrine (PHP) ODM as datalayer.
Doctrine allows to define discriminator field name (_class in SpringData) by annotation and have it different for each collection.
In Spring Data when I pass typeKey to DefaultMongoTypeMapper it used for all collections.
Thanks.
// MyCustomMongoTypeMapper.java
// ...
#SuppressWarnings("unchecked")
#Override
public <T> TypeInformation<? extends T> readType(DBObject source, TypeInformation<T> basicType) {
Assert.notNull(basicType);
Class<?> documentsTargetType = null;
Class<? super T> parent = basicType.getType();
while (parent != null && parent != java.lang.Object.class) {
final String discriminatorKey = getDiscriminatorKey(parent); //fetch key from annotation
if (null == discriminatorKey) {
parent = parent.getSuperclass();
} else {
accessor.setKey(discriminatorKey);
return super.readType(source, basicType);
}
}
accessor.resetKey();
return super.readType(source, basicType);
}
Something that should work for you is completely exchanging the MongoTypeMapper instance that MappingMongoConverter uses. As you discovered the already available implementation assumes a common field name and takes yet another strategy to either write the fully-qualified class name or an alias or the like.
However, you should be able to just write your own and particularly focus on the following methods:
void writeType(TypeInformation<?> type, DBObject dbObject) — you basically get the type and have complete control over where and how to put that into the DBObject.
<T> TypeInformation<? extends T> readType(DBObject source, TypeInformation<T> defaultType); — you get the type declared on the reading side (i.e. usually the most common type of the hierarchy) and based on that have to lookup the type from the given source document. I guess that's exactly the inverse of what's to be implemented in the other method.
On a final note, I would strongly recommend against using different type field names for different collections as on the reading side you might run into places where just Object is declared on the property and you basically don't get no clue where to even look for in the document.

Spring Data JPA repository methods don't recognize property names with underscores

I have underscores in the entity property names, and when Spring tries to create the JPA repository implementation, it results in an exception trying to resolve the name of the property.
Entity:
#Entity
public class Student {
#Id
private String s_id;
private String s_name;
...
}
Repository:
#Repository
#Transactional
public interface StudentRepository extends CrudRepository<Student, String> {
List<Student> findByS__name(String name);
}
Exception:
org.springframework.data.mapping.PropertyReferenceException:
No property s found for type Student
It is said here http://docs.spring.io/spring-data/jpa/docs/current/reference/html/
If your property names contain underscores (e.g. first_name) you can
escape the underscore in the method name with a second underscore. For
a first_name property the query method would have to be named
findByFirst__name(…).
I just did as document said, but I still got the exception.
I dont want write #Query by myself, and I need underscore in my property name, how to fix this problem?
I use Spring data jpa 1.8.0.RELEASE + hibernate 4.3.9.Final
Avoid using underscores in the entity property names if you have control over the property naming. This will resolve your repository woes, and will result in a cleaner code-base. Developers dealing with the code after you will thank you.
Note, it's not just my opinion: Spring specifically discourages using underscores.
As we treat underscore as a reserved character we strongly advise to
follow standard Java naming conventions (i.e. not using underscores in
property names but camel case instead).
this JIRA issue shows why the documentation was updated with this reccomendation, and the part describing the double underscore option were removed.
I suspect your root problem is that Spring/Hibernate is not mapping camel case property names to the snake case names you have for your columns in the database. What you really need is for your property name to be interpreted in the SQL that hiberate generates as S_NAME.
Is that why underscores in your property name are "required"? If so, there are a few solutions:
Option 1: #Column annotation
To get JPA/Hibernate to map to the correct column names you can tell it the names explicitly. Use the annotation #Column(name="...") to tell it what column names to use in SQL. Then the field names are not constrained by the column names.
#Entity
public class Student {
#Id
#Column(name="s_id")
private String sId;
#Column(name="s_name")
private String sName;
//...getters and setters...
}
Option 2: Improved Naming Strategy
Or if your application has a large number of entities, rather than adding #Column to every property, change the default naming strategy in your configuration file to the hibernate improved naming strategy.
<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
This naming strategy will convert camelCase to SNAKE_CASE. Then your class could look as simple as this:
#Entity
public class Student {
#Id
private String sId;
private String sName;
//...getters and setters...
}
Using either of those options, when it creates the SQL it will resolve the column names to:
S_ID
S_NAME
Note: If you are using, or can use Spring Boot, the auto-configuration default will use SpringNamingStrategy, which is a slightly modified version of the hibernate improved strategy. You won't have to do anything to get this improved naming strategy.
The finish line:
Using camel case in your property names you can write your repository method name using camel case, and you can stop trying to wrangle the double underscore:
#Repository
#Transactional
public interface StudentRepository extends CrudRepository<Student, String> {
List<Student> findBySName(String name);
}
Writing double underscore i.e. writing findByS__Name() for property name s_name just does not work. I have tried and tested it. Go by the above answer and change the name of existing instance variables in your entity class. Just dont change getters and setters as they might be used in the existing code.
If you cant change the entities which was my case then better use jqpl query or native sql query on top of repository method
#Query("select s from Student s where s.s_name=?")
List<Student> findBySName();

Why is my projection interface not picked up by Spring Data REST?

I am trying to use up projections with Spring Data REST (version 2.3.0.RELEASE). I read the reference documentation, and gathered that these are the parts I need:
A JPA Entity
#Entity
public class Project implements Serializable {
#Basic(optional = false)
#Column(name = "PROJECT_NAME")
private String projectName;
// ... lots and lots of other stuff
}
A repository that works with that entity
#Repository
public interface ProjectRepository extends JpaRepository<Project, Long> { }
And a projection to retrieve just the name for that entity
#Projection(name="names", types={Project.class})
public interface ProjectProjectionNamesOnly {
String getProjectName();
}
I would like to be able to optionally retrieve just a list of names of projects, and projections seemed perfectly suited to this. So with this setup, I hit my endpoint at http://localhost:9000/projects/1?projection=names. I get back ALL of the attributes and collections links, but I expected to get back just the name and self link.
I also viewed the sample project on projections, but the example is for excerpts, which seems different from projections as it is a different section of the reference. I tried it and it didn't work anyway though.
So the question is this: How do you use spring data rest projections to retrieve just a single attribute of an entity (and its self link)?
Looks like your projection definition is not even discovered and thus it doesn't get applied if you select it for the HTTP request.
For projection interfaces to be auto-discovered they need to be placed inside the very same or a sub-package of the package of the domain type they're bound to.
If you can't put the type into that location, you can manually register a projection definition on RepositoryRestConfiguration by calling ….projectionConfiguration().addProjection(…).
The reference documentation does not really mention this at the moment but there's already a ticket to get this fixed in future versions.

How to use QueryDslJpaRepository?

In my current project setup I'm defining repositories as:
public interface CustomerRepository extends JpaRepository<Customer, Long>, QueryDslPredicateExecutor<Customer> {
}
The QueryDslPredicateExecutor provides additional findAll methods which return e.g. an Iterable.
It e.g. does not contain a method to only specify an OrderSpecifier.
I just came across the QueryDslJpaRepository which contains more variants of these Predicate and OrderSpecifier aware methods, and also return Lists instead of Iterables.
I wonder why QueryDslPredicateExecutor is limited and if it is possible to use QueryDslJpaRepository methods?
I used a custom BaseRepository already so It was easy to make sure my repositories use the List variant (instead of Iterable) using:
#NoRepositoryBean
public interface BaseRepository<T, ID extends Serializable> extends JpaRepository<T, ID>, QueryDslPredicateExecutor<T> {
#Override
List<T> findAll(Predicate predicate);
#Override
List<T> findAll(Predicate predicate, Sort sort);
#Override
List<T> findAll(Predicate predicate, OrderSpecifier<?>... orders);
#Override
List<T> findAll(OrderSpecifier<?>... orders);
}
Note that my commnent reagarding missing methods in QueryDslPredicateExecutor was incorrect.
QueryDslJpaRepository extends SimpleJpaRepository
SimpleJpaRepository is used when you want to adding custom behavior to all repositories. It takes three steps to do so:
Step 1: Create an interface (eg CustomRepository) that extends JpaRepository, then add your own interface methods
Step 2: Create a class (eg CustomRepositoryImpl) that implements your CustomRepository, which naturally requires you to supply concrete method implementations to each and every method defined in not only CustomRepository but also JpaRepository as well as JpaRepository's ancestor interfaces. It'd be a tedious job, so Spring provide a SimpleJpaRepository concrete class to do that for you. So all you need to do is to make CustomRepositoryImpl to extend SimpleJpaRepository and then only write concrete method for the method in your own CustomRepository interface.
Step 3: make CustomRepositoryImpl the new base-class in the jpa configuration (either in xml or JavaConfig)
Similarly, QueryDslJpaRepository is the drop-in replacement for SimpleJpaRepository when your CustomRepository extends not only JpaRepository but also QueryDslPredicateExecutor interface, to add QueryDsl support to your repositories.
I wish Spring Data JPA document made it clear what to do if someone is using QueryDslPredicateExecutor but also wants to add his/her own customized methods. It took me some time to figure out what to do when the application throws errors like "No property findAll found for type xxx" or "No property exists found for type xxx".
Check your Predicate import in Service, for my case it was because auto import brings import java.util.function.Predicate; instead of import com.querydsl.core.types.Predicate;. This gives confusion like findall with predicate function gives error.

Reusable Querying in Entity Framework WITHOUT Repository. How?

Let me say, I have come to the conclusion (after a lot of trial) that Repository & Unit of Work when using Entity Framework is just wrong, wrong, wrong and this says why quite well.
But I really hate on those embedded queries. Question is, where can I put them instead if I'm so against a repository, etc? (clean answers only please, examples much appreciated).
I just nuked two projects containing my repositories, unit of work and interfaces with hundreds of files because the payback was nowhere to be seen. I think lots of people, myself included, just jumped on the Repository bandwagon because that's what everybody else was doing but in retrospect, I think it's really a ride to nowhere.
/sigh
Richard
Where do you expect to put them? You have only few choices:
Let them be where they are and use custom extension methods, query views, mapped database views or custom defining queries to define reusable parts
Expose every single query as method on some separate class. The method mustn't expose IQueryable and mustn't accept Expression as parameter = whole query logic must be wrapped in the method. But this will make your class covering related methods much like repository (the only one which can be mocked or faked). This implementation is close to implementation used with stored procedures.
You will do the same as in previous method but instead of placing queries in separate class you will put them as static methods to entity directly. This is much worse testable because static methods cannot be replaced by mocking (it requires more complex testing framework). This is part of active record pattern where each entity is responsible for its loading and saving to database.
Example of custom extension method:
public static IQueryable<TEntity> GetByName(this IQueryalbe<TEntity> query, string name)
where TEntity : IEntityWithName
{
return query.Where(e => e.Name == name);
}
Example of custom class exposing methods:
public class QueryProvider
{
public QueryProvider() {}
public IEnumerable<TEntity> GetByName(IYourContext context, string name)
where TEntity : IEntityWithName
{
return context.CreateObjectSet<TEntity>().Where(e => e.Name == name).ToList();
}
}
Build Reusable, Testable Queries Part 1
This is a blog post I wrote about building reusable queries. Using Extension Methods allows you to build composable queries.
using a pattern like the specification pattern can help you build queries that can be reused or saved (serialized). Further more if you have a double entry system you can execute the same query instance over two different databases.
the following example does not use EF but replace the IEnumerable by an EF context and you get what ou are looking for. parameters are passed in through the constructor.
public class PartialMatchQuery : IModelQuery<string, IEnumerable<string>>
{
private readonly string partial;
public PartialMatchQuery(string partialString)
{
partial = partialString;
}
public IEnumerable<string> Execute(IEnumerable<string> model)
{
return model.Where(s => s.ToLower().Contains(partial));
}
}