Atualizar algum campo específico de uma entidade no Android Room

122

Estou usando a biblioteca de persistência de sala do Android para meu novo projeto. Eu quero atualizar algum campo da tabela. Eu tentei como no meu Dao-

// Method 1:

@Dao
public interface TourDao {
    @Update
    int updateTour(Tour tour);
}

Mas quando tento atualizar usando este método, ele atualiza todos os campos da entidade onde corresponde ao valor da chave primária do objeto de passeio. Eu tenho usado@Query

// Method 2:

@Query("UPDATE Tour SET endAddress = :end_address WHERE id = :tid")
int updateTour(long tid, String end_address);

Está funcionando, mas haverá muitas consultas no meu caso porque tenho muitos campos na minha entidade. Eu quero saber como posso atualizar alguns campos (não todos) como Method 1onde id = 1; (id é a chave primária gerada automaticamente).

// Entity:

@Entity
public class Tour {
    @PrimaryKey(autoGenerate = true)
    public long id;
    private String startAddress;
    private String endAddress;
    //constructor, getter and setter
}
Rasel
fonte
Como atualizar uma lista na Tabela. Na verdade, eu inseri a lista na Tabela por TypeConverter. Mas enquanto vem com a atualização, não está funcionando. Por favor, sugira, se você enfrentou algum problema como este.
Aman Gupta - ShOoTeR
@ AmanGupta-ShOoTeR Você obteve alguma solução para o comentário acima?
skygeek
Minha biblioteca Kripton Persistence Library funciona de maneira bastante semelhante à biblioteca Room. Se você quiser dar uma olhada em como eu resolvo esse problema usando o Kripton, visite abubusoft.com/wp/2019/10/02/…
xcesco
@ AmanGupta-ShOoTeR Eu enfrentei esse tipo de problema na atualização usando '@Query'. Então, usei '@Insert (onConflict = OnConflictStrategy.REPLACE)' criando um objeto com o mesmo valor de chave primária em vez de atualizar e funcionou
Rasel

Respostas:

68

Quero saber como posso atualizar alguns campos (não todos) como o método 1 onde id = 1

Use @Query, como você fez no Método 2.

é uma consulta muito longa no meu caso porque tenho muitos campos na minha entidade

Então, tenha entidades menores. Ou não atualize os campos individualmente, mas em vez disso tenha interações mais granulares com o banco de dados.

IOW, não há nada no próprio quarto que fará o que você procura.

CommonsWare
fonte
É possível atualizar a tabela sem passar nenhum parâmetro? Quero dizer trocar um valor booleano.
Vishnu TB
1
@VishnuTB: Se você pode criar uma instrução SQL que faz o que você deseja, você deve ser capaz de usá-la com @Query.
CommonsWare
@CommonsWare entendeu. Eu queria buscar um conjunto de linhas com um valor verdadeiro booleano. mas o arco da sala não me permitiu passar verdadeiro / falso como parâmetro. em vez disso true =1,false=0 SELECT * FROM note_table WHERE isNoteArchived == 0
Vishnu TB
4
O Room 2.2.0-alpha01 ( developer.android.com/jetpack/androidx/releases/… ) introduziu um novo parâmetro para @Update que permite definir a entidade de destino. Isso deve possibilitar atualizações parciais.
Jonas de
84

De acordo com o SQLite Update Docs :

<!-- language: lang-java -->
@Query("UPDATE tableName SET 
    field1 = :value1,
    field2 = :value2, 
    ...
    //some more fields to update
    ...
    field_N= :value_N
    WHERE id = :id)

int updateTour(long id, 
               Type value1, 
               Type value2, 
               ... ,
               // some more values here
               ... ,
               Type value_N);

Exemplo:

Entidade:

@Entity(tableName = "orders")
public class Order {

@NonNull
@PrimaryKey
@ColumnInfo(name = "order_id")
private int id;

@ColumnInfo(name = "order_title")
private String title;

@ColumnInfo(name = "order_amount")
private Float amount;

@ColumnInfo(name = "order_price")
private Float price;

@ColumnInfo(name = "order_desc")
private String description;

// ... methods, getters, setters
}

Dao:

@Dao
public interface OrderDao {

@Query("SELECT * FROM orders")
List<Order> getOrderList();

@Query("SELECT * FROM orders")
LiveData<List<Order>> getOrderLiveList();

@Query("SELECT * FROM orders WHERE order_id =:orderId")
LiveData<Order> getLiveOrderById(int orderId);

/**
* Updating only price
* By order id
*/
@Query("UPDATE orders SET order_price=:price WHERE order_id = :id")
void update(Float price, int id);

/**
* Updating only amount and price
* By order id
*/
@Query("UPDATE orders SET order_amount = :amount, price = :price WHERE order_id =:id")
void update(Float amount, Float price, int id);

/**
* Updating only title and description
* By order id
*/
@Query("UPDATE orders SET order_desc = :description, order_title= :title WHERE order_id =:id")
void update(String description, String title, int id);

@Update
void update(Order order);

@Delete
void delete(Order order);

@Insert(onConflict = REPLACE)
void insert(Order order);
}
Jurij Pitulja
fonte
4
Você pode explicar isso mais?
Michael
1
Com a entidade da questão, o método DAO será semelhante a este: @Query ("UPDATE Tour SET endAddress =: end_address, startAdress =: start_address WHERE id =: tid) int updateTour (tid longo, String end_address, String start_address);
Jurij Pitulja
1
Eu quis dizer na sua solução :) É melhor tê-lo lá para que outros usuários vejam
Michael
oh, eu pensei que fosse óbvio. Corrigido agora.
Jurij Pitulja
14

A partir da Room 2.2.0 lançada em outubro de 2019, você pode especificar uma entidade de destino para atualizações. Então, se o parâmetro de atualização for diferente, a Room atualizará apenas as colunas de entidade parciais. Um exemplo para a pergunta OP mostrará isso um pouco mais claramente.

@Update(entity = Tour::class)
fun update(obj: TourUpdate)

@Entity
public class TourUpdate {
    @ColumnInfo(name = "id")
    public long id;
    @ColumnInfo(name = "endAddress")
    private String endAddress;
}

Observe que você deve criar uma nova entidade parcial chamada TourUpdate, junto com sua entidade Tour real na questão. Agora, quando você chamar update com um objeto TourUpdate, ele atualizará endAddress e deixará o valor startAddress igual. Isso funciona perfeitamente para mim para meu caso de uso de um método insertOrUpdate em meu DAO que atualiza o banco de dados com novos valores remotos da API, mas deixa os dados do aplicativo local na tabela sozinho.

georgiecasey
fonte
6

Você pode tentar isso, mas o desempenho pode ser um pouco pior:

@Dao
public abstract class TourDao {

    @Query("SELECT * FROM Tour WHERE id == :id")
    public abstract Tour getTour(int id);

    @Update
    public abstract int updateTour(Tour tour);

    public void updateTour(int id, String end_address) {
        Tour tour = getTour(id);
        tour.end_address = end_address;
        updateTour(tour);
    }
}
SUPERYAO
fonte
2

Acho que você não precisa atualizar apenas alguns campos específicos. Basta atualizar os dados inteiros.

@Update query

É basicamente uma determinada consulta. Não há necessidade de fazer novas consultas.

@Dao
interface MemoDao {

    @Insert
    suspend fun insert(memo: Memo)

    @Delete
    suspend fun delete(memo: Memo)

    @Update
    suspend fun update(memo: Memo)
}

Memo.class

@Entity
data class Memo (
    @PrimaryKey(autoGenerate = true) val id: Int,
    @ColumnInfo(name = "title") val title: String?,
    @ColumnInfo(name = "content") val content: String?,
    @ColumnInfo(name = "photo") val photo: List<ByteArray>?
)

A única coisa que você precisa saber é 'id'. Por exemplo, se você deseja atualizar apenas 'título', pode reutilizar 'conteúdo' e 'foto' de dados já inseridos. Em código real, use assim

val memo = Memo(id, title, content, byteArrayList)
memoViewModel.update(memo)
J.Dragon
fonte
-3

Se você precisar atualizar as informações do usuário para um ID de usuário específico "x",

  1. você precisa criar um classe dbManager que irá inicializar o banco de dados em seu construtor e atuar como um mediador entre seu viewModel e DAO, e também.
  2. o ViewModel inicializará uma instância de dbManager para acessar o banco de dados. O código deve ser semelhante a este:

       @Entity
        class User{
        @PrimaryKey
        String userId;
        String username;
        }
    
        Interface UserDao{
        //forUpdate
        @Update
        void updateUser(User user)
        }
    
        Class DbManager{
        //AppDatabase gets the static object o roomDatabase.
        AppDatabase appDatabase;
        UserDao userDao;
        public DbManager(Application application ){
        appDatabase = AppDatabase.getInstance(application);
    
        //getUserDao is and abstract method of type UserDao declared in AppDatabase //class
        userDao = appDatabase.getUserDao();
        } 
    
        public void updateUser(User user, boolean isUpdate){
        new InsertUpdateUserAsyncTask(userDao,isUpdate).execute(user);
        }
    
    
    
        public static class InsertUpdateUserAsyncTask extends AsyncTask<User, Void, Void> {
    
    
         private UserDao userDAO;
         private boolean isInsert;
    
         public InsertUpdateBrandAsyncTask(BrandDAO userDAO, boolean isInsert) {
           this. userDAO = userDAO;
           this.isInsert = isInsert;
         }
    
         @Override
         protected Void doInBackground(User... users) {
           if (isInsert)
        userDAO.insertBrand(brandEntities[0]);
           else
        //for update
        userDAO.updateBrand(users[0]);
        //try {
        //  Thread.sleep(1000);
        //} catch (InterruptedException e) {
        //  e.printStackTrace();
        //}
           return null;
         }
          }
        }
    
         Class UserViewModel{
         DbManager dbManager;
         public UserViewModel(Application application){
         dbmanager = new DbMnager(application);
         }
    
         public void updateUser(User user, boolean isUpdate){
         dbmanager.updateUser(user,isUpdate);
         }
    
         }
    
    
    
    
    Now in your activity or fragment initialise your UserViewModel like this:
    
    UserViewModel userViewModel = ViewModelProviders.of(this).get(UserViewModel.class);

    Em seguida, basta atualizar seu item de usuário desta forma, suponha que seu userId seja 1122 e userName seja "xyz", que deve ser alterado para "zyx".

    Obtenha um userItem de id 1122 Objeto de usuário

User user = new user();
 if(user.getUserId() == 1122){
   user.setuserName("zyx");
   userViewModel.updateUser(user);
 }

Este é um código bruto, espero que ajude você.

Boa codificação

Kunal Parte
fonte
10
Você realmente faz ações de bancos de dados em um modelo de visão ??? god no plz no ...: '(basta ver o nome do modelo deve acertá-lo na cara
mcfly
@mcfly, Por que é considerado ruim fazer operações de banco de dados no View Model?
Daniel
o princípio de responsabilidade única é quebrado sim
mcfly