How to fix typing in hibernate "findBy" for enums? - spring-boot

I have an entity like so
public class SomeClass {
#NonNull
#NotNull
#Column(nullable = false)
#Convert(converter = SomeEnumConverter.class)
private SomeEnum fieldName;
}
I want to be able to find row in DB where the name of the enum matches to the string/enum which I pass. This table has only 2 columns. UUID which is PK and fieldName which is a varchar. I tried the following repository methods.
public interface SomeClassRepository extends AnotherRepository<SomeClass, UUID> {
Optional<SomeClass> findByFieldName(SomeEnum param);
}
public interface SomeClassRepository extends AnotherRepository<SomeClass, UUID> {
Optional<SomeClass> findByFieldName(String param);
}
My problem is if I try to find by passing a String (2nd case) it complains that my passed value is not the expected type(SomeEnum).
On the other hand if I pass the enum directly, It tries to look for rows with PK(UUID) = enum which I passed instead of searching on the other column(fieldName varchar). How do I get past this ?

public class SomeClass {
#Column(nullable = false)
#Enumerated(EnumType.STRING)
private SomeEnum fieldName;
}
public interface SomeClassRepository extends AnotherRepository<SomeClass, UUID> {
Optional<SomeClass> findByFieldName(SomeEnum param);
}
Please try this. This should work

Related

Invalid parameter index! You seem to have declared too little query method parameters

I have an entity:
#Document(collection = "userData")
#Data
public class UserData {
#Id
#Field("_id")
private String id;
#Field("orderColumns")
private List<String> orderColumns;
#Field("ownerId")
private String ownerId;
}
And I have a repository:
#Repository
interface UserDataRepository extends MongoRepository<UserData, String> {
Optional<UserData> findByOwnerIdAndOrderColumnsExists(String ownerId);
}
When I execute this method, I get an exception:
Invalid parameter index! You seem to have declared too little query method parameters!

How to solve Spring Boot findBy method with underscore variable

When I run the below project, I receive the following error. How can I fix it?
Caused by: java.lang.IllegalArgumentException: Failed to create query for method public abstract com.example.pharmanic.model.Rdhs_Hospital_Current_Stock com.example.pharmanic.repositories.Rdhs_Hospital_Current_StockRepository.findBysr_no(java.lang.String)! No property sr found for type Rdhs_Hospital_Current_Stock!
This is my Rdhs_Hospital_Current_Stock model class.
#Entity
#Data
#Table(name = "Rdhs_Hospital_Current_Stock")
public class Rdhs_Hospital_Current_Stock {
#Id
private Long batchId;
private int quantity;
private String expiredate;
#ManyToOne
private Hospital_By_Rdhs hospital_by_rdhs;
#ManyToOne
#JoinColumn(name = "sr_no", nullable = false, referencedColumnName = "sr_no")
private Medicine medicine;
}
sr_no is the foreign key of the Medicine table.
This is my Medicine entity:
#Data
#Entity
public class Medicine {
private #Id String sr_no;
private String name;
private String side_effect;
private String description;
public Medicine() {
}
public Medicine(String sr_no, String name, String side_effect, String description) {
this.sr_no = sr_no;
this.name = name;
this.side_effect = side_effect;
this.description = description;
}
}
When I use sr_no with my findBy() function:
#GetMapping("/rhstock/{id}")
ResponseEntity<?> getMedicine(#PathVariable String id){
Optional<Rdhs_Hospital_Current_Stock> rdhs_hospital_current_stock = Optional.ofNullable(rdhs_hospital_current_stockRepository.findBysr_no(id));
return rdhs_hospital_current_stock.map(response->ResponseEntity.ok().body(response)).orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
This is my repository:
public interface Rdhs_Hospital_Current_StockRepository extends JpaRepository<Rdhs_Hospital_Current_Stock,Long> {
Rdhs_Hospital_Current_Stock findBysr_no(String id);
}
Inspired from: Spring-Data-Jpa Repository - Underscore on Entity Column Name
The underscore _ is a reserved character in Spring Data query derivation (see the reference docs for details) to potentially allow manual property path description.
Stick to the Java naming conventions of using camel-case for member variable names and everything will work as expected.
Change sr_no to srNo.
Update repository function
Rdhs_Hospital_Current_Stock findBymedicine_srNo(String id);
I solve this error. I change in Reposity Interface & Controller class like as below
Repository Interface -:
#Query(value="select * from Rdhs_Hospital_Current_Stock h where h.sr_no = :sr_no",nativeQuery=true)
List<Rdhs_Hospital_Current_Stock> findBySr_no(#Param("sr_no")String sr_no);
Controller class -:
#RequestMapping(value = "/rhstocksr/{sr_no}", method = RequestMethod.GET)
List<Rdhs_Hospital_Current_Stock> getBatchByMedicine(#PathVariable("sr_no") String sr_no) {
return rdhs_hospital_current_stockRepository.findBySr_no(sr_no);
}
To keep using a derived query you may change sr_no to srNo.
You could solve this by starting using a native query, but my advice is to use derived querys everytime as it is possible to use it.

Spring Data REST and custom entity lookup (Provided id of the wrong type)

I have a model that looks something like this:
#Entity
public class MyModel {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(unique = true, nullable = false)
#RestResource(exported = false)
private int pk;
#Column(unique = true, nullable = false)
private String uuid = UUID.randomUUID().toString();
#Column(nullable = false)
private String title;
public int getPk() {
return pk;
}
public void setPk(int pk) {
this.pk = pk;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
As you can see I have an auto-incrementing PK as my ID for the model, but also a random UUID. I want to use the PK in the database as the primary key, but want to use the UUID as a public facing ID. (To be used in URLs etc.)
My repository looks like this:
#RepositoryRestResource(collectionResourceRel = "my-model", path = "my-model")
public interface MyModelRepository extends CrudRepository<MyModel, String> {
#RestResource(exported = false)
MyModel findByUuid(#Param("uuid") String id);
}
As you can see I've set the repository to use a String as the ID.
Finally I set the entity lookup in a config file like this:
#Component
public class RepositoryEntityLookupConfig extends RepositoryRestConfigurerAdapter {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.withEntityLookup().forRepository(MyModelRepository.class, MyModel::getUuid, MyModelRepository::findByUuid);
}
}
This works perfectly well for GET and POST requests, but for some reason I get an error returned on PUT and DELETE methods.
o.s.d.r.w.RepositoryRestExceptionHandler : Provided id of the wrong type for class MyModel. Expected: class java.lang.Integer, got class java.lang.String
Anyone know what might be causing this? I don't understand why it's expecting an Integer.
I may be doing something stupid as I'm quite new to the framework.
Thanks for any help.
The identifier of your domain object is obviously of type int. That means, your repository needs to be declared as extends CrudRepository<MyModel, Integer>.

How to fetch only selected attributes of an entity using Spring JPA?

I'm using Spring Boot (1.3.3.RELEASE) and Hibernate JPA in my project. My entity looks like this:
#Data
#NoArgsConstructor
#Entity
#Table(name = "rule")
public class RuleVO {
#Id
#GeneratedValue
private Long id;
#Column(name = "name", length = 128, nullable = false, unique = true)
private String name;
#Column(name = "tag", length = 256)
private String tag;
#OneToMany(mappedBy = "rule", cascade = CascadeType.ALL, orphanRemoval = true)
private List<RuleOutputArticleVO> outputArticles;
#OneToMany(mappedBy = "rule", cascade = CascadeType.ALL, orphanRemoval = true)
private List<RuleInputArticleVO> inputArticles;
}
My repository looks like this:
#Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
}
In some cases I need to fetch only id and name attributes of entity RuleVO. How can I achieve this? I found a notice it should be doable using Criteria API and Projections but how? Many thanks in advance. Vojtech
UPDATE:
As has been pointed out to me, I'm lazy and this can very well be done hence I'm updating my answer after having looked around the web for a proper one.
Here's an example of how to get only the id's and only the names:
#Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
#Query("SELECT r.id FROM RuleVo r where r.name = :name")
List<Long> findIdByName(#Param("name") String name);
#Query("SELECT r.name FROM RuleVo r where r.id = :id")
String findNameById(#Param("id") Long id);
}
Hopefully this update proves helpful
Old Answer:
Only retrieving the specific attributes name/id is not possible as this is not how spring was designed or any SQL database for that matter as you always select a row which is an entity.
What you CAN do is query over the variables in the entity, for instance:
#Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
public RuleVo findOneByName(String name);
public RuleVo findOneByNameOrId(String name, Long id);
public List<RuleVo> findAllByName(String name);
// etc, depending on what you want
}
You can modify these however you want w.r.t. your needs. You can call these methods directly via the autowired repository
See http://docs.spring.io/spring-data/jpa/docs/current/reference/html/ Section 5.3 for more options and examples
interface IdOnly{
String getId();
}
#Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
public List<IdOnly> findAllByName(String name);
}
I notice that this is a very old post, but if someone is still looking for an answer, try this. It worked for me.
You can also define custom constructor to fetch specific columns using JPQL.
Example:
Replace {javaPackagePath} with complete java package path of the class
use as a constructor in JPQL.
public class RuleVO {
public RuleVO(Long id, String name) {
this.id = id;
this.name = name;
}
}
#Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
#Query("SELECT new {javaPackagePath}.RuleVO(r.id, r.name) FROM RuleVo r where r.name = :name")
List<RuleVO> findIdByName(#Param("name") String name);
}
Yes, you can achieve it with projections. You have many ways to apply them:
If you could upgrade to Spring Data Hopper, it provides an easy to use support for projections. See how to use them in the reference documentation.
Otherwise, first of all create a DTO with the attributes you want to load, something like:
package org.example;
public class RuleProjection {
private final Long id;
private final String name;
public RuleProjection(Long id, String name) {
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
}
Of course, you could use Lombok annotations also.
Then, you can use in the JPQL queries like this:
select new org.example.RuleProjection(rule.id, rule.name) from RuleVO rule order by rule.name
Another option, if you want to avoid using DTO class names in your queries, is to implement your own query method using QueryDSL. With Spring Data JPA, you have to:
Create a new interface with the new method. Ex:
public interface RuleRepositoryCustom {
public List<RuleProjection> findAllWithProjection();
}
Change your repository to extend the new interface. Ex:
public interface RuleRepository extends JpaRepository<RuleVO, Long>, RuleRepositoryCustom {
...
Create an implementation of the Custom repository using the Spring Data JPA QueryDSL support. You have to previously generate the Q clases of QueryDSL, using its Maven plugin. Ex:
public class RuleRepositoryImpl {
public List<RuleProjection> findAllWithProjection() {
QRuleVO rule = QRuleVO.ruleVO;
JPQLQuery query = getQueryFrom(rule);
query.orderBy(rule.name.asc());
return query.list(ConstructorExpression.create(RuleProjection.class, rule.id, rule.name));
}
}
You can do it by using #Query annotation(HQL).
Please refer to the Spring docs below:
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.at-query
(search for #Query in spring document)

Spring Data Neo4J #Indexed(unique = true) not working

I'm new to Neo4J and I have, probably an easy question.
There're NodeEntitys in my application, a property (name) is annotated with #Indexed(unique = true) to achieve the uniqueness like I do in JPA with #Column(unique = true).
My problem is, that when I persist an entity with a name that already exists in my graph, it works fine anyway.
But I expected some kind of exception here...?!
Here' s an overview over basic my code:
#NodeEntity
public abstract class BaseEntity implements Identifiable
{
#GraphId
private Long entityId;
...
}
public class Role extends BaseEntity
{
#Indexed(unique = true)
private String name;
...
}
public interface RoleRepository extends GraphRepository<Role>
{
Role findByName(String name);
}
#Service
public class RoleServiceImpl extends BaseEntityServiceImpl<Role> implements
{
private RoleRepository repository;
#Override
#Transactional
public T save(final T entity) {
return getRepository().save(entity);
}
}
And this is my test:
#Test
public void testNameUniqueIndex() {
final List<Role> roles = Lists.newLinkedList(service.findAll());
final String existingName = roles.get(0).getName();
Role newRole = new Role.Builder(existingName).build();
newRole = service.save(newRole);
}
That's the point where I expect something to go wrong!
How can I ensure the uniqueness of a property, without checking it for myself??
P.S.: I'm using neo4j 1.8.M07, spring-data-neo4j 2.1.0.BUILD-SNAPSHOT and Spring 3.1.2.RELEASE.
I walked into the same trap... as long as you create new entities, you will not see the exception - the last save()-action wins the battle.
Unfortunately, the DataIntegrityViolationException will be raised only in case of update an existing entity!
A detailed description of that behaviour can be found here:
http://static.springsource.org/spring-data/data-graph/snapshot-site/reference/html/#d5e1035
If you are using SDN 3.2.0+ use the failOnDuplicate attribute:
public class Role extends BaseEntity
{
#Indexed(unique = true, failOnDuplicate = true)
private String name;
...
}

Resources