英文:
Android Room Subquery unmatched field error
问题
我无法弄清如何将子查询选择绑定到实体的属性中,我只需将“main_game”子查询绑定到实体的mainGame String属性中。
我的DAO:
@Dao
public interface GamesDao {
@Query("SELECT *, (SELECT g_nested._id FROM games as g_nested, game_groups as gg WHERE g_nested._id = gg._game_group AND g_nested.is_main = 1 AND gg._game = g._id) as main_game FROM games as g")
List<GameEntity> getAll();
}
我的GameEntity:
@Entity(tableName = "games")
public class GameEntity extends GenericIndexEntity {
@NonNull
@ColumnInfo(name = "gen")
private Integer gen;
@NonNull
@ColumnInfo(name = "is_main")
private Integer isMain;
@Ignore
@ColumnInfo(name = "main_game")
private String mainGame;
public GameEntity(@NonNull String indexNumber, String name, String nameEs, String nameIt, String nameFr, String nameDe, String namePt, String nameRu, @NonNull Integer gen, @NonNull Integer isMain, String mainGame) {
super(indexNumber, name, nameEs, nameIt, nameFr, nameDe, namePt, nameRu);
this.gen = gen;
this.isMain = isMain;
this.mainGame = mainGame;
}
@NonNull
public Integer getGen() {
return gen;
}
public void setGen(@NonNull Integer gen) {
this.gen = gen;
}
@NonNull
public Integer getIsMain() {
return isMain;
}
public void setIsMain(@NonNull Integer isMain) {
this.isMain = isMain;
}
public String getMainGame() {
return mainGame;
}
public void setMainGame(String mainGame) {
this.mainGame = mainGame;
}
}
我尝试过使用@Ignore、@ColumnInfo的多种组合,删除它们,但它似乎无法绑定值。
@Ignore
@ColumnInfo(name = "main_game")
private String mainGame;
这种方式会抛出Tried the following constructors but they failed to match: ... param:mainGame -> matched field:unmatched]
。
如果我从属性中移除所有标签,查询可以工作,但不会填充值。
我有什么遗漏吗?
英文:
I can't figure out how to bind a subquery select within an attribute of the entity, I simply need to bind the "main_game" subquery into the mainGame String attribute of my entity:
My DAO:
@Dao
public interface GamesDao {
@Query("SELECT *, (SELECT g_nested._id FROM games as g_nested, game_groups as gg WHERE g_nested._id = gg._game_group AND g_nested.is_main = 1 AND gg._game = g._id) as main_game FROM games as g")
List<GameEntity> getAll();
}
My GameEntity:
@Entity(tableName = "games")
public class GameEntity extends GenericIndexEntity {
@NonNull
@ColumnInfo(name = "gen")
private Integer gen;
@NonNull
@ColumnInfo(name = "is_main")
private Integer isMain;
@Ignore
@ColumnInfo(name = "main_game")
private String mainGame;
public GameEntity(@NonNull String indexNumber, String name, String nameEs, String nameIt, String nameFr, String nameDe, String namePt, String nameRu, @NonNull Integer gen, @NonNull Integer isMain, String mainGame) {
super(indexNumber, name, nameEs, nameIt, nameFr, nameDe, namePt, nameRu);
this.gen = gen;
this.isMain = isMain;
this.mainGame = mainGame;
}
@NonNull
public Integer getGen() {
return gen;
}
public void setGen(@NonNull Integer gen) {
this.gen = gen;
}
@NonNull
public Integer getIsMain() {
return isMain;
}
public void setIsMain(@NonNull Integer isMain) {
this.isMain = isMain;
}
public String getMainGame() {
return mainGame;
}
public void setMainGame(String mainGame) {
this.mainGame = mainGame;
}
}
I've tried using multiple combinations of @Ignore, @ColumnInfom removing them but it simply doesn't bind the value:
@Ignore
@ColumnInfo(name = "main_game")
private String mainGame;
This way is throwing Tried the following constructors but they failed to match: ... param:mainGame -> matched field:unmatched]
If I remove all the tags from the attribute the query works, but doesn't populate the value.
Am I missing something?
答案1
得分: 1
以下是您请求的翻译内容:
如果您在成员变量上使用@Ignore
注解,那么Room会将该成员变量视为一列。如果您不使用@Ignore
注解,该成员变量将成为表中的一列。
假设您不希望将成员变量作为表中的一列,但有时需要检索并分配一个值,那么您需要使用一个POJO,该POJO具有一个未被@Ignored
注解的成员变量,也许在您的情况下是额外的成员变量(由于扩展类的复杂性)。
另一种方法是硬编码所有成员变量,例如根据类似问题 https://stackoverflow.com/questions/75860588/android-room-additional-constructor-parameter/75861355#comment133938832_75861355 中的回答所述。
由于表的复杂性,即GameEntity扩展了GenericIndexEntity,可能还扩展了其他类,因此硬编码所有涉及的列可能会变成一场噩梦。
因此,您可能希望在POJO中使用@Embedded
注解来简化操作。但这样做会包括@Ignore
注解的main_game列。因此,您需要使用另一个成员变量。
例如:
public class GameWithMainGamePOJO {
@Embedded
GameEntity gameEntity;
private String mg;
public void setGameEntity(GameEntity gameEntity) {
this.gameEntity = gameEntity;
}
public String getMg() {
return mg;
}
public void setMg(String mainGame) {
this.mg = mainGame;
}
/* 这可能是您想要的 */
public GameEntity getGameEntity() {
gameEntity.setMainGame(this.mg);
return gameEntity;
}
}
在这种情况下,您需要输出一个名为mg的列,然后可能使用getGameEntity
方法,该方法应返回一个已设置提取值的GameEntity。
演示
以下是基于可用代码的演示,其中还包含其他代码,以及一个查询,用于获取GameEntity列(包括从扩展的GenericIndexEntity继承的列)。
为演示此操作,使用了一个简化的查询,@Dao
注解的接口如下:
@Dao
public interface GamesDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
long insert(GameEntity gameEntity);
@Query("SELECT *, (SELECT 'MG1'||g.gen) as mg FROM games as g")
List<GameWithMainGamePOJO> getAll();
}
完整的演示代码如下(不包括上面的GamesDao接口):
GenericIndexEntity(自拼凑的)
class GenericIndexEntity {
private String indexNumber;
private String name;
private String nameEs;
// ...
}
**GameEntity**(已调整以克服一些未知/猜测):
```java
@Entity(tableName = "games")
public class GameEntity extends GenericIndexEntity {
@PrimaryKey
@ColumnInfo(name = "gen")
private Integer gen;
@ColumnInfo(name = "is_main")
private Integer isMain;
@Ignore
@ColumnInfo(name = "main_game")
private String mainGame;
// ...
}
GameWithMainGamePOJO(如上)
TheDatabase(@Database
注解的抽象类):
@Database(entities = {GameEntity.class}, exportSchema = false, version = 1)
abstract class TheDatabase extends RoomDatabase {
abstract GamesDao getGamesDao();
// ...
}
MainActivity(用于演示原理):
public class MainActivity extends AppCompatActivity {
TheDatabase db;
GamesDao dao;
// ...
}
结果 输出到日志中:
D/DBINFO: IXN = ix1 NAME = G1 NAMEDE = G1DE NAMEES = G1ES NAMEFR = G1FR NAMEIT = G1IT ETC .... MGFROMGE = MG11 MGFROMPOJO = MG11
D/DBINFO: IXN = ix2 NAME = G2 NAMEDE = G2DE NAMEES = G2ES NAMEFR = G2FR NAMEIT = G2IT ETC .... MGFROMGE = MG12 MGFROMPOJO = MG12
可以看到MG11/MG12已动态生成并应用,同时getGameEntity
方法返回了预期的GameEntity,mainGame也正确设置。
英文:
If you @Ignore
a member variable then Room discounts considering that member variable as a column. If you do not @Ignore
the member variable it WILL be a column in the table.
Assuming that you do not want the member variable to be a column in the table BUT at some time you want to retrieve and assign a value then you will need to use a POJO that either has a member variable that is not @Ignored
perhaps in your case an extra member variable (due to the complexity of extended classes).
- The alternative is to hard code all the member variables e.g. as per this answer to a similar issue https://stackoverflow.com/questions/75860588/android-room-additional-constructor-parameter/75861355#comment133938832_75861355
Due to the apparent complexity of the table i.e. GameEntity extends GenericIndexEntity and possibly other classes are extended, it would be possibly be a nightmare to hard code all the involved columns.
Thus you may well want to use the @Embedded
annotation in the POJO to make life easy. However, doing so will include the @Ignore
d main_game column. So you would need to use another member variable.
So something like:-
public class GameWithMainGamePOJO {
@Embedded
GameEntity gameEntity;
private String mg;
public void setGameEntity(GameEntity gameEntity) {
this.gameEntity = gameEntity;
}
public String getMg() {
return mg;
}
public void setMg(String mainGame) {
this.mg = mainGame;
}
/* Perhaps what you want */
public GameEntity getGameEntity() {
gameEntity.setMainGame(this.mg);
return gameEntity;
}
}
In which case you would want to output a column named mg and then perhaps use the getGameEntity method which should return a GameEntity with the main_game set with the extracted value.
Demo
Here's a demo based upon the available code, with other code made up and importantly a query that a) gets the GameEntity columns (including the columns from the extended GenericIndexEntity *(my cobbled together version)).
To demo this a simplified query was used, The @Dao
annotated interface used being:-
@Dao
public interface GamesDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
long insert(GameEntity gameEntity);
@Query("SELECT *, (SELECT 'MG1'||g.gen) as mg FROM games as g")
List<GameWithMainGamePOJO> getAll();
}
The full, often cobbled together, code used for the demo (less the GamesDao interface as above):-
GenericIndexEntity (cobbled together)
class GenericIndexEntity {
private String indexNumber;
private String name;
private String nameEs;
private String nameIt;
private String nameFr;
private String nameDe;
private String namePt;
private String nameRu;
GenericIndexEntity(
String indexNumber, String name, String nameEs, String nameIt, String nameFr, String nameDe, String namePt, String nameRu
) {
this.indexNumber = indexNumber;
this.name = name;
this.nameEs = nameEs;
this.nameIt = nameIt;
this.nameFr = nameFr;
this.nameDe = nameDe;
this.namePt = namePt;
this.nameRu = nameRu;
}
public String getIndexNumber() {
return indexNumber;
}
public String getName() {
return name;
}
public String getNameDe() {
return nameDe;
}
public String getNameEs() {
return nameEs;
}
public String getNameFr() {
return nameFr;
}
public String getNameIt() {
return nameIt;
}
public String getNamePt() {
return namePt;
}
public String getNameRu() {
return nameRu;
}
public void setIndexNumber(String indexNumber) {
this.indexNumber = indexNumber;
}
public void setName(String name) {
this.name = name;
}
public void setNameDe(String nameDe) {
this.nameDe = nameDe;
}
public void setNameEs(String nameEs) {
this.nameEs = nameEs;
}
public void setNameFr(String nameFr) {
this.nameFr = nameFr;
}
public void setNameIt(String nameIt) {
this.nameIt = nameIt;
}
public void setNamePt(String namePt) {
this.namePt = namePt;
}
public void setNameRu(String nameRu) {
this.nameRu = nameRu;
}
}
GameEntity (adjusted to overcome some unknowns/guesses):-
@Entity(tableName = "games")
public class GameEntity extends GenericIndexEntity {
@NonNull
@PrimaryKey
@ColumnInfo(name = "gen")
private Integer gen;
@NonNull
@ColumnInfo(name = "is_main")
private Integer isMain;
@Ignore
@ColumnInfo(name = "main_game")
private String mainGame;
public GameEntity(@NonNull String indexNumber, String name, String nameEs, String nameIt, String nameFr, String nameDe, String namePt, String nameRu){
super(indexNumber,name,nameEs,nameIt,nameFr,nameDe,namePt,nameRu);
}
public GameEntity(@NonNull String indexNumber, String name, String nameEs, String nameIt, String nameFr, String nameDe, String namePt, String nameRu, @NonNull Integer gen, @NonNull Integer isMain, String mainGame) {
super(indexNumber, name, nameEs, nameIt, nameFr, nameDe, namePt, nameRu);
this.gen = gen;
this.isMain = isMain;
this.mainGame = mainGame;
}
@NonNull
public Integer getGen() {
return gen;
}
public void setGen(@NonNull Integer gen) {
this.gen = gen;
}
@NonNull
public Integer getIsMain() {
return isMain;
}
public void setIsMain(@NonNull Integer isMain) {
this.isMain = isMain;
}
public String getMainGame() {
return mainGame;
}
public void setMainGame(String mainGame) {
this.mainGame = mainGame;
}
}
GameWithMainGamePOJO (as above)
TheDatabase the @Database
annotated abstract class:-
@Database(entities = {GameEntity.class},exportSchema = false,version = 1)
abstract class TheDatabase extends RoomDatabase {
abstract GamesDao getGamesDao();
private static volatile TheDatabase instance;
public static TheDatabase getInstance(Context context) {
if (instance==null) {
instance= Room.databaseBuilder(context,TheDatabase.class,"the_games_database.db")
.allowMainThreadQueries() /* for brevity of the demo */
.build();
}
return instance;
}
}
MainActivity to demonstrate the principle:-
public class MainActivity extends AppCompatActivity {
TheDatabase db;
GamesDao dao;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = TheDatabase.getInstance(this);
dao = db.getGamesDao();
long g1Id = dao.insert(
new GameEntity("ix1","G1","G1ES","G1IT","G1FR","G1DE","G1PT","G1RU",1,2,"G1MG"));
GameEntity ge2 = new GameEntity("ix2","G2","G2ES","G2IT","G2FR","G2DE","G2PT","G2RU");
ge2.setMainGame("GM2MG");
ge2.setIsMain(0);
ge2.setGen(2);
long g2id = dao.insert(ge2);
List<GameWithMainGamePOJO> debug = dao.getAll();
for (GameWithMainGamePOJO gwmgpojo: dao.getAll()) {
Log.d(
"DBINFO","IXN = " + gwmgpojo.gameEntity.getIndexNumber()
+ "NAME = " + gwmgpojo.gameEntity.getName()
+ "NAMEDE = " + gwmgpojo.gameEntity.getNameDe()
+ "NAMEES = " + gwmgpojo.gameEntity.getNameEs()
+ "NAMEFR = " + gwmgpojo.gameEntity.getNameFr()
+ "NAMEIT = " + gwmgpojo.gameEntity.getNameIt()
+ " ETC .... "
+ "MGFROMGE = " + (gwmgpojo.getGameEntity()).getMainGame()
+ "MGFROMPOJO = " + gwmgpojo.getMg()
);
}
}
}
The RESULT output to the log:-
D/DBINFO: IXN = ix1NAME = G1NAMEDE = G1DENAMEES = G1ESNAMEFR = G1FRNAMEIT = G1IT ETC .... MGFROMGE = MG11MGFROMPOJO = MG11
D/DBINFO: IXN = ix2NAME = G2NAMEDE = G2DENAMEES = G2ESNAMEFR = G2FRNAMEIT = G2IT ETC .... MGFROMGE = MG12MGFROMPOJO = MG12
As can be seen MG11/MG12 has been dynamically generated and applied and also that the getGameEntity method returned the expected HameEntity with mainGame set correctly.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论