I have a JPA query to delete selected entities which is stored in a list. I am currently deleting the entities in a loop via the in built JPA delete() method 1 by 1. Is there a way to just pass in the list instead of looping it?
Current implementation that works but looping to delete 1 by 1. I need the initial query to get list of entities for other reasons thus not looking to change that. Just a way to pass in the list of entiies to be deleted. Please advice. Thanks.
Note: This is with Java 8 and Spring 4.3 if it matters.
#GetMapping("/delete/{name}/{count}")
public String delete(#PathVariable String name, #PathVariable int count){
boolean isDelete = true;
while (isDelete){
//1st query
List<PersonEntity> results = personService.get(name, count);
if(results != null && !results.isEmpty()){
System.out.println("Deleting following: ");
//2nd query
results.forEach(p -> {
System.out.println(p.getName());
personService.delete(p);
});
} else {
isDelete = false;
}
}
return "Done!";
}
You can try something like this:
List<PersonEntity> results = personService.get(name, count);
if(results != null && !results.isEmpty()) {
List<Integer> personIds = results.stream()
.map(personIds)
.collect(Collectors.toList());
personService.deleteManyById(personIds);
In your service:
public void deleteManyById(List<Integer> ids) {
personRepository.deleteByIdIn(ids);
}
In your repo (assuming it's a spring JpaRepository):
void deleteByIdIn(List<Integer> ids);
Just be aware of the fact that dbs have a limit in the number of parameters you can pass in a IN condition
Related
I have a task to read 2 files and match the contents of the files and provide a list of unmatched entries of both files. That means I have to present how many matched entries in the two files and how many unmatched entries in file 1 which is not in file 2 , how many unmatched entries in file 2 which is not in file 1.
My apporach is reading the files , creating java objects out of it, putting the contents of 2 files to 2 separate arraylists and compare them. My current code is listed below. For clarification, I want to check the content of the object ( eg : check EmployeeID and match from both files).
In below code, I have matched file1 content with file2, and removed the matched contents from file2.Works fine to match entries and get the unmatched count of file1 compared to file2.
I am plannning to match the remaining items in file2 and go another round in the same compareByEmpIdandDOBmethod using fileTwoEmpList as first parameter and fileOneEmpList as second parameter get the unmatched count of file2 compared to file1. But I feel this is an overkill and not very efficient. Can someone point out a different approach if any pelase ?
Both of the arraylists are sorted. Thanks in advance !
public class EmpMatching {
public void compareLists(List<EmployeeDetails> fileOneEmpList, List<EmployeeDetails> fileTwoEmpList){
Collections.sort(fileOneEmpList);
Collections.sort(fileTwoEmpList);
List<EmployeeDetails> unmatchedFromListTwo = compareByEmpIdandDOB(fileOneEmpList,fileTwoEmpList);
}
public List<EmployeeDetails> compareByEmpIdandDOB(List<EmployeeDetails> fileOneEmpList,List<EmployeeDetails> fileTwoEmpList){
int matchEmpCountFromTwoFiles = 0;
System.out.println("File One List Size Before Recon " + fileTwoEmpList.size());
for(EmployeeDetails fileOneEmp : fileOneEmpList){
for(int index = 0;index < fileTwoEmpList.size();index++ ){
EmployeeDetails fileTwoEmp= fileTwoEmpList.get(index);
if(fileOneEmp.getEmpID().equals(fileTwoEmp.getEmpID()) && fileOneEmp.getEmpDOB().equals(fileTwoEmp.getEmpDOB())){
matchEmpCountFromTwoFiles++;
fileTwoEmpList.remove(fileTwoEmp);
System.out.println("Match Found " + fileOneEmp.getEmpID());
}
}
System.out.println("File Two List Size " + fileTwoEmpList.size());
}
System.out.println("Match Count >>>>> " + matchEmpCountFromTwoFiles);
System.out.println("File Two List Size >>>>> " + fileTwoEmpList.size());
return fileTwoEmpList;
}
}
//Model class
public class EmployeeDetails implements Comparable<EmployeeDetails>{
private String EmpID;
private String EmpName;
private String EmpDOB;
#Override
public int compareTo(EmployeeDetails o) {
return 0;
}
}
You don't need to sort these lists for this task.
In terms of the Set theory, you need to find the set difference. I.e. to find all unique objects that appear only in the first or in the second list.
This task can be solved in a few lines of code with liner time complexity. But it is important to implement the equals/hashCode contract in the EmployeeDetails.
public List<EmployeeDetails> compareLists(List<EmployeeDetails> fileOneEmpList,
List<EmployeeDetails> fileTwoEmpList) {
Set<EmployeeDetails> emp1 = new HashSet<>(fileOneEmpList);
Set<EmployeeDetails> emp2 = new HashSet<>(fileTwoEmpList);
emp1.removeAll(emp2);
emp2.removeAll(emp1);
emp1.addAll(emp2);
return new ArrayList<>(emp1);
}
The approach above is both the most efficient and the simplest.
If you are comfortable with Streams API, you can try another approach and implement this method in the following way:
public List<EmployeeDetails> compareLists(List<EmployeeDetails> fileOneEmpList,
List<EmployeeDetails> fileTwoEmpList) {
return Stream.of(new HashSet<>(fileOneEmpList), new HashSet<>(fileTwoEmpList)) // wrapping with sets to ensure uniqueness (if objects in the list are guaranteed to be unique - use lists instead)
.flatMap(Collection::stream)
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
.entrySet().stream()
.filter(entry -> entry.getValue() == 1) // i.e. object appear only once either in the first or in the second list
.map(Map.Entry::getKey)
.collect(Collectors.toList()); // .toList(); for Java 16+
}
Time complexity of the stream based solution would be linear as well. But as I've said, the first solution based on the Collections API is simpler and slightly more performant.
If for some reason, there's no proper implementation of equals() and hashCode() in the EmployeeDetails. And you have no control over this class and can't change it. Then you can declare a wrapper class and perform the same actions.
Below is an example of how to create the wrapper using Java 16 records.
Methods equals() and hashCode() will be generated by the compiler based on empId and empDob.
public record EmployeeWrapper(String empId, String empDob) {
public EmployeeWrapper(EmployeeDetails details) {
this(details.getEmpID(), details.empDOB);
}
}
The implementation of the equals/hashCode for the EmployeeDetails class based on the empID and empDOB might look like this (also, you can use the facilities of your IDE to generate these methods):
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
EmployeeDetails that = (EmployeeDetails) o;
return empID.equals(that.empID) && empDOB.equals(that.empDOB);
}
#Override
public int hashCode() {
return Objects.hash(empID, empDOB);
}
How do I return the deleted rows using jdbi? For example, I'd like to do something like this:
public List<User> deleteAndReturnUsers() {
return jdbi.withHandle(handle -> {
return handle.createQuery("DELETE FROM mytable where id = :id")
.bind("id", "someid")
.map(new UserMapper())
.list();
});
}
Postgresql has a returning keyword...
https://dbfiddle.uk/?rdbms=postgres_13&fiddle=25d284309745e40c8ea29945481d1aa2
DELETE FROM data WHERE val >= 2 RETURNING *;
You can then execute that query in the same way as you would a normal SELECT statement, and get a result set back; containing the records deleted.
I have a method that allows removing the publication by a specific year.
However, I want to put all the publication which I will be removed into a new array and print them.
Any suggestion or help?
Thank you.
enter code here
public void removeYear(int removeYear)
{
Iterator<Publication> pb = publicationList.iterator();
while(pb.hasNext()){
Publication publication = pb.next();
if(publication.getYear() == removeYear){
pb.remove();
}
}
}
First what you should do is change the return type of the method to List because besides the deleting part we also want to save data to a new List so when calling this method it will return a List with the deleted data.
public List<Publication> removeYear(int removeYear){
And you have to declare a new List of Publication inside the method, so there we can save the deleted Publications
List<Publication> listNew = new ArrayList<>();
And we simply save the object with the add() method
listNew.add(publication);
All the code
public List<Publication> removeYear(int removeYear){
Iterator<Publication> pb = publicationList.iterator();
List<Publication> listNew = new ArrayList<>();
while(pb.hasNext()){
Publication publication = pb.next();
if(publication.getYear() == removeYear){
listNew.add(publication);
pb.remove();
}
}
return listNew;
}
Here we have returned a new list with deleted Publications.
And later if you want to print it simply call the method
List<Publication> deletedList = classIntance.removeYear(2000);
And print it
System.out.println("Deleted Publication are");
deletedList.stream().forEach(System.out::println);
The following approach may not be optional but seems to be a bit cleaner:
filter out the publications to be deleted by year
Use List::removeAll to complete removal:
public void removeYear(int removeYear) {
List<Publication> toDelete = publicationList.stream()
.filter(p -> p.getYear() == removeYear)
.collect(Collectors.toList());
// use publications from `toDelete` as needed
// ...
publicationList.removeAll(toDelete);
}
Another approach may be based on using Collectors.partitioningBy collector which would split the list into two parts by predicate value:
public void removeYear(int removeYear) {
Map<Boolean, Publication> partitioned = publicationList.stream()
.collect(Collectors.partitioningBy(p.getYear() == removeYear));
System.out.println("deleted publications: " + partitioned.get(true));
// keep publications without the removeYear
publicationList = partitioned.get(false);
}
This is in my DAO:
public List<Weather> getCurrentWeather() {
return sessionFactory.getCurrentSession().createQuery("from Weather").list();
}
This gets all of the elements from table Weather. But lets say I wanna do something like this(I want only one element from table Weather):
public Weather getCurrentWeather() {
return sessionFactory.getCurrentSession().createQuery("from Weather where id = 1").list(); // here should be something else than list()
}
I know there should not be list() in the end, but what must I write there, to get only one object?
If you have an id, you just use get:
public Weather getCurrentWeather() {
return sessionFactory.getCurrentSession().get(Weather.class, 1);
}
If you do need to do a query, yeah you'll have to grab the top of the result set, or you can use uniqueResult() on the query.
Is there something wrong with getting a list? :) Even if you know there is only 1 hibernate cannot assume that. Getting a list is safer anyway!
public Weather getCurrentWeather() {
List<Weather> list = sessionFactory.getCurrentSession().createQuery("from Weather where id = 1").list(); // here should be something else than list()
return (list.isEmpty() ? null : list.get(0));
}
You need to use the Criteria API as below:
List<LeaveManagement> leaveManagements = session.createCriteria(LeaveManagement.class)
.add( Restrictions.isNull("approvedTimeStamp") )
.uniqueResult();
If you want to write a hql query you can write as:
String hql = "from LeaveManagement where approvedTimeStamp is null";
Query query = session.createQuery(hql);
LeaveManagement results = (LeaveManagement) query.uniqueResult();
I have list List<Long> list, that contains: [160774, 7212775] and Long id = 7212775. I need to check if the list contains an element with value of id. How to do that? Unfortunately list.contains(id) returns false in my case.
I'm using it that way:
#RequestMapping("/case/{id}")
public String openCase(#PathVariable("id") Long id) {
log.debug(caseDAO.findAllCasesId()); // [160774, 7212775]
log.debug(id); // 7212775
if(caseDAO.findAllCasesId().contains(id)) {
return "case";
} else {
return "404";
}
}
Piece of DAO (Hibernate, but native sql here):
public List<Long> findAllCasesId() {
String sql = "select id from cases";
SQLQuery query = getSession().createSQLQuery(sql);
return query.list();
}
SOLVED
The problem was with caseDAO.findAllCasesId(), that return list of Object, not list of Long. I corrected this by:
SQLQuery query = getSession().createSQLQuery(sql).addScalar("id", Hibernate.LONG);
Big thanks to: Nayuki Minase
When autoboxing, you need to make sure you postfix the literal with an L i.e. Long id = 7212775L for this to work.
Running the code below on eclipse helios:
public static void main(String[] args) {
List<Long> list = new ArrayList<Long>();
list.add(160774L);
list.add(7212775L);
System.out.println(list.contains(7212775L);
}
Output:
true
What you're doing wrong is
System.out.println(list.contains(7212775));
The problem is that your list takes Long objects and you are searching for a literal.
List<Long> list = new ArrayList<Long>(Arrays.asList(160774L, 7212775L));
Long id = 7212775L;
System.out.println(list.contains(id)); // prints true
Hum, I believe List list should return true in your case.
The following piece of code returns true.
List<Long> listOfLongs = new java.util.ArrayList<Long>();
listOfLongs.add(160774L);
listOfLongs.add(7212775L);
return listOfLongs.contains(7212775L);
Same with
List<Long> listOfLongs = new java.util.ArrayList<Long>();
listOfLongs.add(Long.valueOf(160774L));
listOfLongs.add(Long.valueOf(7212775L));
return listOfLongs.contains(Long.valueOf(7212775L));
If you could show us the code where this ain't working, it would help.