Spring Data JPA repository methods don't recognize property names with underscores - spring-data-jpa

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();

Related

creating Spring JPARepository method for entity variable with name "cId"

I have entity class as
class Policy
{
private String customerId;
private String sicNumber;
private String cId;
/*
more variables can be ignored
*/
}
now inside JPA Repository interface, I want to create method that finds based on sicNumber And cId
following is the attempt i tried
Policy findBySicNumberAndCId(String policyNumber, String cId) ;
above method asks spring to search for "CId" property in entity class
likewise i tried almost all combinations
Policy findBySicNumberAndcId(String policyNumber, String cId) ;
above method caused spring to look for "AndcId".
now the question is what should be the method name that will target right property in entity class.
thanks in advance
The issue is with the way Spring Data JPA forms the query out of the method name. It follows the regular camel casing approach where the field names are inferred. As per the convention followed, if a method name has uppercase letters consecutively, the field name is inferred in uppercase.
So in your case, findBySicNumberAndCId - the field inferred is CId & is failing to find it.
One way to overcome the problem is having the method name with CId in the beginning with 'c' being in lowercase i.e., findBycIdAndSicNumber .
Alternatively, you can consider renaming the variable to avoid these issues or make use of #Query & define your queries if renaming the variables is not possible.
More info about the issue :
https://github.com/spring-projects/spring-data-commons/issues/1996

Disable query creation from method name - use of projections

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.

Using #Id on methods

I would like to annotate a method with Spring Data #Id but it only works with fields, despite the fact that the annotation can be used on methods.
Is there a way to use #Id on methods too?
I'm using Spring Boot 1.3.0.RELEASE
EDIT
Actually I have this interface that will have an instance being created at runtime.
import org.springframework.data.annotation.Id;
#Document(indexName = "index", type = "document")
public interface Document {
#Id
Integer getId();
}
And this repository.
public interface DocumentRepository extends ElasticsearchCrudRepository<Document, Integer> {
}
Problem is that SimpleElasticsearchPersistentProperty from spring-data-elasticsearch 1.3.0.RELEASE always look for fields:
https://github.com/spring-projects/spring-data-elasticsearch/blob/1.3.0.RELEASE/src/main/java/org/springframework/data/elasticsearch/core/mapping/SimpleElasticsearchPersistentProperty.java
That way if I create an asbtract class instead and put #Id on a field, everything works fine.
The #Id annotation does work on properties, i.e. you can put it on getters, setters or fields. If this does not work something is wrong. Possible reasons are:
the names don't fit the property conventions
you are using the wrong #Id annotation
It does not work on arbitrary methods because Spring Data wouldn't be able to determine a name for that non-property, which in turn is required for many features.

Is it possible to specify a property naming strategy with an annotation?

I have a class defined as:
class Person {
public int age;
public String firstName;
}
Note that I use camel case for the field names. Also, I know that I could have generated getters and setters but I tend to not do that for simple domain objects.
When I deserialize a JSON or XML response in my REST API, it should spit out:
<Person><Age>11</Age><FirstName>Johnson</FirstName></Person>
You will notice that the first letter is upper-cased.
I could use, for example, #JsonPoperty("FirstName") on my POJO to get the output the way I need it, but this doesn't scale when there are too many fields. I'd like to use a custom property naming strategy (as described in How To Use Property Naming Strategy In Jackson). But instead of configuring an ObjectMapper, I was wondering if its possible to specify a naming strategy using annotations?
Thanks

JPA validation problem: Entity 'X' has no Id or EmbeddedId

Which, I couldn't quite figure out why Eclipse Galileo's JPA validator would say so, as my class 'X' clearly had an #Id annotation on the primary key "uniqueId". The error would disappear if I commented out the #Basic annotation line, but that didn't quite make sense. Anyway, attaching snippet below from memory:
#Entity
class X {
...
#Id
#Basic(optional=false)
#Column(name="someUniqueId", unique=false)
String uniqueId;
...
}
Is this a bug, or is there a JPA standard which I'm not aware of on the use of annotations?
You have here a similar case which may suggest a case or type issue.
But as mentioned here, I would rather bet on bug 217192 (which can be comared with the "non-bug" 208249)
You can also specify which version of eclipse and Dali JPA Tool you are using.
#EmbeddedId
String uniqueId;
You annotated the "uniqueId" with "unique=false".
This could be the root because IDs have to be unique.
You do not need a #Basic annotation as #Id already restricts the attribute's type (as defined by the spec) and the only information #Basic can add are settings for fetch and optional both of which make no sense for a primary key (i.e. a primary key must have fetch=EAGER and optional=false).