android Room, how to delete multiple rows in the table by id list - android-room

Having a Android Room with three tables timestamp, index, details, and all three have
#PrimaryKey #ColumnInfo(name = "id") var id: Int = 0
having fun clearDataByID(idList: List<Int>) to clear data from all three tables by the id in the idList
Dao as:
#Dao
interface DataDAO {
#Transaction
fun clearDataByID(idList: List<Int>) {
deleteDataInTimestamp(idList)
deleteDataIndex(idList)
deleteDataDetails(idList)
}
#Query("delete from timestamp where id in :idList")
fun deleteDataInTimestamp(idList: List<Int>)
#Query("delete from index where id in :idList")
fun deleteDataIndex(idList: List<Int>)
#Query("delete from details where id in :idList")
fun deleteDataDetails(idList: List<Int>)
}
but it gets compiler error (similar for all three)
error: no viable alternative at input 'delete from timestamp where id in :idList'
public abstract void deleteDataInTimestamp(#org.jetbrains.annotations.NotNull()
if delete by single id it worked.
How to delete by a list of ids?
#Query("delete from timestamp where id = :id")
fun deleteSingleTimestamp(id: Int)

Thanks Simon points to the similar question, it should be done like:
#Query("delete from timestamp where id in (:idList)")
fun deleteDataInTimestamp(idList: List<Int>)

just extending the answer given by #lannyf
#Query("delete from timestamp where id in (:idList)")
fun deleteDataInTimestamp(idList: List<Int>)
this could be written in this way also
#Query("delete from timestamp where id = :idList")
fun deleteDataInTimestamp(idList: List<Int>)
PS: edit queue was full

Related

How to tell Spring Data MongoDB to store nested fields of a document that are also documents in their own collection?

I have two collections called persons and addresses. The idea is to have person hold an address in the field address. I use Spring Data MongoDB to persist those mentioned documents.
My usual way of crafting the "relation" between Person > Address was to store the ID of the address and give it to the person object. Later when I find() a person I resolve the address object by it's id and voila I have my person + address.
However I find this somewhat every cumbersome since in my code I just want to add the Address object as whole and not only it's ID so I can work with it while also saving it to the repository at any point of time.
I therefore started a little unit test to see how Spring Data MongoDB saves the Address object if it's just a field of Person and is not saved by it's own Repository.
This is what I came up with:
import org.springframework.data.mongodb.core.mapping.Document
import org.springframework.data.mongodb.repository.MongoRepository
import org.springframework.stereotype.Repository
#Document("person")
data class Person(
val id: String,
val name: String,
val age: Int,
var address: Address
)
#Document("addresses")
data class Address(
val id: String,
val street: String?,
val number: Int?
)
#Repository
interface PersonRepository : MongoRepository<Person, String>
#Repository
interface AddressRepository : MongoRepository<Address, String>
And this is the unit test - that fails with the last steps as I was expecting:
internal class FooTest #Autowired constructor(
private val personRepository: PersonRepository,
private val addressRepository: AddressRepository
) {
#Test
fun `some experiment`() {
val testPerson = Person("001", "Peter", 25, Address("011","Lumberbumber", 12))
personRepository.save(testPerson)
val person = personRepository.findAll()[0]
assertThat(person).isNotNull
assertThat(person.address).isNotNull
assertThat(person.address.street).isEqualTo("Lumberbumber")
assertThat(person.address.number).isEqualTo(12)
// works because address was just copied into the object structure
// of `person` and was not seen as a standalone document
val address = addressRepository.findAll()[0]
assertThat(address.street).isEqualTo("Lumberbumber") // fails
assertThat(address.number).isEqualTo(12) // fails
// As expected `address` was not persisted alongside the `person` document.
}
}
So I thought about using AbstractMongoEventListener<Person> to intercept the saving process and pick the Address object out from Person here and do a addressRepository.save(addressDocument) while putting a lightweight address object (only having the ID) back in the Person document.
The same I'd to in the reverse when doing a find for Person and assembling Person and Address together again.
#Component
class MongoSaveInterceptor(
val addressRepository: AddressRepository
) : AbstractMongoEventListener<Person>() {
override fun onBeforeConvert(event: BeforeConvertEvent<Person>) {
val personToSave = event.source
val extractedAddress = personToSave.address
val idOfAddress = addressRepository.save(extractedAddress).id
personToSave.address = Address(idOfAddress, null, null)
}
override fun onAfterConvert(event: AfterConvertEvent<Person>) {
val person = event.source
val idOfAddress = person.address.id
val foundAddress = addressRepository.findById(idOfAddress)
foundAddress.ifPresent {
person.address = it
}
}
}
It works that way and might be a workaround solution for my requirement.
BUT
I feel that there has to be something like that already working and I might just need to find the proper configuration for that.
That's where I am stuck atm and need some guidance.
Another research showed me it's about #DBRef (https://www.baeldung.com/cascading-with-dbref-and-lifecycle-events-in-spring-data-mongodb) I have to use. This way Spring Data MongoDB stores the embedded document class and resolves it when loading the parent document object from the database.

Spring JPA: How should I create/use a look up table?

So I have 2 different entities:
#Entity
data class PickupOrderEvent(
#Id
override val id: UUID,
#Enumerated(EnumType.STRING)
#Column(nullable = false)
override val eventType: EventType,
// other fields...
)
#Entity
data class DeliveryOrderEvent(
#Id
override val id: UUID,
#Enumerated(EnumType.STRING)
#Column(nullable = false)
override val eventType: EventType,
// other fields... different than PickupOrderEvent
)
Both of these inherit from a parent class OrderEvent, but they have other (different) fields.
What I ultimately want to do, is to receive an OrderEvent id, and get that event from its respective table. However, I don't want to look at both tables (and later, even more tables) to figure out what the type is.
I imagined the right answer here, is to create a "lookup table" which looks something like this:
Events(
val eventId: UUID, // FK to the other tables PK
val eventType: EventType
)
So I would do the following:
1- Look at this table, and figure out what the eventType is for this Order.
2- Look at the respective tables (depending on the eventType above) and get the event
How can/should I go about this?
I don't think you need a look up table.
This looks more like a case for the "JOINED" inheritance mapping strategy.

Spring-boot jpa how to find entity with max value

Lets tell I have two tables.
CREATE TABLE user (ID int AUTO_INCREMENT,PRIMARY KEY (ID));
CREATE TABLE points (ID int AUTO_INCREMENT, user_id int, points int,PRIMARY KEY (ID));
How can I use spring-boot jpa to request user and max points like this?
select u.ID,max(p.points) from user u, points p where u.id=p.user_id
Or any alternatives to solve this kind of problems?
Assuming you have a Repository of User:
public class User {
private int id;
private List<Point> points;
...
}
With a relationship to the Points object:
public class Point {
private int id;
private User User;
private int points;
...
}
I haven't tested, but you should be able to do:
User findFirstByIdOrderByPointPointsDesc(int userId)
Similar to example 18 in the docs.
The only problem you have, regardless of the query or Spring Data, is if you have two users with the same point values. If you need more logic around tie-breaking, it might be more worth it to write a #Query (with your query, plus the extra tie-breaking logic) or a #NativeQuery.
I usually create a class to hold result such as
public class Result {
private User user;
private int votes;
// getters and setters
}
And write a custom query in the repository to fetch the data
#Query(value = "SELECT new com.package.Result (u, MAX (p.points) )
FROM user u
JOIN points p
ON u.id = p.user_id
GROUP BY u")
List<Result> getPointsPerUser();
Replace com.package.Result with appropriate path to the Result class.
Below method can be written in Repo and used as Transaction as in dao layer, which will be accessible from service layer.
#Query(value = "SELECT max(transactionId) FROM TransactionPayloadInfo")
int getMaxTransactionId();
create a model of data.
public class Data {
private int id;
private int maxPoints;
// getters and setters method
}
And write your query like this for getting model of Data.
#Query(select packagename.Data(u.ID,max(p.points) ) from user u, points p where u.id=p.user_id)
Data findUserWithMaxVots();

What is the most convenient way to deal with nested objects in Room?

I want to save the server’s response in database (class Parent). The json has nested object, which also should be saved in database in new table (class Nested). The problem is what I don’t know how to write class Parent and ParentDao to make it use NestedDao
#Entity
data class Parent(
#PrimaryKey(autoGenerate = true)
var id: Long? = null,
#SerializedName(«nested»)
val homeTeam: Nested,
//other fields
)
#Entity
data class Nested(
#PrimaryKey(autoGenerate = true)
var nestedId: Long? = null,
#SerializedName("name")
val name: String,
//other fields
)
#Dao
interface ParentDao {
#Query("SELECT * FROM parent»)
fun getData(): Single<List<Parent>>
#Insert
fun insert(matches: List<Parent>)
}
This gives me an error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.
So, what should I do to save and query Parent with Nested at once?
I don't know if you've succeed or not, but here is my answer I hope it'll help you.
That's what I used for my project and what is recommended for Room in Android docs
#Entity
data class Parent(
#PrimaryKey(autoGenerate = true)
var id: Long? = null,
#Embedded #ColumnInfo(name= "nested")
val homeTeam: Nested,
//other fields
)
data class Nested(
#PrimaryKey(autoGenerate = true)
var nestedId: Long? = null,
#ColumnInfo(name= "name")
val name: String,
//other fields
)
#Dao
interface ParentDao {
#Query("SELECT * FROM parent»)
fun getData(): Single<List<Parent>>
#Insert
fun insert(matches: List<Parent>)
}

JPA CrudRepository for findIn method

In one requirement I need find in a list by id. Below is the example.
class Employee{
Long id;
Contractor contractor;
}
class Contractor {
Long id;
List<Address> addressList;
}
class Address {
Long id;
String line;
}
Now in EmployeeController.java class I need to find out by employee Id and address Id.
I have CrudRepository as below.
interface EmployeeRepository extends CrudRepository<Employee, Long> {
Employee findOne(Long id);
// I want one method here that can find by employee id and address id.
Employee findByIdAndAddress();
}
When I try to run the spring application up it gives the below exception.
PropertyReferenceException: No property Address found for type Employee!
Thank you!
Well, the exception itself is quite clear, it tries to find a property "address" on Employee, which does not exist, so you'll have to join Contractor and Address to make that work. However, as far as I can see in the list of reserved keywords, you can't do that with method naming.
So the solution is to write your own query, for example:
#Query("SELECT e FROM Employee e JOIN e.contractorList c JOIN c.addressList a " +
"WHERE e.id = :id AND a.id = :addressId")
Employee findByIdAndAddress(#Param("id") Long id, #Param("addressId") Long addressId);

Resources