how to look if List exists in DB in Springboot - java

I have a springboot application where I am saving a list with my Meeting Entity. I can save my entities all at once, but I want to check before each save if one entity inside my list already exists inside my Db and then just save the ones which are not inside. But I am kinda stuck and do not know how to do it. Could someone look at my code and give me an advice?
MeetingController:
#PostMapping("/")
public void saveMeeting(#RequestBody List<Meeting> meeting){
List<Meeting> exist =
meetingService.findAllMeetingsWithName(meeting.stream().map(m -> m.getMeetingName()).collect(Collectors.toList()));
meeting.removeAll(exist);
meetingService.saveMeeting(meeting);
}
MeetingService:
public void saveMeeting(List<Meeting> meeting){
meetingRepository.saveAll(meeting);
}
Repository:
#Repository
public interface MeetingRepository extends JpaRepository<Meeting, Long> {
}

This is the functionality that you want
#Transactional
public void saveMeeting(List<Meeting> meeting){
List<Meeting> alreadyExist = meetingRepository.findByMeetingNameIn(meeting.stream().map(m -> m.getMeetingName()).collect(Collectors.toList());
meeting.removeAll(alreadyExist);
meetingRepository.saveAll(meeting);
}
and then just define that repository method
#Repository
public interface MeetingRepository extends JpaRepository<Meeting, Long> {
List<Meeting> findByMeetingNameIn (List<String> meetingNames);
}
Remember to override equals and hashcode in Meeting entity to consider the id field.

You can check whether your meetings are in the DB. Try this.
List<Meeting> findByValueIn(List<Meeting> values);

Since you already are planning to use JpaRepository, take advantage of the #Query and batch queries
#Repository
//custom query using IN
public interface MeetingRepository extends JpaRepository<Meeting, Long>
{
#Query("SELECT m from MEETING m where m.name IN (:names)")
public List<Meetings> findAllMeetingWithNames(List<String> meetings)
}
#Service
class MeetingService{
private MeetingRepository meetingRepository;
...
//other code....
#Autowired
public MeetingService(MeetingRepository meetingRepository){
this.meetingRepository = meetingRepository;
}
public void saveAllMeetings(List<Meeting> meetings){
//call repository custom query here
names = meetings.stream()
.map((m) -> m.name)
.collect(Collectors.toList());
List<Meeting> existingMeetings = meetingRepository.findAllMeetingWithNames(names);
//delete using a single query
meetingRepository.deleteAllInBatch(existingMeetings);
meetingRepository.saveAllAndFlush(meetings);
}
.....
}

Related

How to use JPQL without #Query annotation inside the method?

I want to use dependency inversion principle inside my book rental project. Before, I used AccountRepository that extends CrudRepository, so my method looked like this:
#Query("SELECT CASE WHEN COUNT(account) > 0 THEN true ELSE false END FROM
Account account WHERE account.id =:accountID")
boolean doesAccountExistsWithGivenID(#Param("accountID") int accountID);
I've created AccountRepository and class that implements this repository.
Class that implements interface is called PostgreSQLAccountRepository. And inside doesAccountExistsWithGivenID I want to query somehow to get same result.
It looks like this:
package bookrental.account;
import bookrental.bookrentals.BookRentals;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
#Repository
public class PostgreSQLAccountRepository implements AccountRepository {
private CrudRepository<Account, Integer> repository;
public PostgreSQLAccountRepository(CrudRepository<Account, Integer> repository) {
this.repository = repository;
}
#Override
public List<BookRentals> getAccountRentalsByGivenID(int accountID) {
//TODO
}
#Override
public void deleteById(Integer id) {
this.repository.deleteById(id);
}
#Override
public List<Account> findAll() {
return (List<Account>) this.repository.findAll();
}
#Override
public boolean doesAccountExistsWithGivenID(int accountID) {
//HERE I WANT TO USE JPQL
}
``}
I do not want to use existsByID, because I have a lot of methods that use JPQL so I need to know how to implement it inside the method.
The documentation is clear on how to customize methods from a Data repository:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.custom-implementations
Basically define the fragment of the interface you want to customize (CustomizedRepository). Extend this interface in your data repository
interface SomeRepositry extends CrudRepository<...>, CustomizedRepository
Create implementation for CustomizedRepository called CustomiyedRepositoryImpl. The Impl postfix is critical here. See the docs for more customizations.
You will need to autowire the SessionFactory and use it manually.
#Autowired
public setSessionFactory(EntityManagerFactory factory) {
if(factory.unwrap(SessionFactory.class) == null){
throw new NullPointerException("factory is not a hibernate factory");
}
this.hibernateFactory = factory.unwrap(SessionFactory.class);
}
After you have access to it, then you can use it directly
Session session = hibernateFactory.createSession();
Query query = session.createQuery("SELECT CASE WHEN COUNT(account) > 0 THEN true ELSE false END FROM Account account WHERE account.id =:accountID");
query.setParameter("accountId", "7277");
List list = query.list();

Spring Data JPA: Generate dynamic query

I have an entity that hold some logic data :
#Entity
public class Person {
private Long id.
private String name;
private int age;
private String address;
...
}
I create my Spring data interface
#Repository
public interface CardInventoryRepository extends JpaRepository<Person , Long> {
}
My purpose is to create a dynamic query based on the exist values of my entity for example
if the name is null the query is :
select * from Person p Where p.age=12 AND p.address="adress.."
When the address is null the query should be :
select * from Person p Where p.age=12 AND p.name="ALI"
I want to extract data using only the non empty fields ?
is there any solution suing spring data for building dynamic queries ?
Thanks in advance
Based on Spring doc https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#query-by-example
Query by Example (QBE) is a user-friendly querying technique with a
simple interface. It allows dynamic query creation and does not
require you to write queries that contain field names. In fact, Query
by Example does not require you to write queries by using
store-specific query languages at all.
DEFINITION:
An Example takes a data object (usually the entity object or a sub-type of it) and a specification how to match properties. You can use Query by Example with JPA
Repositories.
To do so, let your repository interface extend QueryByExampleExecutor<T>, for example:
public interface PersonRepository extends CrudRepository<Person, String>, QueryByExampleExecutor<Person> {
}
Here are the available methods in QueryByExampleExecutor :
public interface QueryByExampleExecutor<T> {
<S extends T> S findOne(Example<S> example);
<S extends T> Iterable<S> findAll(Example<S> example);
// … more functionality omitted.
}
USAGES:
Example<Person> example = Example.of(new Person("Jon", "Snow"));
repo.findAll(example);
ExampleMatcher matcher = ExampleMatcher.matching().
.withMatcher("firstname", endsWith())
.withMatcher("lastname", startsWith().ignoreCase());
Example<Person> example = Example.of(new Person("Jon", "Snow"), matcher);
repo.count(example);
MORE INFO
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#query-by-example
https://github.com/spring-projects/spring-data-examples/tree/master/jpa/query-by-example
Spring Data JPA: Query by Example?
Yes, please take a look at the QueryDSL support for Spring Data. Your use case can be implemented via a Predicate. In a nutshell, you have to create a predicate in which you would pass the non null fields, and then pass that predicate to a findAll method that takes a Predicate as argument. Your repository interface also has to extend QueryDslPredicateExecutor
Need to extend repository from JpaSpecificationExecutor
#Repository
#Transactional
public interface EmployeeDAO extends CrudRepository<Employee,Long>,JpaSpecificationExecutor<Employee>{
}
Use specification and predicate like below
public List<Employee> findByCriteria(String employeeName,String employeeRole){
return employeeDAO.findAll(new Specification<Employee>() {
#Override
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
List<Predicate> predicates = new ArrayList<>();
if(employeeName!=null) {
predicates.add(criteriaBuilder.and(criteriaBuilder.like(root.get("employeeName"), "%"+employeeName+"%")));
}
if(employeeRole!=null){
predicates.add(criteriaBuilder.and(criteriaBuilder.equal(root.get("employeeRole"), employeeRole)));
}
return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
}
});
}

Customize a Spring MongoRepository - filter by class

I´ve got several Java documents that are stored in the same collection "app" in mongodb. My intention is use it for the common persistence actions (insert, find, delete...). The problem comes when I try to define repositories for only one of the classes as the repository that I´ve defined will search all the entities, and what I need is a repository where I can call to all the mongorepository standard functions (find, findAll, findby...). My explanation can be hard to understand, this is what I have now:
Base document:
#Document(collection = "app")
#NoRepositoryBean
public abstract class AbstractApplicationDocument {
#Id
public String mongoId;
// more variables, getters, setters...
}
One of the subdocuments (there will be many).
#EqualsAndHashCode(callSuper=true)
public class ClientApplicationDocument extends AbstractApplicationDocument
{
#Id
public String mongoId;
}
Base Repository
#NoRepositoryBean
public interface ApplicationRepository<T, ID extends Serializable> extends MongoRepository<T, ID> {
}
Extended repository (where I want to filter by class)
public interface HttpClientRepository extends ApplicationRepository<HttpClientDocument, String> {
}
Sample of temporary solution that I'd like to avoid if I can reuse the MongoDb utilities
#Component
public class HttpClientRepositoryImpl {
#Autowired
private HttpClientRepository clientRepo;
#Autowired
private MongoTemplate mongo;
public List<HttpClientDocument> findAll() {
Query query = new Query();
query.addCriteria(Criteria.where("_class").is(HttpClientDocument.class.getName()));
mongo.getConverter();
return mongo.find(query, HttpClientDocument.class,"app");
}
}
Relevant information in gradle. I add this because I've seen some solutions that are not valid for me. Probably because they are using different libraries:
compile("org.springframework.boot:spring-boot-starter-data-mongodb:${springBootVersion}")
compile ("org.springframework:spring-context-support:4.1.8.RELEASE");
Is there any simple solution?
You can customize Spring Data repositories in many ways. You can provide custom repository base classes or provide custom implementation classes.
For your question in particular, SimpleMongoRepository does not restrict query on the _class field. You can see below an example for a custom repository base class that restricts the findAll query method to use only the declared entity type.
You would be required to apply that pattern also for other methods which are declared on the MongoRepository level that you want to restrict to the particular class type.
#EnableMongoRepositories(repositoryBaseClass = MyMongoRepository.class)
static class Config {
}
static class MyMongoRepository<T, ID extends Serializable> extends SimpleMongoRepository<T, ID> {
private final MongoEntityInformation<T, ID> metadata;
private final MongoOperations mongoOperations;
public MyMongoRepository(MongoEntityInformation<T, ID> metadata, MongoOperations mongoOperations) {
super(metadata, mongoOperations);
this.metadata = metadata;
this.mongoOperations = mongoOperations;
}
#Override
public List<T> findAll() {
Query query = new Query();
query.restrict(this.metadata.getJavaType());
return this.mongoOperations.find(query, this.metadata.getJavaType(), this.metadata.getCollectionName());
}
}
findAll queries restrict now the class type and queries for a e.g. ModelRepository extends MongoRepository<Model, String> would look like:
{ "_class" : { "$in" : [ "com.example.Model"]}}
Query methods with #Query can be used to pass in the class type (#Query("{'_class': ?0}")) but there's no way to contrain the class type for derived query methods (List<Model> findByFirstnameAndLastname(…)).
There's an open ticket in our Jira related to restricting types of the repository. That particular ticket is related to polymorphic queries but in its essence, it's related to type restrictions on query methods.

How to extract "find or create" method to abstract class? (Spring Data Jpa)

I'm using Spring Data JPA and I have a bunch of repositories like this one:
public interface CustomerRepository extends JpaRepository<Customer, Long> {}
Under repositories I have services and a lot of them need to have implemented method findOrCreate(String name) like this one:
#Override
#Transactional
public List<Customer> findOrCreate(final String name) {
checkNotNull(name);
List<Customer> result = this.customerRepository.findByName(name);
if (result.isEmpty()) {
LOGGER.info("Cannot find customer. Creating a new customer. [name={}]", name);
Customer customer = new Customer(name);
return Arrays.asList(this.customerRepository.save(customer));
}
return result;
}
I would like to extract method to the abstract class or somewhere to avoid implementing it for each services, testing and so on.
Abstract class can be look like this:
public abstract class AbstractManagementService<T, R extends JpaRepository<T, Serializable>> {
protected List<T> findOrCreate(T entity, R repository) {
checkNotNull(entity);
checkNotNull(repository);
return null;
}
}
And the problem is it due to the fact that I need to find object by name as a string before creating a new one. Of course interface JpaRepository doesn't offer this method.
How can I solve this problem?
Best Regards
Create a custom JpaRepository implementation that includes this behaviour. See this post for an example of writing a custom JpaRepository implementation.

Print Records from FindAll() in CrudRepository

I am extending CrudRepository in my Repository class. I want to print the records in my table using the findAll method. So far, I have written a test class, and I can see the result query is correct. How can I print the individual records in the table?
Here is a snippet of my code:
Repository Class
public interface RepositoryAda extends CrudRepository{
}
Service Class
#Service
public class Service{
#Autowired private RepositoryAda repository;
#Transactional
public List selectRecords(){
return (List) repository.findAll();
}
}
Test Case:
#Test
public void getAllRecords() {
service.selectRecords();
}
How can I print the individual records from the table to a console?
I prefer to use Google's Guava when using the repository interfaces. You can turn findAll() Iterable into a List<Type> with one line.
public RecordRepository extends CrudRepository<Record, Long> {}
public class RecordServiceImple implements RecordService {
RecordRepository recordRepository;
public List<Record> selectRecord() {
return Lists.newArrayList(recordRepository.findAll()); // Guava library
// or just simply cast it.
// return (List<Record>)recordRepository.findAll();
}
}
Then just loop through the list
for (Record record : records) {
System.out.println(record);
}
Just overrive the toString() in your Record class, or whatever your class name is, to tabular formatting using String.format()

Categories