Actualizar algún campo específico de una entidad en Android Room

122

Estoy usando la biblioteca de persistencia de habitaciones de Android para mi nuevo proyecto. Quiero actualizar algún campo de la tabla. Lo he intentado como en mi Dao-

// Method 1:

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

Pero cuando trato de actualizar usando este método, actualiza todos los campos de la entidad donde coincide con el valor de la clave principal del objeto tour. He usado@Query

// Method 2:

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

Está funcionando pero habrá muchas consultas en mi caso porque tengo muchos campos en mi entidad. Quiero saber cómo puedo actualizar algún campo (no todos) como Method 1donde id = 1; (id es la clave primaria de generación automática).

// Entity:

@Entity
public class Tour {
    @PrimaryKey(autoGenerate = true)
    public long id;
    private String startAddress;
    private String endAddress;
    //constructor, getter and setter
}
Rasel
fuente
Cómo actualizar una lista en Table. De hecho, he insertado la lista en la Tabla por TypeConverter. Pero aunque viene con la actualización, no funciona. Sugiera, si ha enfrentado algún problema como este.
Aman Gupta - SHOoTeR
@ AmanGupta-ShOoTeR ¿Obtuviste alguna solución para el comentario anterior?
skygeek
Mi biblioteca Kripton Persistence Library funciona de manera bastante similar a esa biblioteca de Room. Si desea ver cómo resuelvo
xcesco
@ AmanGupta-ShOoTeR Me enfrenté a este tipo de problemas en la actualización usando '@Query'. Luego utilicé '@Insert (onConflict = OnConflictStrategy.REPLACE)' creando un objeto con el mismo valor de clave principal en lugar de actualizar y funcionó
Rasel

Respuestas:

68

Quiero saber cómo puedo actualizar algún campo (no todos) como el método 1 donde id = 1

Úselo @Query, como lo hizo en el Método 2.

es una consulta demasiado larga en mi caso porque tengo muchos campos en mi entidad

Entonces tenga entidades más pequeñas. O no actualice los campos individualmente, sino que tenga interacciones más detalladas con la base de datos.

OIA, no hay nada en la habitación en sí que haga lo que busca.

CommonsWare
fuente
¿Es posible actualizar la tabla sin pasar ningún parámetro? Me refiero a intercambiar un valor booleano.
Vishnu TB
1
@VishnuTB: Si puede crear una declaración SQL que haga lo que quiere, debería poder usarla con @Query.
CommonsWare
@CommonsWare lo tiene. Quería obtener un conjunto de filas con un valor verdadero booleano. pero Room arch no me permitió pasar verdadero / falso como parámetro. en cambio true =1,false=0 SELECT * FROM note_table WHERE isNoteArchived == 0
Vishnu TB
4
Room 2.2.0-alpha01 ( developer.android.com/jetpack/androidx/releases/… ) introdujo un nuevo parámetro para @Update que le permite establecer la entidad de destino. Esto debería posibilitar las actualizaciones parciales.
Jonas
84

Según 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);

Ejemplo:

Entidad:

@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
fuente
4
¿Puedes explicar esto más?
Michael
1
Con la entidad de la pregunta, el método DAO se verá así: @Query ("UPDATE Tour SET endAddress =: end_address, startAdress =: start_address WHERE id =: tid) int updateTour (long tid, String end_address, String start_address);
Jurij Pitulja
1
Me refiero a su solución :) Es mejor tenerlo allí para que otros usuarios lo vean
Michael
oh, pensé que era obvio. Arreglado ahora.
Jurij Pitulja
14

A partir de Room 2.2.0 lanzada en octubre de 2019, puede especificar una entidad de destino para las actualizaciones. Luego, si el parámetro de actualización es diferente, Room solo actualizará las columnas de entidad parciales. Un ejemplo de la pregunta OP lo mostrará un poco más 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;
}

Tenga en cuenta que debe crear una nueva entidad parcial llamada TourUpdate, junto con su entidad Tour real en la pregunta. Ahora, cuando llame a update con un objeto TourUpdate, actualizará endAddress y dejará el valor startAddress igual. Esto funciona perfectamente para mí para mi caso de uso de un método insertOrUpdate en mi DAO que actualiza la base de datos con nuevos valores remotos de la API pero deja los datos de la aplicación local en la tabla solos.

georgiecasey
fuente
6

Puede intentar esto, pero el rendimiento puede ser un poco peor:

@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
fuente
2

Creo que no es necesario actualizar solo un campo específico. Solo actualice los datos completos.

@Update consulta

Básicamente es una consulta determinada. No es necesario realizar ninguna consulta nueva.

@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>?
)

Lo único que necesita saber es 'id'. Por ejemplo, si desea actualizar solo el "título", puede reutilizar el "contenido" y la "foto" de los datos ya insertados. En código real, use así

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

Si necesita actualizar la información de usuario para un ID de usuario específico "x",

  1. necesita crear una clase dbManager que inicializará la base de datos en su constructor y actuará como un mediador entre su viewModel y DAO, y también.
  2. El modelo de vista va a inicializar una instancia de DBManager para acceder a la base de datos. El código debería verse así:

       @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);

    Luego, simplemente actualice su elemento de usuario de esta manera, suponga que su ID de usuario es 1122 y el nombre de usuario es "xyz", que debe cambiarse a "zyx".

    Obtener un userItem de id 1122 User object

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

Este es un código sin formato, espero que te ayude.

Codificación feliz

Kunal Parte
fuente
10
¿Realmente haces acción de bases de datos en un modelo de vista? dios no por favor no ...: '(solo ver el nombre del modelo debería golpearte en la cara
mcfly
@mcfly, ¿Por qué se considera malo realizar operaciones de base de datos en el modelo de vista?
Daniel
el principio de responsabilidad única está roto, sí
mcfly