Android Room #Delete with parameters - java

I know I can't use DELETE in a query (that is a shame by the way), I will get the following error:
<i>Error:error: Observable query return type (LiveData, Flowable etc) can only be used with SELECT queries that directly or indirectly (via #Relation, for example) access at least one table.</i>
But I can't use #Delete(WHERE... xxx)
So how do I delete a specific row by a parameter?

Actually, you can use #Query to perform a delete.
#Query("DELETE FROM users WHERE user_id = :userId")
abstract void deleteByUserId(long userId);
Extracted from Query javadoc:
UPDATE or DELETE queries can return void or int. If it is an int, the
value is the number of rows affected by this query.

The beauty of room is, we play with the objects. As per requirement you can use
for kotlin:
#Delete
fun delete(model: LanguageModel)
for Java:
#Delete
void delete(LanguageModel model)
it will delete the exact object which is stored in the db with the same values. LanguageModel is my model class and it works perfectly.

You can use below method to delete by ID
#Query("DELETE FROM yourDatabaseTable WHERE id = :id")
void deleteById(int id);
for delete all rows
#Query("DELETE FROM yourDatabaseTable")
void delete();

ROOM database provides easy way to INSERT, UPDATE and DELETE an object in the database. To perform thus operation just needed to annotate #Delete. The DELETE operation returns the Int when deletion of the single object is successful returns 1 else returns 0 if the DELETE operation is unsuccessful, Adding the return type is a good practice.
KotlinEG.kt
#Dao
interface EntityLocalDAO {
#Delete
fun deleteData(entityObject: EntityObject) : Int
}
javaEG.java
#Dao
interface EntityLocalDAO {
#Delete
int deleteData(EntityObject entityObject);
}

You can now delete using only partial data.
Per the documentation:
#Entity
data class Playlist (
#PrimaryKey
val playlistId: Long,
val ownerId: Long,
val name: String,
#ColumnInfo(defaultValue = "normal")
val category: String
)
data class OwnerIdAndCategory (
val ownerId: Long,
val category: String
)
#Dao
public interface PlaylistDao {
#Delete(entity = Playlist::class)
fun deleteByOwnerIdAndCategory(varargs idCategory: OwnerIdAndCategory)
}
In this example you can see that they are deleting the Playlist using only the ownerId and the category. You do not even need to use the primary key (playlistId).
The key is to use the #Delete(entity = Playlist::class) annotation.

Related

Android room how to return a flow of Map where the map value can be a pair or an object?

I have a table like this:
data class ChapterDetails(
val id: Int,
var correctAnswersCount: Int,
var totalQuestionsCount: Int,
// ... ... ...
)
I would like to return a Flow<Map<Int, Pair<Int, Int>> > from the room dao. I saw this stackOverflow answer, and this mapInfo doc but they return only 2 comumns. I need a map where the key is Id, and value is a pair of <correctAnswersCount, totalQuestionsCount>. What is the room way of doing this?
Probably not exactly what you want, but an alternative to writing complex SQL query can be to transform the flow of list of ChapterDetails which can be returned by the DAO easily.
interface ChapterDetailsDao {
#Query("SELECT * FROM ChapterDetails")
fun getChapterDetails(): Flow<List<ChapterDetails>>
}
val chapterDetailsMapFlow: Flow<Map<Int, Pair<Int, Int>>> =
dao.getChapterDetails().map { list ->
list.associate { it.id to (it. correctAnswersCount to it. totalQuestionsCount) }
}
If you are worried that fetching all the columns when you only need three will impact performance, I guess you will have to create a new data class consisting only those particular columns, but even then you will have to convert the list to map yourself as above.
data class ChapterDetailsCounts(
val id: Int,
var correctAnswersCount: Int,
var totalQuestionsCount: Int
)
interface ChapterDetailsDao {
#Query("SELECT id, correctAnswersCount, totalQuestionsCount FROM ChapterDetails")
fun getChapterDetails(): Flow<List<ChapterDetailsCounts>>
}
You could something like:-
A) Have a suitable POJO e.g. :-
data class ChapterDetailsCount(
val chapterDetailsCount: Map<Int,Pair<Int,Int>>
)
B) Have a base query that gets the respective ChapterDetails list. e.g.
#Query("SELECT * FROM ChapterDetails")
fun getChapterDetails(): List<ChapterDetails>
C) Have a function (in the #Dao annotated interface) with a body, that uses the base query to get the ChapterDetails list but then returns the list of the POJO e.g.
fun getChapterDetailsCountList(): List<ChapterDetailsCount> {
val rv: ArrayList<ChapterDetailsCount> = arrayListOf()
for (cd in getChapterDetails()) {
rv.add(ChapterDetailsCount(mapOf(cd.id to Pair(first = cd.correctAnswersCount, second = cd.totalQuestionsCount))))
}
return rv.toList()
}
Note I haven't used Flow because I don't do stuff with Flow
The following demonstrates:-
db = TheDatabase.getInstance(this)
dao = db.getChapterDetailsDao()
val t1 = dao.getChapterDetailsCountList()
dao.insert(ChapterDetails(100,5,10))
val t2 = dao.getChapterDetailsCountList()
with a breakpoint after the last line and :-
i.e.
first is empty as expected (now rows exist at that point)
second has the expected data from the first row
You just need to adapt it to use Flow

How to get incremental value in JPA?

I'm trying to get incremented value only while extracting data from Table. Below is my code snippet.
public interface Repo extends PagingAndSortingRepository<T,T> {
#Query("Select new CustomObject(name, phone, place) from table")
List<CustomObjectDTO> getData();
}
Note: CustomObjectDTO has all three attributes mentioned in select query.
May I know please How can I get the incrementalID as first col in select query ? I mean need a serial number with every record in select query output.
Any suggestions ?
It seems to be this:
#Query("Select new CustomObject(incremantal_column_name,name, phone, place) from table")
List<CustomObjectDTO> getData();
and in CustomObjectDTO class:
#Column(name = "incremantal_column_name)
long incrementalID;

Spring Data JPA #Query with foreign key: Parameter not matched

I have a table "Signal" with id, volume and object_id columns.
Object_id is a foreign key. I need to retrieve each signal that has a particular object_id.
Im trying to use this Query
public interface SignalRepository extends JpaRepository<Signal, Integer> {
#Query("select s from Signal s where s.object = ?1")
Optional<List<Signal>> findSignalByObjectId(Integer objectId);
}
It doens't work. If I change "?1" to 1 it gets the hardcoded value. If I try to query the "volume", it works fine.
I get this error:
Blockquote
nested exception is java.lang.IllegalArgumentException: Parameter value [1] did not match expected type
I'd recommend you omit the query and let spring data generate one for you. So your case may be represented somehow like that (in case of proper relation mapping defined):
public interface SignalRepository extends JpaRepository<Signal, Integer> {
Optional<Signal> findByObject(YourObjectType object);
}
If you provide more info e.g. your entities - you can get more help.
You can use Spring data to generate the underlying query like this :
public interface SignalRepository extends JpaRepository<Signal, Integer> {
List<Signal> findSignalByObjectId(Integer objectId);
}
or you can write the query with this return type and parameter:
public interface SignalRepository extends JpaRepository<Signal, Integer> {
#Query("select s from Signal s where s.object = :id")
List<Signal> findSignalByObjectId(#Param("id") Integer objectId);
}

JOOQ: fetch single row by primary key?

I have a primary key value and I want to fetch a record for updating, so at the moment I write:
AccountRecord account = db.selectFrom(ACCOUNT).
where(ACCOUNT.ID.eq(identity.getAccountId())).fetchSingle();
JOOQ knows about the primary keys of my tables (such that it generates onKey() methods etc.) - so I was hoping for something like:
AccountRecord account = db.fetchByKey(ACCOUNT, identity.getAccountId())
But that doesn't seem to be a thing.
Is there a more concise way of using the JOOQ API to do what I want?
You can use DSLContext.fetchSingle(Table, Condition):
AccountRecord account = db.fetchSingle(ACCOUNT, ACCOUNT.ID.eq(identity.getAccountId()));
The generated ACCOUNT reference does not have a type reference to the key type, so the syntax you suggested is not possible. You could of course extend the code generator to produce a method that takes the primary key value and produces a Condition:
class Account {
..
public Condition byKey(Long accountId) {
return ID.eq(accountId);
}
public AccountRecord fetchByKey(DSLContext ctx, Long accountId) {
return ctx.fetchSingle(this, byKey(accountId));
}
}
And now use the above:
AccountRecord account = ACCOUNT.fetchByKey(db, identity.getAccountId());

Spring Data JPA and Exists query

I'm using Spring Data JPA (with Hibernate as my JPA provider) and want to define an exists method with a HQL query attached:
public interface MyEntityRepository extends CrudRepository<MyEntity, String> {
#Query("select count(e) from MyEntity e where ...")
public boolean existsIfBlaBla(#Param("id") String id);
}
When I run this query, I get a java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.Boolean.
How does the HQL query have to look like to make this work? I know I could simply return a Long value and afterwards check in my Java code if count > 0, but that workaround shouldn't be necessary, right?
Spring Data JPA 1.11 now supports the exists projection in repository query derivation.
See documentation here.
In your case the following will work:
public interface MyEntityRepository extends CrudRepository<MyEntity, String> {
boolean existsByFoo(String foo);
}
I think you can simply change the query to return boolean as
#Query("select count(e)>0 from MyEntity e where ...")
PS:
If you are checking exists based on Primary key value CrudRepository already have exists(id) method.
in my case it didn't work like following
#Query("select count(e)>0 from MyEntity e where ...")
You can return it as boolean value with following
#Query(value = "SELECT CASE WHEN count(pl)> 0 THEN true ELSE false END FROM PostboxLabel pl ...")
It's gotten a lot easier these days!
#Repository
public interface PageRepository extends JpaRepository<Page, UUID> {
Boolean existsByName(String name); //Checks if there are any records by name
Boolean existsBy(); // Checks if there are any records whatsoever
}
Since Spring data 1.12 you can use the query by Example functionnality by extending the QueryByExampleExecutor interface (The JpaRepositoryalready extends it).
Then you can use this query (among others) :
<S extends T> boolean exists(Example<S> example);
Consider an entity MyEntity which as a property name, you want to know if an entity with that name exists, ignoring case, then the call to this method can look like this :
//The ExampleMatcher is immutable and can be static I think
ExampleMatcher NAME_MATCHER = ExampleMatcher.matching()
.withMatcher("name", GenericPropertyMatchers.ignoreCase());
Example<MyEntity> example = Example.<MyEntity>of(new MyEntity("example name"), NAME_MATCHER);
boolean exists = myEntityRepository.exists(example);
Apart from the accepted answer, I'm suggesting another alternative.
Use QueryDSL, create a predicate and use the exists() method that accepts a predicate and returns Boolean.
One advantage with QueryDSL is you can use the predicate for complicated where clauses.
You can use Case expression for returning a boolean in your select query like below.
#Query("SELECT CASE WHEN count(e) > 0 THEN true ELSE false END FROM MyEntity e where e.my_column = ?1")
Spring data provides method for checking the existence of a row using field:
example: boolean existsByEmployeeIdAndEmployeeName(String employeeId, String employeeName);
You can use .exists (return boolean) in jpaRepository.
if(commercialRuleMsisdnRepo.exists(commercialRuleMsisdn.getRuleId())!=true){
jsRespon.setStatusDescription("SUCCESS ADD TO DB");
}else{
jsRespon.setStatusCode("ID already exists is database");
}

Categories

Resources