If I don't write a custom query the #Autowired FirstnameRepository object is null, therefore I tried to write a custom query, but this produces the following error.
java.lang.IllegalArgumentException: Validation failed for query for method public abstract java.util.List FirstnameRepository.findByNation(Nation)!
Since the query looks correct to me, I think it has something to do with my classstructure or incorrect annotations.
FirstnameRepo
public interface FirstnameRepository extends JpaRepository<Firstname, String>{
#Query("SELECT fn FROM Firstname fn WHERE fn.nation = :nation")
List<Firstname> findByNation(#Param("nation")Nation nation);
}
Firstname Model
#Entity
#Table(name = "Firstnames")
public class Firstname extends Name implements Serializable {
private static final long serialVersionUID = 1L;
#Basic(optional = false)
#Column(name = "gender")
private short gender;
#JoinColumn(name = "nation", referencedColumnName = "id")
#ManyToOne(optional = false)
private Nation nation;
public Firstname() {}
}
Since there is also a Lastname model class I extend a class named Name to save Firstname and Lastname in the same Map. Name has no table in the database and both classes only inherit the ID property.
Name class
#MappedSuperclass
public abstract class Name {
#Id
#Basic(optional = false)
#Column(name = "name")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
If I comment the findByNation() method out, the server starts without a problem. I hope this is all you need, the server configuration is more or less copied from a working project, but if I should include something let me know. thanks
EDIT
The Problem was a incorrect configuration. I changed alot of stuff before testing it again, but seems like the main issue was a wrong version of in the web.xml.
Even though this problem has most likely something to do with the Query itself, in my case was a configuration issue. Here are some things I checked
Version of in web.xml which specifies the Version of the used Servlet specification - SO Question
Make sure your database context (e.g. db-context.xml) gets loaded
Your query is malformed... you can't put this "fn.nation = :nation" where Nation is an Entity, you should use a join between tables to work fine.
I suggest you this link.
Related
I found similar questions, but they did not answer my question.
I have two entities with a many-to-one relationship - unidirectional.
But most importantly, the relationship is lazy. Because it is correct to use a lazy connection, everyone knows it.
Code:
#Entity
public class User implements BaseEntity {
#Id
#Column
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String name;
#ManyToOne(fetch = FetchType.LAZY)
private City city;
}
#Entity
public class City implements BaseEntity {
#Id
#Column
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String name;
}
interface BaseEntity {
void setId(Long id);
Long getId();
}
I wrote a method that allows you to search by the transferred fields of the entity.
An example of how this works:
public class Search<T extends BaseEntity> {
public List<T> getByFields(T entity, List<FieldHolder> data) {
// create criteria with passed field name and value by reflection
}
}
class FieldHolder {
private String fieldName;
private Object value;
/**
* "true" - means that the field "value" contains id related object
* "false" - elementary type like: String, Wrapper, Primitive
*/
private boolean isRelationId;
}
The problem is that problems start when you need to search and related objects - by creating related queries.
The following entry is used to send the associated field: "city.id" and the problem is that when I transfer the essence of the related object (City) it is in a proxy and I cannot get id by reflection from City.
My function works perfectly if you specify:
#ManyToOne(fetch = FetchType.EAGER)
private City city;
But it will greatly affect performance, since I have a lot of related objects. Therefore, I want to solve this problem for a lazy load.
I know that this is not an easy task. But perhaps there is some opportunity to somehow get around this problem.
I am unable to to get a list of nested objects using a JpaRepository. I'll try to explain what I want using the following code:
AutoService entity:
#Entity
public class AutoService {
#Id
private long id;
#Column(name = "serviceName", nullable = false)
private String serviceName;
}
Service entity:
#Entity
public class Service {
#Id
private long serviceId;
#Column(name = "serviceName", nullable = false)
private String serviceName;
#Column(name = "category", nullable = false)
private String category;
#ManyToOne
#JoinColumn(name = "autoServiceId", nullable = false)
private AutoService autoService;
}
ServiceRepository interface:
public interface ServiceRepository extends JpaRepository<Service, Long> {
List<Service> findByServiceNameAndCategory(String autoServiceName, String categoryName);
}
Business logic:
#org.springframework.stereotype.Service
public class ServiceServiceImpl implements ServiceService {
#Autowired
private ServiceRepository serviceRepository;
#Override
public List<Service> findByAutoServiceAndCategory(String autoServiceName, String serviceCategory) {
return serviceRepository.findByServiceNameAndCategory(autoServiceName, serviceCategory);
}
}
As I am expecting, the code above is unable to provide the desired list of Services matching the provided category and AutoService names.
Can someone provide advice on how should I use my repository to get list of nester services by: autoServiceName and serviceCategory please?
EDIT:
Right now I am using the custom query.
I am using autoServiceId instead of service name right now.
But for some reason I am getting empty list of objects.
Here is my JPA Repo.
public interface ServiceRepository extends JpaRepository<Service, Long> {
#Query("SELECT s from Service s where s.autoService.id = :autoServiceId and s.category = :categoryName")
List<Service> findByServiceNameAndCategory(#Param("autoServiceId") Long autoServiceId, #Param("categoryName") String categoryName);
}
Any suggestions please ?
I think i know the answer. Problem in my category, sended to the server. I wrote it on Russian language. And encoding broken value of category on server side.
1- Use #Embedded and #Embeddable annotation accordingly on your entity object then your method will fetch nested object.
OR
2- #Query annotation is used for writing custom query please refer this link custom query reference
You may have to write a query like this in your ServiceRepository.
public interface ServiceRepository extends JpaRepository<Service, Long> {
#Query("SELECT s from Service s where s.autoService.serviceName = :autoServiceName and s.category = :categoryName")
Set<Round> getRoundsBySessionQuestionId(#Param("autoServiceName") String autoServiceName, #Param("categoryName") String categoryName);
}
Hope this helps. Happy coding !
Since you have a serviceName attribute in both AutoService and Service entities, ServiceRepository.findByServiceNameAndCategory is equivalent to the following SQL query:
SELECT
*
FROM
Service
WHERE
serviceName = ?
AND category = ?
As seen, this query does not hit the AutoService entity at all, which is why the results are not as expected.
The correct repository method is:
public interface ServiceRepository extends JpaRepository<Service, Long> {
List<Service> findByCategoryAndAutoServiceServiceName(String category, String autoServiceName);
}
This method will search the nested AutoService object by its serviceName, as expected.
A sample project is available on Github to show this in action.
I am using spring-data-jpa on a spring webmvc project. I am facing an issue using query creation on a Repository of one of my Entities. Below you can see my Entity, my Repository and the Exception.
My Entity:
#Entity
#Table(schema = "mainschema")
#XmlRootElement
public class Municipalperson implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(nullable = false)
private Integer id;
#Basic(optional = false)
#Column(name = "municipal_id", nullable = false)
private Integer municipal_id;
#Basic(optional = false)
#Column(nullable = false, length = 60)
private String firstname;
public Municipalperson(Integer id, Integer municipal_id, String firstname) {
this.id = id;
this.municipal_id = municipal_id;
this.firstname = firstname;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getMunicipal_id() {
return municipal_id;
}
public void setMunicipal_id(Integer municipal_id) {
this.municipal_id = municipal_id;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
}
my Repository:
#Repository
public interface MunicipalpersonRepository extends JpaRepository<Municipalperson, Integer> {
List<Municipalperson> findByMunicipal_idOrderByLastnameDesc(int municipal_id);
}
and the exception,
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'municipalpersonRepository': Invocation of init method failed; nested exception is org.springframework.data.mapping.PropertyReferenceException: No property municipal found for type Municipalperson!
I tried to set municipal_id as int, then as Integer and the same for the parameter municipal_id on my Repository, but none worked. Also, I renamed the Repository to findByMunicipalidOrderByLastnameDesc and findByMunicipalIdOrderByLastnameDesc but it didn't work either.
Finally I renamed the municipal_id to municipalId (underscore removed) and also renamed getters/setters and the Repository (findByMunicipalIdOrderByLastnameDesc) and the issue was resolved.
My question is why this is happening?
I solved this error by renaming field to the name without underscore.
#Column(name = "municipal_id", nullable = false)
private Integer municipalId; // <-- field was renamed
The underscore _ is a reserved character in Spring Data query derivation (see the reference docs for details) to potentially allow manual property path description. So there are two options you have:
Stick to the Java naming conventions of using camel-case for member variable names and everything will work as expected.
Escape the _ by using an additional underscore, i.e. rename your query method to findByMunicipal__idOrderByLastnameDesc(…).
I'd recommend the former as you're not going to alienate fellow Java developers :).
Please add the following properties to application.properties file:
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.ImprovedNamingStrategy
I know the question was answered a long time ago, but it can help others in the future.
According to the Docs, underscore is a special character used by spring to separate properties names. If you really want to stick with snake case notation, you can set nativeQuery to true and solve this problem:
#Query(value = "SELECT * FROM municipalperson WHERE municipal_id=?1 ORDER BY last_name DESC", nativeQuery = true)
List<Municipalperson> findByMunicipal_idOrderByLastnameDesc(int municipal_id);
One other approach that worked for me is to use #JsonProperty to differentiate between field name used in REST request/response and that used for database.
For example:
#JsonProperty("municipalId")
private Integer municipal_id;
I have some trouble with Hibernate 4 and inheritance:
I use a ChildData class which inherit from BaseData by a JOIN inheritance strategy. My mapping is done by annotation in classes.
Everything is working fine except that when I delete a ChildData instance (with session.delete() or with a Hql query) the BaseData entry is also deleted.
I understand that in most case this is the awaited behavior, but for my particular case, I would like to preserve the BaseData entry no matter what for history purpose.
In other words I want all actions on the child class to be cascaded to base class except deletion.
I have already tried #OnCascade on the child class, with no success.
Is it a way to achieve this by code or do I have to use a SQL Trigger ON DELETE ?
EDIT :
Base Class
#Entity
#Table(name = "dbBenchHistory", uniqueConstraints = #UniqueConstraint(columnNames = "Name"))
#Inheritance(strategy = InheritanceType.JOINED )
public class DbBenchHistory implements java.io.Serializable {
private int id;
private String name;
private String computer;
private String eap;
private Date lastConnexion;
private Set<DbPlugin> dbPlugins = new HashSet<DbPlugin>(0);
private Set<DbSequenceResult> dbSequenceResults = new HashSet<DbSequenceResult>(
0);
public DbBenchHistory() {
}
public DbBenchHistory(int id, String name) {
this.id = id;
this.name = name;
}
public DbBenchHistory(int id, String name, String computer, String eap,
Date lastConnexion, Set<DbPlugin> dbPlugins,
Set<DbSequenceResult> dbSequenceResults) {
this.id = id;
this.name = name;
this.computer = computer;
this.eap = eap;
this.lastConnexion = lastConnexion;
this.dbPlugins = dbPlugins;
this.dbSequenceResults = dbSequenceResults;
}
#Id
#Column(name = "Id", unique = true, nullable = false)
#GeneratedValue(strategy=GenerationType.IDENTITY)
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
//Getters/Setters
Child Class :
#Entity
#Table(name = "dbBench")
#OnDelete(action=OnDeleteAction.NO_ACTION)
public class DbBench extends DbBenchHistory {
private Set<DbProgram> dbPrograms = new HashSet<DbProgram>(0);
private Set<DbUser> dbUsers = new HashSet<DbUser>(0);
public DbBench() {
}
public DbBench(Set<DbProgram> dbPrograms,
Set<DbUser> dbUsers) {
this.dbPrograms = dbPrograms;
this.dbUsers = dbUsers;
}
//Getters/Setters
But I'm starting to think that I was wrong from the beginning and that inheritance was not the good way to handle this. If nothing shows up I will just go for BenchHistory - Bench being a simple one-to-one relationship
EDIT2 :
I edit while I can't answer my own question for insuficient reputation
I feel completly stupid now that I found the solution, that was so simple :
As I said, I was using hibernate managed methods : session.delete() or hql query. Hibernate was doing what he was supposed to do by deletintg the parent class, like it would have been in object inheritance.
So I just bypass hibernate by doing the deletion of the child class with one of the simplest SqlQuery on earth. And the base class entry remain untouched.
I understand that I somehow violate the object inheritance laws, but in my case it is really handy.
Thanks to everyone for your time, and believ me when I say I'm sorry.
I don't think Hibernate/JPA supports this. What you basically want is conversion from a subclass to a superclass, and not a cascading delete. When you have an object of the subclass, the members from the superclass are treated no different than the members of the subclass.
This can be solved through writing some logic for it though:
public void deleteKeepSuperclassObject(final ChildData childData) {
final BaseData baseDataToKeep = new BaseData();
//populate baseDataToKeep with data from the childData to remove
em.persist(baseDataToKeep);
em.remove(childData);
}
Lets assume we have the following situation:
We want to inherit all the values of the class Articles except one it's name for instance. How can we achieve it? I know that if we want to inherit everything from the Articles just write
public class Fruits extends Articles{ ... }
but how can we manage to inherit only specific attributes of the class Articles, ie. every attribute except one and one attribute leave intact?
EDIT:
#Entity
#Table(name = "Article")
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Article {
#Id #GeneratedValue(strategy=GenerationType.AUTO)
#Basic(optional = false)
#Column(name = "ART_ID")
private Long id;
#Basic(optional = false)
#Column(name = "ART_NAME")
private String name;
#Basic(optional = true)
#Column(name = "ART_COST")
private String cost;
// ...
}
#Entity
#Table(name="Fruits")
#AttributeOverrides({
#AttributeOverride(name="name", column=#Column(name="ART_NAME")),
#AttributeOverride(name="cost", column=#Column(name="ART_COST")),
})
// This is what is causing the issue. Fruits inherits something which is already defined in it's scope, and as the result can't declare exactly how to process it.
public class Fruits extends Article {
#Id #GeneratedValue(strategy=GenerationType.AUTO)
#Basic(optional = false)
#Column(name = "FRU_ID")
private Long fruitID;
#Column(name="FRU_FROZEN")
private String fruFrozen;
//...
}
So, I think code won't work, because this will result in multiple IDs in the entity hierarchy, so is there any other way I can solve this?
You can't remove a member from Articles
When name is a member of Articles and Fruits IS A Articles,
there could not be a way to remove name
You may hide some members from Articles using scope private
An other approach is to create a class "BaseArticles" without the member name.
Then derive both Articles AND Fruits from BaseArticles
public BaseArticles {
// HAS NO private String name;
...
}
public Article extends BaseArticles {
private String name;
...
}
public Fruits extends BaseArticles {
// WITHOUT private String name;
...
}
However, it is not simple but possible to deal with OO-inheritance using hibernate.
There is an annotation but I do not know off hands
You can put base class not required attribute as a private.