Find All based on the flag value in Spring Data JPA - spring-boot

I want to fetch all the records based on the flag value. Flag value can be either 'A' for Active or 'I' for Inactive.
I want to fetch both Active and Inactive records and put them in some kind of List or Map.
These are my repository and entity class:
#Repository
public interface StatusRepository extends JpaRepository<StatusEntity,Long>{
}
public class StatusEntity{
#Id
#Column(name="id_status")
private Long idStatus;
#Column(name="name")
private String name;
#Column(name="status_flg")
private String statusFlg;
}
I am able to fetch all the records using statusRepository.findAll(). I was wondering if there is something like statusRepository.findAllByStatusFlg(String flag) or statusRepository.findByStatusFlg(String flag)?
Is there any way to fetch all the records by specifying where condition on certain columns?
P.S. I am using spring-boot-starter-data-jpa 2.3.2.RELEASE

You should make yourself familiar with the basics of spring data. As you thought, you can define a query method in your StatusRepository that fetches your StatusEntitys according to your needs.
#Repository
public interface StatusRepository extends JpaRepository<StatusEntity,Long>{
List<StatusEntity> findByStatusFlg(String flag);
}

Related

How to make #Indexed as unique property for Redis model using Spring JPA Repository?

I have a model class that I store in Redis and I use Jpa Repository with Spring java. Normally(not with redis) jpa repository is saving the new data or updates(conditionally) if the given model is already exist in Db. Here, I want to add new item to redis but if it is not already exists on db otherwise update it just like usual Jpa implementation.
Here is my model:
#Getter
#Setter
#RedisHash("MyRecord")
public class MyRecordRedisModel {
private String id;
#Id
#Indexed
private String recordName;
private Date startDate;
private Date endDate;
}
And my repository class is just a normal spring jpa repo as follows:
#Repository
public interface IFRecordRedisRepository extends JpaRepository<IFRecordRedisModel, String> {
Page<IFRecordRedisModel> findAll(Pageable pageable);
}
Unique key must be the name (I totally do not care about uniquiness of the id). Thus, if the name is already exist in Db than do not add it again. I marked it as Indexed but still it is adding same data (with same recordName).
How can I make it unique?
This would require an additional query, but I think this solution would work for you. You can use query by Example to check if there exists a record with that name, and save conditionally, or do something else if it already exists.
IFRecordRedisModel exampleRecord = new IFRecordRedisModel();
exampleRecord.setRecordName(inputRecord.getRecordName());
if (!repository.exists(Example.of(exampleModel)))
repository.save(inputRecord);
else ..... // do something else

Why does graphql java query all fields in entity when I only ask for a few?

I created a basic graphql-java app with the spring boot starter and using the graphql spqr library against an MSSQL database utilizing Hibernate and Jpa.
I have an entity called "Task" with 5 fields. I have a simple Jpa repository and a simple Jpa service that calls a "findAllTasks" method. It works great, but if I specify, for example, only one field to query with graphiql, I can see through my SQL log that the select command executed is querying for ALL fields in my Task entity/table, rather than the one I want. Is this expected? I thought graphql only selects the fields you specify in the query command?
Here is my code:
Entity
#Entity
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class Task {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
#Column
public String desc;
#Column
public LocalDateTime createdOn;
#Column
public LocalDateTime modifiedOn;
#Column
public String owner;
}
Repository
#Repository
public interface TaskRepository extends JpaRepository<Task, Long> {}
Service
#GraphQLApi
#Service
public class TaskService {
private TaskRepository taskRepo;
#Autowired
public TaskService(TaskRepository taskRepo) {
this.taskRepo = taskRepo;
}
#GraphQLQuery
public List<Task> findAllTasks() {
return taskRepo.findAll();
}
}
When I run the following in graphiql:
query {
findAllTasks {
id
}
}
I get the following SQL statement that was generated from my log:
select task0_.id as Task1_1_0_. task0_.desc as Task1_2_0, task0_.createdOn as Task1_3_0, task0_.modifiedOn as Task1_4_0, task0_.owner as Task1_4_0 from Task as task0_
You have to make a distinction between your GraphQL API and your database. You defined a query method GraphQL that is called findAllTasks. In consequence, when you call this GraphQL query with any number of fields, it will call the Java method implementation findAllTasks.
You can see that the implementation of this Java method calls taskRepo.findAll(). Therefore, you will fetch all data from your tasks in database.
GraphQL will then filter the data from the tasks fetched by your Java method to match what is asked in the GraphQL query.
In a nutshell, GraphQL is in charge in returning just the fields that you requested, but your implementation is in charge of getting the data from the database.
Disclaimer: I'm not an expert of graphql-spqr, so the upcoming information might not work in your case (as it applied to graphql-java).
If you feel that your implementation is however not efficient enough (here we are really talking about efficiency), you could look into dataloaders.

Spring Data JPA - findBy mapped object

In my legacy application, I have a country table, state table and a mapping table for country and state with few additional columns.
I have created an entity class like this.
class CountryStateMapping {
#Id
private long id;
private Long countryId;
#OneToOne
#JoinColumn(name="state_id")
private State state;
//getters seters
}
My repository.
public interface CountryStateMapping extends JpaRepository<CountryStateMapping, Long>{
Optional<CountryStateMapping> findByStateId(long stateId);
Optional<CountryStateMapping> findByState(State state);
}
I would like to check if the state exists in the mapping table. Both of the below approaches do not work.
countryStateMapping.findByStateId(long stateId)
countryStateMapping.findByState(State state)
What is the right way?
Its not the correct way i feel.The correct way for doing this will be
public interface CountryStateMappingRepository extends JpaRepository<CountryStateMapping, Long> {
Optional<CountryStateMapping> findByStateId(long stateId);
#Query("select s.something from State s" )
Optional<CountryStateMapping> findByState(State state);
}
This implies two things
By extending JpaRepository we get a bunch of generic CRUD methods to create, update, delete, and find
2.It allows Spring to scan the classpath for this interface and create a Spring bean for it.
Also you need some configuration.For that you need to create a configuration class to be used with your data source.You can find many examples to do the same and one such is https://www.baeldung.com/the-persistence-layer-with-spring-data-jpa.
You can also use custom queries and simple queries using the #Query annotation.
Thanks
Try with an underscore for id like below;
public interface CountryStateMapping<CountryStateMapping, Long>{
Optional<CountryStateMapping> findByState_Id(long stateId);
Optional<CountryStateMapping> findByState(State state);
}

How to use projection interfaces with pagination in Spring Data JPA?

I'm trying to get a page of a partial entity (NetworkSimple) using the new feature of spring data, projections
I've checked the documentation and if I request only:
Collection<NetworkSimple> findAllProjectedBy();
It works, but if I'm using pageable:
Page<NetworkSimple> findAllProjectedBy(Pageable pageable);
It throws an error:
org.hibernate.jpa.criteria.expression.function.AggregationFunction$COUNT cannot be cast to org.hibernate.jpa.criteria.expression.CompoundSelectionImpl
Any one has already work with this ?
My NetworkSimple class is the following:
public interface NetworkSimple {
Long getId();
String getNetworkName();
Boolean getIsActive();
}
Note: This feature should work in the way described by the original poster but due to this bug it didn't. The bug has been fixed for the Hopper SR2 release, if you're stuck on an earlier version then the workaround below will work.
It is possible to use Pageable with the new query projection features introduced in Spring Data JPA 1.10 (Hopper). You will need to use the #Query annotation and manually write a query for the fields you require, they must also be aliased using AS to allow Spring Data to figure out how to project the results. There is a good example in spring-boot-samples part of the spring boot repository.
In your example it would be quite simple:
#Query("SELECT n.id AS id, n.name AS networkName, n.active AS isActive FROM Network n")
Page<NetworkSimple> findAllProjectedBy(Pageable pageable);
I have made the assumption that your entity looks something like this:
#Entity
public class Network
{
#Id
#GeneratedValue
private Long id;
#Column
private String name;
#Column
private boolean active;
...
}
Spring Data will derive a count query automatically for the paging information. It is also possible to make use of joins in the query to fetch associations and then summarise them in the projection.
I think you need create findAllProjectedBy() as specification.Then you can use findAll() method like this.
example :findAll(findAllProjectedBy(),pageable)
Following link may be help to find how to create specification in spring.
https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/
The issue may come from the method name. The by keyword means that you ae filterig data by a specific property: findByName for example. Its called query creation from method name:
http://docs.spring.io/spring-data/jpa/docs/1.10.1.RELEASE/reference/html/#repositories.query-methods.query-creation
So try with Page<NetworkSimple> findAll(Pageable pageable);
Even with spring-data-jpa 1.11.4, something like
public interface NetworkRepository extends JpaRepository<Network, String> {
Page<NetworkSimple> findAll(Pageable pageable);
}
would not compile; reporting
findAll(org.springframework.data.domain.Pageable) in NetworkRepository clashes with findAll(org.springframework.data.domain.Pageable) in org.springframework.data.repository.PagingAndSortingRepository
return type org.springframework.data.domain.Page<NetworkSimple> is not compatible with org.springframework.data.domain.Page<Network>
The workaround we found was to rename findAll to findAllBy, e.g.
public interface NetworkRepository extends JpaRepository<Network, String> {
Page<NetworkSimple> findAllBy(Pageable pageable);
}
You can use interface projection with Pageable like this :
Page<NetworkSimple> findPagedProjectedBy(Pageable pageable);
with some parameter :
Page<NetworkSimple> findPagedProjectedByName(String name, Pageable pageable);
Implementing interface projection with pagination
1. Our ResourceEntity.java class
#Getter
#Setter
#NoArgsConstructor
#Entity
public class ResourceEntity{
private Long id;
private String name;
}
2. Creating projection Interface name ProjectedResource.java, which maps data collected by the SQL query from repository layer method
public interface ProjectedResource {
Long getId();
String getName();
String getAnotherProperty();
}
3. Creating Repository layer method: getProjectedResources()
We are considering the database table name is resource.
We are only fetching id and name here.
#Query(name="select id, name, anotherProperty from resource", countQuery="select count(*) from resource", nativeQuery=true)
Page<ProjectedResource> getProjectedResources(Pageable page);
Hope the issue will be resolved!
You can use:
#Query("SELECT n FROM Network n")
Page<? extends NetworkSimple> findAllProjectedBy(Pageable pageable);

Multiple Repositories for the Same Entity in Spring Data Rest

Is it possible to publish two different repositories for the same JPA entity with Spring Data Rest?
I gave the two repositories different paths and rel-names, but only one of the two is available as REST endpoint.
The point why I'm having two repositories is, that one of them is an excerpt, showing only the basic fields of an entity.
The terrible part is not only that you can only have 1 spring data rest repository (#RepositoryRestResource) per Entity but also that if you have a regular JPA #Repository (like CrudRepository or PagingAndSorting) it will also interact with the spring data rest one (as the key in the map is the Entity itself).
Lost quite a few hours debugging random load of one or the other. I guess that if this is a hard limitation of spring data rest at least an Exception could be thrown if the key of the map is already there when trying to override the value.
The answer seems to be: There is only one repository possible per entity.
I ended up using the #Subselect to create a second immutable entity and bound that to the second JpaRepsotory and setting it to #RestResource(exported = false), that also encourages a separation of concerns.
Employee Example
#Entity
#Table(name = "employee")
public class Employee {
#Id
Long id
String name
...
}
#RestResource
public interface EmployeeRepository extends PagingAndSortingRepository<Employee, Long> {
}
#Entity
#Immutable
#Subselect(value = 'select id, name, salary from employee')
public class VEmployeeSummary {
#Id
Long id
...
}
#RestResource(exported = false)
public interface VEmployeeRepository extends JpaRepository<VEmployeeSummary, Long> {
}
Context
Two packages in the monolithic application had different requirements. One needed to expose the entities for the UI in a PagingAndSortingRepository including CRUD functions. The other was for an aggregating backend report component without paging but with sorting.
I know I could have filtered the results from the PagingAndSorting Repository after requesting Pageable.unpaged() but I just wanted a Basic JPA repository which returned List for some filters.
So, this does not directly answer the question, but may help solve the underlying issue.
You can only have one repository per entity... however, you can have multiple entities per table; thus, having multiple repositories per table.
In a bit of code I wrote, I had to create two entities... one with an auto-generated id and another with a preset id, but both pointing to the same table:
#Entity
#Table("line_item")
public class LineItemWithAutoId {
#Id
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid")
private String id;
...
}
#Entity
#Table("line_item")
public class LineItemWithPredefinedId {
#Id
private String id;
...
}
Then, I had a repository for each:
public interface LineItemWithoutId extends Repository<LineItemWithAutoId,String> {
...
}
public interface LineItemWithId extends Repository<LineItemWithPredefinedId,String> {
...
}
For the posted issue, you could have two entities. One would be the full entity, with getters and setters for everything. The other, would be the entity, where there are setters for everything, but only getters for the fields you want to make public. Does this make sense?

Resources