Room #Query error: Cannot find method parameters - java

in my project I have a library module and an application module using it. In both modules I have the same gradle dependencies on Android Architecture Components library:
// ViewModel and LiveData
implementation "android.arch.lifecycle:extensions:1.0.0"
implementation "android.arch.lifecycle:common-java8:1.0.0"
// Room
implementation "android.arch.persistence.room:runtime:1.0.0"
annotationProcessor "android.arch.persistence.room:compiler:1.0.0"
In my library module I have defined a User entity
#Entity(tableName = "users",
indices = {#Index(value = {"firstName", "lastName"})})
public class User {
public enum ROLE {
...
}
public enum FEEDBACK_LEVEL {
...
}
#PrimaryKey
public int id;
#TypeConverters(UserConverters.class)
ROLE role;
#TypeConverters(UserConverters.class)
FEEDBACK_LEVEL feedbackLevel;
public String firstName;
public String lastName;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public ROLE getRole() {
return role;
}
public void setRole(ROLE role) {
this.role = role;
}
public FEEDBACK_LEVEL getFeedbackLevel() {
return feedbackLevel;
}
public void setFeedbackLevel(FEEDBACK_LEVEL feedbackLevel) {
this.feedbackLevel = feedbackLevel;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
and the related DAO interface
#Dao
public interface UserDAO {
#Insert(onConflict = OnConflictStrategy.REPLACE)
void insertUser(User ... u);
#Query("select * from users where users.id = :userId")
LiveData<User> getUser(int userId);
}
In my application module I've created my database in which I'm using the entity defined in the library project
#Database(entities = {User.class}, version = 1)
public abstract class TDatabase extends RoomDatabase{
private static TDatabase sInstance;
public static TDatabase getInstance(final Context c) {
if(sInstance == null)
sInstance = Room.databaseBuilder(c, TDatabase.class, "t_db").build();
return sInstance;
}
public abstract UserDAO userDao();
}
The problem is that when I try to refer to a method parameter in a #Querystatement using its name I get the following error
Error:Each bind variable in the query must have a matching method parameter. Cannot find method parameters for :userId.
If I change the #Query from
#Query("select * from users where users.id = :userId")
LiveData<User> getUser(int userId);
to
#Query("select * from users where users.id = :arg0")
LiveData<User> getUser(int userId);
everything works fine.
Am I doing some mistakes? Why I'm getting this error?
I've googled for a solution but I found only results referring to Kotlin while I'm using Java.

This issue reported at https://issuetracker.google.com/issues/68118746 and fixed in version 1.1.0-alpha3, but only for Kotlin code since parameter names are always stored in Kotlin classes metadata.
For Java, there is only workaround with :arg0 until annotation like NamedArg will be added to Room.

I think you should use it like that:
#Query("select * from users where id = (:userId)")
LiveData<User> getUser(int userId);

This problem still pops up for me sometimes when working on db logic, but "Clean Project" usually fixes it in IntelliJ IDEA (should work just the same in Android Studio).

Related

Am I missing something if I use my entity class without #Id in Spring Data JDBC?

I am new to spring.
I just tried successfully using an entity class without #Id in Spring Data JDBC
Custom query was added in my repository for retrieving data from 2 mysql tables and returning an entity having the joined table data.
If I plan to use only custom queries, am I missing anything here?
Here's my entity class without #Id or #Entity:
public class Item
{
private long id;
private String code;
private String itemName;
private String groupName;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
}
Repository layer:
#Repository
public interface ItemRepository extends PagingAndSortingRepository<Item, Long>
{
#Query("SELECT a.id, a.code, a.name AS item_name,
b.name as group_name from item a, item_group b
WHERE a.group_id = b.id AND a.id=:id")
Item findItemById(#Param("id") Long id);
}
Service layer:
#Service
public class ItemServiceImpl implements ItemService
{
private final ItemRepository itemRepository;
public ItemServiceImpl(ItemRepository itemRepository)
{
this.itemRepository = itemRepository;
}
#Override
#Transactional(readOnly=true)
public Item findItemById(Long id)
{
return itemRepository.findItemById(id);
}
}
My updated main Configuration class in response to answer of Jens:
#SpringBootApplication
#EnableJdbcRepositories
public class SpringDataJdbcApplication extends AbstractJdbcConfiguration
{
public static void main(String[] args)
{
SpringApplication.run(SpringDataJdbcApplication.class, args);
}
#Bean
#ConfigurationProperties(prefix="spring.datasource")
public DataSource dataSource()
{
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
return dataSourceBuilder.build();
}
#Bean
NamedParameterJdbcOperations namedParameterJdbcOperations(DataSource dataSource)
{
return new NamedParameterJdbcTemplate(dataSource);
}
#Bean
PlatformTransactionManager transactionManager()
{
return new DataSourceTransactionManager(dataSource());
}
}
If you don't get any exceptions you should be fine. There shouldn't be anything in Spring Data JDBC that silently breaks when the id is not specified.
The problem is though: I don't consider it a feature that this works, but just accidental behaviour. This means it might break with any version, although replacing these methods with custom implementations based on a NamedParameterJdbcTemplate shouldn't be to hard, so the risk is limited.
The question though is: Why don't you add the #Id annotation, after all your entity does have an id. And the whole idea of a repository conceptually requires an id.
If it's working and you really don't want to use the annotations, you can do it. But I think that it's unnecessary complication. You can expect errors that would not be there if you had used the annotations and code will be harder to debug. If you are new in Spring I recommend to use annotations. But after all it depend on you how will you design your applications. For sure advantage of approach without annotations is higher control about database.

Implementing a base dao, I get error Type of the parameter must be a class annotated with #Entity or a collection/array of it

I am struggeling with creating a base DAO in Java using Room in Android.
There are several posts out there but not one solves the error I get.
This is the error I get at compile time:
error: Type of the parameter must be a class annotated with #Entity or a collection/array of it.
This is my entity / model class:
#Entity (tableName = "user")
public class User {
#PrimaryKey
#ColumnInfo (name = "user_id")
private int userId;
#ColumnInfo (name = "lastname")
private String lastName;
#ColumnInfo (name = "firstname")
private String firstName;
public User(int userId, String lastName, String firstName) {
this.userId = userId;
this.lastName = lastName;
this.firstName = firstName;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
This is my base DAO:
#Dao
public abstract class BaseDao<T> {
#Insert
public abstract long insert(T object); // error here
#Insert
public abstract long[] insertAll(List<T> object); // error here
#Update
public abstract int update(T object); // error here
#Update
public abstract int updateAll(List<T> object); // error here
#Delete
public abstract int delete(T object); // error here
#Delete
public abstract int deleteAll(List<T> object); // error here
}
This is my User DAO:
#Dao
public abstract class UserDao extends BaseDao<User> {
#Query ("select * from user")
public abstract LiveData<List<User>> getAll();
#Query("delete from user")
public abstract int deleteAll();
}
I get six compilation errors of the same type. That ist the number of functions in my base DAO. Of course generic type T is not annotated with #Entity, but how to deal with that fact?
What I tried to solve this:
Read all posts about this topic carefully over days. Most posts use Kotlin, and I believe that it works for Kotlin, but I use Java.
Tried to implement the Base DAO as an interface or abstract class
Tried to annotate / not annotate the base DAO with #Dao (as mentioned in some posts)
Tried to create a Base Entity annotated with #Entity and have my model classes extend that like so:
#Entity
public class BaseEntity { ... } // Base entity annotated with #Entity
public class User extends BaseEntity { ... } // subclass the model class from BaseEntity
public abstract class BaseDao<T extends BaseEntity> { ...} // type parameterize BaseDao with base type BaseEntity
public abstract class UserDao extends BaseDao<User> { ...} // type T should now be an descendant of BaseEntity which is an #Entity
None of this worked for me!
Some of the posts tell, that it has worked for them this way, but not for me.
I hope someone can tell me what I'm doing wrong!
Finally I found the error!
Among my model classes I have one model, that is just a DTO for a joined query result.
public class SessionAndUser {
#Embedded
private Session session;
#Relation(parentColumn = "user_id", entityColumn = "user_id")
private User user;
}
The DAO implementation looks like this:
#Dao
public abstract class SessionAndUserDao { //extends BaseDao<SessionAndUser> { <-- this caused the error
#Transaction
#Query("select * from session")
public abstract LiveData<List<SessionandUser>> getAll();
}
Because of SessionAndUser is not in the database it must not be annotated with #Entity.
The error was, that I extended the DAO from my base DAO. So the solution for me was, to just create the dao without extending the base DAO (in fact it does not need the functionality of the base DAO).
After figuring out that, I could see that every kind of tested implementations (using interfaces or abstract classes, using Java or Kotlin) worked as expected.

How to fix unsatisfied dependencies for injecting Models (javax.mvc.Models)?

I get this error while following this tutorial for mvc 1.0.
[ERROR] Caused by: org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type Models with qualifiers #Default at injection point [BackedAnnotatedField] #Inject ch.xxx.controller.UserController.models at ch.xxx.controller.UserController.models(UserController.java:0)
I tried...
putting my beans.xml into multiple places (resources/META-INF/beans.xml | webapp/WEB-INF/beans.xml)
changing the bean-discovery-mode to "all"
annotated my User.class with #Named and/or #SessionScoped (not in tutorial)
This is my User.class
#Entity
#Table(name = "users")
#NamedQuery(name = "User.findAll", query = "SELECT u FROM User u")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String email;
private String password;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Here my UserForm.class
import javax.ws.rs.FormParam;
public class UserForm {
#FormParam("id")
private String id;
#FormParam("email")
private String email;
#FormParam("password")
private String password;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
and at last my UserController.class, where the error actually happens:
#Controller
#Stateless
#Path("/user")
public class UserController {
#PersistenceContext
EntityManager entityManager;
#Inject
Models models;
#GET
#View("user/all-users.html")
public void getAllREST(){
List<User> allUsers = entityManager.createNamedQuery("User.findAll", User.class).getResultList();
models.put("users", allUsers);
}
#GET
#Path("/create")
public String getCreateRESTForm(){
return "user/create-user.html";
}
#POST
#Path("/create")
public String create(#BeanParam UserForm userForm){
User user = new User();
user.setEmail(userForm.getEmail());
user.setPassword(user.getPassword());
entityManager.persist(user);
return "redirect:/user";
}
}
I'm really lost here, since I have no idea what more to change and what the actual problem is for such a simple form. And don't mind the plain text password, it's just for testing.
I have not worked with JEE MVC yet but this seems to be CDI related issue and should not be located in the MVC framework, because it should only make use of CDI.
The reason for this exception, as already answered, is because the CDI Container does not find any injectible implementations of this interface, and therefore cannot inject it.
Take a look at the javadoc which says, that any implementation of this interface needs to be injectible and request scoped. Are you sure that the implementing bean you expect fulfills these requirements?
https://github.com/javaee/mvc-spec/blob/master/api/src/main/java/javax/mvc/Models.java
Is there a beans.xml present in the artifact the bean resides?
src/main/reosurces/META-INF/beans.xml (for JAR)
src/main/webapp/WEB-INF/beans.xml (for WAR)
Is the implementing bean annotated with #RequestScoped?
If referenced via EL, is the bean annotated with #Named?
Another thing is, do not make JPA entities CDI-Beans and make them injectible, this is a bad approach and the Models interfaces and its implementations should not require it.

How do I prepopulate my Room database?

It is my first time experimenting with Room database and I want to pre populate the database with 20 User objects. But the code I'm using adds 20 more users to the existing list everytime it runs. How do I keep it from doing that?
#Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
private static AppDatabase INSTANCE;
public abstract DataExchange dataExchange();
public static AppDatabase getAppDatabase(final Context context){
if (INSTANCE == null){
INSTANCE = Room.databaseBuilder(context,
AppDatabase.class,
"User_Database")
.addCallback(new Callback() {
#Override
public void onCreate(#NonNull final SupportSQLiteDatabase db) {
super.onCreate(db);
Executors.newSingleThreadScheduledExecutor().execute(new Runnable() {
#Override
public void run() {
getAppDatabase(context).dataExchange().insert(User.populate());
}
});
}
})
.build();
}
return INSTANCE;
}
public static void destroyInstance(){
INSTANCE = null;
}
Here's the User class:
#Entity(tableName = "user_table")
public class User{
public User(String name){
setName(name);
}
#PrimaryKey(autoGenerate = true)
private int uid;
#ColumnInfo(name = "Username")
private String name;
public String getName() {
return name;
}
public void setUid(int uid) {
this.uid = uid;
}
public void setName(String name) {
this.name = name;
}
public static User[] populate(){
User[] ar = new User[20];
for(int i=0; i<20;i++){
ar[i] = new User("USER "+(i+1));
}
return ar;
}
}
Also, where I took the code from had a getInstance(context) instead of getAppDatabase(context) inside run(). I changed it because getInstance was found in 5 libraries I didn't know about.
Here's DataExchange :
#Dao
public interface DataExchange {
#Query("SELECT * FROM user_table")
List<User> getAll();
#Query("SELECT * FROM user_table where username LIKE :name ")
User findByName(String name);
#Query("SELECT COUNT(*) from user_table")
int countUsers();
#Update
void update(User user);
#Insert
void insertAll(User... users);
#Delete
void delete(User user);
}
Step 1:
Add a primary key to your Entity, it's recommended to use an int in your scenario. For example: User with ID 1 is unique in your database.
#Entity
public class User {
#PrimaryKey
public int id;
}
https://developer.android.com/training/data-storage/room/defining-data
Step 2:
Assign a value to the id variable. I recommend assigning it through the constructor of the User class (mainly to prevent it from being 0, or to prevent you forgetting to assign it later in a setter)
Step 3:
Define a conflict strategy in your #Insert (that's in your DAO class). What do you want your database to do when it tries to add anohter User with an id = 1? Do you want to delete the older User? Just ignore it?
#Insert(onConflict = OnConflictStrategy.REPLACE)
public void insertUsers(User... users);
https://developer.android.com/reference/android/arch/persistence/room/Insert
Also, I'm 100% sure you're guessing code. Just look up the official documentation.
I recommend to just copy database which you created using DB Browser for SQLite. It is the easiest way to do prepopulating. You just create Assets folder in your app, add folder databases and after that copy your database.
In singleton class where you are creating your database just add createFromAsset() method and you have your pre-populated database. Photo is on that link.
https://i.stack.imgur.com/Q5o4v.jpg
Also in this article is described very well.
https://medium.com/androiddevelopers/packing-the-room-pre-populate-your-database-with-this-one-method-333ae190e680

Spring Data JPA Unable to locate Attribute with the given name

I was trying to use Spring Data JPA on Spring Boot and I kept getting error, I can't figure out what the problem is:
Unable to locate Attribute with the the given name [firstName] on
this ManagedType [com.example.h2demo.domain.Subscriber]
FirstName is declared in my entity class. I have used a service class with DAO before with different project and worked perfectly.
My Entity class (getters and setters are also in the class) :
#Entity
public class Subscriber {
#Id #GeneratedValue
private long id;
private String FirstName,LastName,Email;
public Subscriber(long id, String firstName, String lastName, String email) {
this.id = id;
this.FirstName = firstName;
this.LastName = lastName;
this.Email = email;
}
}
...
My Repository Class
#Component
public interface SubscriberRepository extends JpaRepository<Subscriber,Long> {
Subscriber findByFirstName(String FirstName);
Subscriber deleteAllByFirstName(String FirstName);
}
My Service Class
#Service
public class SubscriberService {
#Autowired
private SubscriberRepository subscriberRepository;
public Subscriber findByFirstName(String name){
return subscriberRepository.findByFirstName(name);
}
public Subscriber deleteAllByFirstName(String name){
return subscriberRepository.deleteAllByFirstName(name);
}
public void addSubscriber(Subscriber student) {
subscriberRepository.save(student);
}
}
And My Controller class:
#RestController
#RequestMapping("/subscribers")
public class SubscriberController {
#Autowired
private SubscriberService subscriberService;
#GetMapping(value = "/{name}")
public Subscriber findByFirstName(#PathVariable("name") String fname){
return subscriberService.findByFirstName(fname);
}
#PostMapping( value = "/add")
public String insertStudent(#RequestBody final Subscriber subscriber){
subscriberService.addSubscriber(subscriber);
return "Done";
}
}
Try changing private String FirstName,LastName,Email; to private String firstName,lastName,email;
It should work.
findByFirstName in SubscriberRepository tries to find a field firstName by convention which is not there.
Further reference on how properties inside the entities are traversed https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.query-property-expressions
The same problem was when i had deal with Spring Data Specifications (https://www.baeldung.com/rest-api-search-language-spring-data-specifications)
Initial piece of code was:
private Specification<Project> checkCriteriaByProjectNumberLike(projectNumber: String) {
(root, query, criteriaBuilder) -> criteriaBuilder.like(root.get("project_number"), "%" + projectNumber)
}
The problem was in root.get("project_number"). Inside the method, I had to put the field name as in the model (projectNumber), but I sent the field name as in the database (project_number).
That is, the final correct decision was:
private Specification<Project> checkCriteriaByProjectNumberLike(projectNumber: String) {
(root, query, criteriaBuilder) -> criteriaBuilder.like(root.get("projectNumber"), "%" + projectNumber)
}
After I change my entity class variables from capital letter to small letter for instance Username to username the method Users findByUsername(String username); is working for me now .
As per specification , the property names should start with small case.
...The resolution algorithm starts with interpreting the entire part (AddressZipCode) as the property and checks the domain class for a property with that name (uncapitalized)....
It will try to find a property with uncapitalized name. So use firstName instead of FristName and etc..

Categories

Resources