Android room. error: Cannot figure out how to read this field from a cursor - android-room

the SQLite database contains three tables 1) employee 2) skills 3) departments. The idea is this - the employee table stores data such as id, name, last_name, salary. Also, an employee has data such as skill and department, but there can be several data for one employee, so I created two separate skills and departments tables and linked them using the key to the employee table where the primary key for employee is id. Now with the help of id I need to display all the information about employee including his skills which can be several and departments. I implement the whole process using the ROOM library.
Here is the request I make
#Query("SELECT employ.id ,employ.name ,employ.last_name, employ.salary, " +
"skill.skill, department.department_name FROM employ INNER JOIN skill,department " +
"ON employ.id = :id AND skill.employ_id = :id AND department.employ_id = :id ")
AllAboutEmployee getAllAboutEmployee(String id);
Here is the AllAboutEmployee class whose object accepts the result of the request
public class AllAboutEmployee {
#ColumnInfo(name = "id")
private String id;
#ColumnInfo(name = "name")
private String name;
#ColumnInfo(name = "last_name")
private String lastName;
#ColumnInfo(name = "salary")
private String salary;
#ColumnInfo(name = "department_name")
private List<String> departmentsList; // THE ERROR IS ON THIS LINE
#ColumnInfo(name = "skill")
private List<String> skillList; // THE ERROR IS ON THIS LINE
public AllAboutEmployee(String id, String name, String lastName, String salary, List<String> departmentsList, List<String> skillList) {
this.id = id;
this.name = name;
this.lastName = lastName;
this.salary = salary;
this.departmentsList = departmentsList;
this.skillList = skillList;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getSalary() {
return salary;
}
public void setSalary(String salary) {
this.salary = salary;
}
public List<String> getDepartmentsList() {
return departmentsList;
}
public void setDepartmentsList(List<String> departmentsList) {
this.departmentsList = departmentsList;
}
public List<String> getSkillList() {
return skillList;
}
public void setSkillList(List<String> skillList) {
this.skillList = skillList;
}
}
So ther are two fields int the AllAboutEmployee class with the List type, in order to put several skills and several departments there. It is in these fields that an error occurs. Thank you in advance for your help

Wow.. that's so cool.. I was coding all day and got this error too!
You have to create a TypeCoverter to store your data into your Room's Database.
In this case you have two List, that are not types recognizable by Database, so you have to create an Converter for it to store as a String, and another method to do the inverse.
Something like:
class TypeCoverter{
#TypeConverter
fun arrayListToString(arrayList: ArrayList<String>?): String? {
if (arrayList.isNullOrEmpty()) return null
val string = StringBuilder()
for (item in arrayList) {
val isNotTheLastItemInTheArrayList = (item == arrayList.last()).not()
if (isNotTheLastItemInTheArrayList) {
string.append(item).append(COMMA)
} else {
string.append(item)
}
}
return string.toString()
}
}
#TypeConverter
fun stringToArrayList(string: String?): ArrayList<String>? {
when {
string.isNullOrEmpty() -> {
return null
}
string.contains(COMMA).not() -> {
val list = ArrayList<String>()
list.add(string)
return list
}
else -> {
return string.split(COMMA.toRegex()).dropLastWhile { it.isEmpty() } as ArrayList<String>
}
}
}
That's actually in Kotlin, but you can see how it works.

Related

How could I fix this error: Cannot create TypedQuery for query with more than one return using requested result type?

I have the following class:
public class House {
#Id
#GeneratedValue
private long id;
private String title;
private String description;
private String city;
private double price;
private String phoneNumber;
}
I need to make a #GetMapping, where I have to get every attribute of a house except it's city.
I tried this:
This is in my repository:
public House findByIdButCity(long id) {
return em.createQuery("SELECT h.title, h.description, h.price FROM House h WHERE h.id = :id", House.class).setParameter("id", id).getSingleResult();
}
And this is in my controller:
#GetMapping("{id}")
public ResponseEntity<House> getAttributesButCityById(#PathVariable long id) {
House house = houseRepository.findByIdButCity(id);
if (house == null)
return ResponseEntity.notFound().build();
else
return ResponseEntity.ok(property);
}
Instead of filter the fields in the select, get all the information of a house and then map it to an object in the controller with only the information that you want to return, this way you also will decouple your database model from your API:
public House findByIdButCity(long id) {
return em.createQuery("SELECT h FROM House h WHERE h.id = :id", House.class).setParameter("id", id).getSingleResult();
}
public class HoouseDto {
private String title;
private String description;
private double price;
}
#GetMapping("{id}")
public ResponseEntity<HouseDto> getAttributesButCityById(#PathVariable long id) {
House house = houseRepository.findByIdButCity(id);
if (house == null)
return ResponseEntity.notFound().build();
else {
HouseDto houseDto = mapper.toDto(house)
return ResponseEntity.ok(houseDto);
}
}
}
You can use mapstruct to make the mapper or you can create a class with a static method to do it
Mapper example
public class HouseMapper {
public static HouseDto toDto(House house) {
HouseDto houseDto = new HouseDto();
houseDto.setTitle(house.getTitle());
houseDto.setDescription(house.getDescription());
houseDto.setPrice(house.getDescription());
return houseDto;
}
You can do more easily if you include the mapstruct with spring because you only have to define an interface and it will create the implementation mapping the attributes that with same names.
Here is an example:
https://www.baeldung.com/mapstruct
try changing your query to
SELECT h FROM House h WHERE h.id = :id

Updating table is not happening through Setters in Spring JPA

I am new to Spring boot and Spring Data JPA . So here i am trying to implement a sample project where a employee has list of workers , while adding a new worker has employee details also to indicate that he works for particular employee. I am able to update the worker table and also fetch the details perfectly . Am trying to update Employee table as well so that while fetching a particular employee i want the list of workers associated with him also to be fetched . But that is not happening , i haven't used any query so far as it seems simple updation and i thought just save and setters would help to do so .
Employee.Java
#Entity
public class Employee {
#Id
private int empId;
private String empName;
private String location;
#OneToMany
private List<Worker> workers;
public Employee(){
}
public Employee(int empId, String empName, String location) {
super();
this.empId = empId;
this.empName = empName;
this.location = location;
}
public List<Worker> getWorkers() {
return workers;
}
public void setWorkers(List<Worker> workers) {
this.workers = workers;
}
public int getEmpId() {
return empId;
}
public void setEmpId(int empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public void setWorkers(Worker worker) {
this.workers.add(worker);
}
#Override
public String toString() {
return "Employee [empId=" + empId + ", empName=" + empName + ", location=" + location + ", workers=" + workers
+ "]";
}
/*#Override
public String toString() {
return "Employee [empId=" + empId + ", empName=" + empName + ", location=" + location + "]";
}*/
Worker.Java
#Entity
public class Worker {
#Id
private int id;
private String name;
#ManyToOne
#JoinColumn(name="empId")
private Employee employee;
public Worker(int id, String name , int empId) {
super();
this.id = id;
this.name = name;
this.employee = new Employee(empId,"","");
}
public Worker() {
// TODO Auto-generated constructor stub
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
WorkerController.Java
#RestController
public class WorkerController {
#Autowired
WorkerRepository workerService;
#Autowired
EmployeeRepository employeeService;
#GetMapping("/employees/{id}/workers")
public List<Worker> getAllWorker(#PathVariable("id") int empId){
return workerService.findByEmployeeEmpId(empId);
}
#PostMapping("/employees/{id}/workers")
public String addNewEmployee(#RequestParam("name") String name ,
#RequestParam("workerId") int id , #PathVariable("id") int empId){
Worker worker = new Worker();
List<Worker> workers = new ArrayList<Worker>();
worker.setId(id);
worker.setName(name);
worker.setEmployee(new Employee(empId,"",""));
workerService.save(worker);
workers.add(worker);
employeeService.findById(empId).get().setWorkers(workers);
Employee emp = new Employee();
emp = employeeService.findById(empId).get();
return "Successfully added";
}
}
After adding worker , i retrieve the following as output
[
{
"id": 108,
"name": "vijay",
"employee": {
"empId": 99,
"empName": "darsha",
"location": "mumbai",
"workers": []
}
},
{
"id": 110,
"name": "suraj",
"employee": {
"empId": 99,
"empName": "darsha",
"location": "mumbai",
"workers": []
}
}
]
but while retrieving i could see the employee table is not updated. can someone guide me .
{
"empId": 99,
"empName": "darsha",
"location": "mumbai",
"workers": []
}
You says:
"...while fetching a particular employee i want the list of workers
associated with him also to be fetched"
then you have to write a OneToMany Relationship on employee's side. What you do is you tries to fetch for each Worker one Employee which is of course working and is there in your JSON result.
Here is a OneToMany Example on Emplyees class side:
#OneToMany(mappedBy="employee", fetch=FetchType.EAGER, cascade=CascadeType.ALL)
private List<Worker> worker = new ArrayList<>();
Note: mappedBy have to refer to the variable Emplyee employee in your Worker Class.
Please try different fetch types also.
Why you are not getting your #OneToMany attributes is due fetch type is lazy by default for all #OneToMany associations like list, set.
To overcome this problem. Change the default lazy fetch to eager fetch.
Modify your entity mapping to this.
#OneToMany(mappedBy="employee", fetch=FetchType.EAGER, cascade=CascadeType.ALL) private List<Worker> worker = new ArrayList<>();
while fetching you may get recursive mapping. To avoid this just add #JsonIgnoreProperties to your employee field in worker class.
Example:
#JsonIgnoreProperties("employee")
#ManyToOne
#JoinColumn(name="empId")
private Employee employee;

How to create a custom json object to columns value returned from my custom #Query

I have a query to My SQL database and I use Spring Boot to return it to Json format.
My problem is it only return value without key like:
[
[
"kermit",
6
]
]
I want it return like:
[
[
"name":"kermit",
"count" :6
]
]
I tried add Jackson Annotation jar file to project and use #JsonProperty in my entity model class:
#Entity
#Table(name = "act_id_membership", schema = "activiti", catalog = "")
#IdClass(ActIdMembershipEntityPK.class)
public class ActIdMembershipEntity {
#JsonProperty("name")
private String userId;
#JsonProperty("group")
private String groupId;
#Id
#Column(name = "USER_ID_")
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
#Id
#Column(name = "GROUP_ID_")
public String getGroupId() {
return groupId;
}
public void setGroupId(String groupId) {
this.groupId = groupId;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ActIdMembershipEntity that = (ActIdMembershipEntity) o;
return Objects.equals(userId, that.userId) &&
Objects.equals(groupId, that.groupId);
}
#Override
public int hashCode() {
return Objects.hash(userId, groupId);
}
}
But it still return without key. What I should do now? Please help me!
Thank you very much!
First, I'm agree with guy who commented that is not valid JSON format. You can see examples here https://json.org/example.html
Second, You need to create an object JSON which has fields needed for example:
public class UserStat es implements Serializable {
private String name;
private long count;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getCount() {
return count;
}
public void setCount(long count) {
this.count = count;
}
}
And in your custom query. Based your return looks like on this way:
#Query("SELECT u.name, count(u) FROM User u")
public List<UserStat> findUserStat() ;

How to use static constructor reference with a three parameters Java 8 Function (without make TriFunction)?

I am currently playing with Java 8 and I found a problem with Function. I would like ton know if there is a way to use function reference (name::methode) with a Function with tree parameters without declare a new functional interface (i.e. TriFunction).
I tried with currying way, but it doesn't work.
I have three classes :
Person.class
public class Person {
public enum Sex {
MALE, FEMALE
}
private String firstName;
private String lastName;
private Sex gender;
public Person(String firstName, String lastName, Sex gender) {
this.firstName = firstName;
this.lastName = lastName;
this.gender = gender;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public Sex getGender() {
return gender;
}
}
PersonFactory
public class PersonFactory {
public static Person create(String firstName, String lastName, String gender) {
// Check firstName Parameter
if(firstName == null || firstName.isEmpty()) {
throw new IllegalArgumentException("The firstName argument expect to not be null or empty");
}
// Check lastName Parameter
if(lastName == null || lastName.isEmpty()) {
throw new IllegalArgumentException("The lastName argument expect to not be null or empty");
}
// Check gender Parameter
if(gender == null || gender.isEmpty()) {
throw new IllegalArgumentException("The gender argument expect to not be null or empty");
} else {
switch(gender) {
case "M":
return new Person(firstName, lastName, Sex.MALE);
case "F":
return new Person(firstName, lastName, Sex.FEMALE);
default:
throw new IllegalArgumentException("The gender parameter is supposed to be either 'M' for male or 'F' for Female");
}
}
}
}
CsVPersonParser
public class CsvPersonParser {
public Person parseLine(String line, String separator, Function<String, Function<String, Function<String, Person>>> creator) {
String[] separedLine = line.split(separator);
String firstName = separedLine[0];
String lastName = separedLine[1];
String gender = separedLine[2];
return creator.apply(firstName).apply(lastName).apply(gender);
}
}
Here is my main class :
public class App {
public static void main(String[] args) {
final String IMAGINARY_CSV_FILE_LINE = "Jean,Dupont,M";
CsvPersonParser csvParser = new CsvPersonParser();
csvParser.parseLine("blabla", ",", PersonFactory::create);
}
}
The compilator show : The type PersonFactory does not define create(String) that is applicable here
It seems pretty logical. I have no solution. Is anyone can help me ?
I wonder why there is no way to do it simple without to create new things.
Probably a tri function is quite complex. I suggest that you use a builder to create a person.
The main reasons are, that you are not fixed on parameter ordering and you can extend your person. When you use a trifunction where all parameters are strings its often hard to say which parameter is the first/second/third. And when you want to add an address to a person it becomes more difficult to make it with generic classes like TriFunction.
My suggestion:
public interface PersonBuilder {
PersonBuilder withFirstName(String firstName);
PersonBuilder withLastName(String lastName);
PersonBuilder withGender(String gender);
Person create();
}
Concrete Implementation:
public class DefaultPersonBuilder implements PersonBuilder {
private String firstName;
private String lastName;
private String gender;
#Override
public PersonBuilder withFirstName(String firstName) {
this.firstName = firstName;
return this;
}
#Override
public PersonBuilder withLastName(String lastName) {
this.lastName = lastName;
return this;
}
#Override
public PersonBuilder withGender(String gender) {
this.gender = gender;
return this;
}
#Override
public Person create() {
// Check firstName Parameter
if (firstName == null || firstName.isEmpty()) {
throw new IllegalArgumentException("The firstName argument expect to not be null or empty");
}
[... your implementation using the fields]
}
}
Your parser method:
public Person parseLine(String line, String separator, PersonBuilder person) {
String[] separedLine = line.split(separator);
String firstName = separedLine[0];
String lastName = separedLine[1];
String gender = separedLine[2];
return person.withFirstName(firstName).withLastName(lastName).withGender(gender).create();
}
Now you can change the argument order oder add new fields to person without creating a function with 10 parameters. The parser interface is simpler now, too.
There is no way to do that what I wanted. However two other solutions is possible. Use a lambda instead of PersonFactory::create or create a new functional interface.
Here is the result :
New functional interface
#FunctionalInterface
public interface TriFunction<A, B, C, D> {
public D apply(A a, B b, C c);
}
Add a function parseLine with my new functional interface
public class CsvPersonParser {
// Currying style
public Person parseLine(String line, String separator, Function<String, Function<String, Function<String, Person>>> creator) {
String[] separedLine = line.split(separator);
String firstName = separedLine[0];
String lastName = separedLine[1];
String gender = separedLine[2];
return creator.apply(firstName).apply(lastName).apply(gender);
}
// New Functionnal interface style
public Person parseLine(String line, String separator, TriFunction<String, String, String, Person> creator) {
String[] separedLine = line.split(separator);
String firstName = separedLine[0];
String lastName = separedLine[1];
String gender = separedLine[2];
return creator.apply(firstName, lastName, gender);
}
}
My main class with solutions
public class App {
public static void main(String[] args) {
final String DATA_FILE_SEPARATOR = ",";
final String FAKE_CSV_LINE = "Jean,Dupont,M";
CsvPersonParser csvParser = new CsvPersonParser();
Person person;
// Use curryling style
person = csvParser.parseLine(FAKE_CSV_LINE, DATA_FILE_SEPARATOR, f -> l -> g -> PersonFactory.create(f, l, g));
System.out.println("Currying style : " + person.getFirstName() + " " + person.getLastName() + " " + person.getGender());
// Use new functionnal interface
person = csvParser.parseLine(FAKE_CSV_LINE, DATA_FILE_SEPARATOR, PersonFactory::create);
System.out.println("TriFunction style : " + person.getFirstName() + " " + person.getLastName() + " " + person.getGender());
// Use lambda style
person = csvParser.parseLine(FAKE_CSV_LINE, DATA_FILE_SEPARATOR, (a,b,c) -> PersonFactory.create(a, b, c));
System.out.println("Lambda style : " + person.getFirstName() + " " + person.getLastName() + " " + person.getGender());
}
}

Accessing Subclass properties in a JavaFX TableView ObservableArrayList

I am trying to access getter properties in a subclass with a TableView in JavaFX. I have the following class:
public class PersonType implements Serializable {
private static final long serialVersionUID = 1L;
Person person;
short count;
public PersonType() {
}
public PersonType(Person person, short count) {
super();
this.person = person;
this.count = count;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public short getCount() {
return count;
}
public void setCount(short count) {
this.count = count;
}
Person is like this:
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
String firstName;
String lastName;
public Person() {
}
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;
}
}
Okay - lastly we have the following:
#FXML
private TableColumn tcFirstName;
#FXML
private TableColumn tcLastName;
#FXML
private TableView tblPersonTypes;
ArrayList<PersonType> pType = new ArrayList<PersonType>();
//Can assume that pType here has say 5 entries, the point of this
//is I'm trying to get to the firstName, lastName properties of the
//PersonType in the TableView below like the following:
tcFirstName.setCellValueFactory(new PropertyValueFactory<String,String>("firstName"));
tcLastName.setCellValueFactory(new PropertyValueFactory<String,String>("lastName"));
//Populate Table with Card Records
ObservableList<PersonType> data = FXCollections.observableArrayList(pType);
tblPersonTypes.setItems(data);
And I'm unsure how with a list of PersonTypes I can tell the table columns that I want the firstName and lastName properties of the Person object contained within. I know I could create a new object, and have the "count" from PersonTypes, then the other properties of "firstName", "lastName" etc without having an object property of Person. Any help would be greatly appreciated.
-- EDIT --
Another way I thought to do this was using CellFactories - where I would pass in to the CellValueFactories the Person object, then set the CellFactory to return a String value (firstName for the first name column, etc). And it would look like this:
tcFirstName.setCellValueFactory(new PropertyValueFactory<Person,String>("person"));
tcFirstName.setCellFactory(new Callback<TableColumn<Person,String>,TableCell<Person,String>>(){
#Override
public TableCell<Person,String> call(TableColumn<Person,String> param) {
TableCell<Person,String> cell = new TableCell<Person,String>(){
#Override
public void updateItem(String item, boolean empty) {
if(item!=null){
setGraphic(new Label(item.getFirstName()));
}
}
};
return cell;
}
});
Try this:
tcFirstName.setCellValueFactory(new Callback<CellDataFeatures<PersonType, String>, ObservableValue<String>>() {
public ObservableValue<String> call(CellDataFeatures<PersonType, String> p) {
// p.getValue() returns the PersonType instance for a particular TableView row
if (p.getValue() != null && p.getValue().getPerson() != null) {
return new SimpleStringProperty(p.getValue().getPerson().getFirstName());
} else {
return new SimpleStringProperty("<No TC firstname>");
}
}
});
}

Resources