I am developping a chat app. I have two Entities
#Entity(tableName = "messages")
public class MessageItem {
#PrimaryKey
private Integer msgId;
#ColumnInfo(name = "from_id")
private String contact_id;
}
And
#Entity(tableName = "contact")
public class Contact{
#PrimaryKey
private Integer id;
#ColumnInfo(name = "contact_phone")
private String phone;
}
In MessageDao I want to get Contact phone correponding to the contact_id in MessageItem
You have three ways you can do this.
1) You can use a POJO with an #Embedded and an #Relation in which case you return MessageItem's with the Contact e.g. :-
public class MessageItemWithContact {
#Embedded
MessageItem messageItem;
#Relation(entity = Contact.class, parentColumn = "from_id", entityColumn = "id")
Contact contact;
}
along with an #Query such as :-
#Transaction
#Query("SELECT * FROM messages WHERE msgId=:msgId")
MessageItemWithContact getMessageByIdWithContact(int msgId);
2) Or you can use a POJO with an #Embedded and an additional variable for the phone using a JOIN e.g. :-
public class MessageItemWithPhone {
#Embedded
MessageItem messageItem;
String phone;
}
Along with an #Query like :-
#Query("SELECT msgId, contact_phone, from_id FROM messages JOIN contact On from_id = id ")
List<MessageItemWithPhone> getMessItemWithContactPhone();
doesn't need #Transaction as the query is a single transaction (whilst the previous method Room obtains the MessageItem and then builds a query to get the related object(s)).
this query gets all MessageItems (as no WHERE clause has been included)
3) Just get the phone using the from_Id for the relevant MessageItem without the need for a POJO by having an #Query like:-
#Query("SELECT contact_phone FROM contact WHERE id=:from_Id")
String getPhoneById(String from_Id);
Related
There are two tables PersonEntity and cityentity. PersonEntity in the database is linked to cityentity by the external key fk_cityid. I need to select all the records (names) of the PersonEntity table with the given CityId. Join is used everywhere for this, but in this case I don't need data from the cityentity table, only the name field of the PersonEntity table. Here is a description of the classes:
#Entity
public class PersonEntity {
private Long id;
private String name;
private CityEntity cityId;
}
#Entity
public class CityEntity {
private Long id;
private String name;
}
Here is the HQL query:
#Repository
public interface PersonEntityRepository extends JpaRepository<PersonEntity, Long> {
#Query("select p.name FROM PersonEntity p where (p.name = :name or :name is null) " +
"and (p.cityId = :cityId or :cityId is null)")
List<PersonEntity> findByNameAndCity (
#Param("name") String name,
#Param("cityId") CityEntity cityId);
}
tried by id:
#Query("select p.name FROM PersonEntity p where (p.name = :name or :name is null) " +
"and (p.cityId.id = :cityId or :cityId is null)")
List<PersonEntity> findByNameAndCity (
#Param("name") String name,
#Param("cityId") Long cityId);
In both cases, the error is: "the data type could not be determined".
options for calling the function:
servisPerson.findByNameAndCity (null, cityId);
or
servisPerson.findByNameAndCity (name, null);
In fact, there are more than two parameters. I only show two for simplification.
servisPerson.findByNameAndCity (name, age, ..., cityId);
Person entity should look something like this
#Entity
public class PersonEntity {
private Long id;
private String name;
#OneToOne
#JoinColumn(name = "city_id")
private CityEntity city;
}
Than you can write your query like this
List<PersonEntity> findByNameAndCityOrNameIsNullOrCityIsNull(String name, CityEntity city);
Spring Data JPA is so smart to generate the query for you under the hood.
BTW, you attempted to write JPQL, not HQL.
EDIT (reaction on comment about long method names):
I would suggest to avoid creating JPQL query, because is is more error prone. If you don't like lengthy method name, you can wrap it into shorter default method within repository:
#Repository
public interface PersonEntityRepository extends JpaRepository<PersonEntity, Long> {
List<PersonEntity> findByNameAndCityOrNameIsNullOrCityIsNull(String name, CityEntity city);
default List<PersonEntity> shortName(String name, CityEntity city) {
return findByNameAndCityOrNameIsNullOrCityIsNull(name, city);
}
}
I am new to Android Room Database.
I saw there have similar questions on that topic, but i really didn't understand what actually worked for this topic.
So, here I am trying to get the first date from the table, but while I query with that
#Query("select min(date) from user_items where userId = :userId ")
long getFirstDate(long userId);
I got the result as a minimum date is 01/01/1970
But i want the first date from the table to show the user.
My entity class User.java:
#Entity(tableName = "users")
public class User {
#PrimaryKey(autoGenerate = true)
private int id;
private String name;
private String email;
private String password;
private String securityAnswer;
// other staff here
}
My another entity has a relation with User table.
UserItem.java:
#Entity(tableName = "user_items", indices = {#Index("userId")}, foreignKeys = #ForeignKey(entity = User.class,
parentColumns = "id",
childColumns = "userId",
onDelete = CASCADE))
public class UserItems {
#PrimaryKey(autoGenerate = true)
private int id;
private String category;
private String amount;
private String date;
#ColumnInfo(name = "userId")
private int userId;
// setter and getter
}
My other all query worked fine except min(date) function.
This is my entity, i want the first date as i marked, to show the user
Finally I got the solution:
Here I change my query
Instead of this:
#Query("select min(date) from user_items where userId = :userId ")
long getFirstDate(long userId);
Use this:
#Query("select min(date) from user_items where userId = :userId ")
String getFirstDate(long userId);
Because if you look my UserItem.java class , here i am storing data as String
so, if i want to store String to the long type, it's not allowed.
Hope it will help you.
The query is returning the start of the UNIX epoch (01/01/1970) which indicates that you might have persisted (or attempted to persist) an invalid value in the date column for one of your rows. You might want to check for any suspicious values in the date column.
Have a "full Entity" class:
#Entity(name = "vacancy_dec_to_words")
public class VacancyDescriptionToWords {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#JoinColumn(name = "vacancy_description_id")
#ManyToOne(cascade = CascadeType.ALL)
private VacancyDescription vacancyDescription;
#JoinColumn(name = "words_id")
#ManyToOne
private Words words;
#Column(name = "qty")
private int qty;
#Column(name = "create_date")
private Date date;
//...getters and setters
In some methods I need use only 2 column from this database: word_id and qty
I try the follow way:
Projections
https://docs.spring.io/spring-data/jpa/docs/2.1.2.RELEASE/reference/html/#projections
public interface QtyWords {
Long getWords();
Integer getQty();
}
JpaReposytory:
*Query, that I use tested and it workable, I use him in JpaRepository:
#Repository
public interface SmallVDTWRepository extends JpaRepository<VacancyDescriptionToWords, Long> {
#Query(nativeQuery = true,
value = "SELECT sum(qty), words_id FROM vacancy_desc_to_words WHERE vacancy_description_id IN (" +
"SELECT id FROM vacancy_description WHERE vacancy_id IN (" +
"SELECT id FROM vacancy WHERE explorer_id = :exp))" +
"GROUP BY words_id")
List<QtyWords> getDistinctWordsByExplorer(#Param("exp") long exp);
}
But I get some interesting result when I get list of entities:
List<QtyWords> list = vdtwService.getByExplorerId(72);
I am not get any exceptions, but I have the list with are unknowns objects. This objects contains my data, which I need(qty and words_id), but I cannot get them from him.
Can I use this method (Projection) to implement this task and, in general, how to correctly implement the 'Light Entity' in this case?
Spring provides two mechanisms that can be used to limit data to be fetched.
Projections
Projections can help you to reduce data, retrieved from database, by setting what exactly attributes you want to fetch.
Example:
#Entity
class Person {
#Id UUID id;
String firstname, lastname;
#OneToOne
Address address;
}
#Entity
static class Address {
#Id UUID id;
String zipCode, city, street;
}
interface NamesOnly {
String getFirstname();
String getLastname();
}
#Repository
interface PersonRepository extends Repository<Person, UUID> {
Collection<NamesOnly> findByLastname(String lastname);
}
Entity graph
Annotation EntityGraph can help you to reduce amount of queries to database, by setting what exactly related entities you need to fetch.
Example:
#Entity
#NamedEntityGraph(name = "GroupInfo.detail", attributeNodes = #NamedAttributeNode("members"))
public class GroupInfo {
#Id UUID id;
#ManyToMany //default fetch mode is lazy.
List<GroupMember> members = new ArrayList<GroupMember>();
}
#Repository
public interface GroupRepository extends CrudRepository<GroupInfo, String> {
#EntityGraph(value = "GroupInfo.detail", type = EntityGraphType.LOAD)
GroupInfo getByGroupName(String name); //Despite of GroupInfo.members has FetchType = LAZY, it will be fetched because of using EntityGraph
}
There are two types of EntityGraph:
EntityGraphType.LOAD - is used to specify an entity graph, attributes that are specified by attribute nodes of the entity graph are treated as FetchType.EAGER and attributes that are not specified are treated according to their specified or default FetchType.
EntityGraphType.FETCH - is used to specify an entity graph, attributes that are specified by attribute nodes of the entity graph are treated as FetchType.EAGER and attributes that are not specified are treated as FetchType.LAZY.
PS: Also remember that you can set lazy fetch type: #ManyToOne(fetch = FetchType.LAZY) and JPA will not fetching child entities when parent is being fetched.
I have an Entity called Student
#Entity
#Table(name = "students")
public class Student implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "STUDENT_ID")
private Integer studentId;
#Column(name = "STUDENT_NAME", nullable = false, length = 100)
private String studentName;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "student", cascade = CascadeType.ALL)
private List<Note> studentNotes;
// Some other instance variables that are not relevant to this question
/* Getters and Setters */
}
and an entity called as Note
#Entity
#Table(name = "notes")
public class Note implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "NOTE_ID")
private Integer noteId;
#Column(name = "NOTE_CONTENT")
private String noteText;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "STUDENT_ID")
private Student student;
/* Getters and Setters */
}
As you can see the relationship dictates that a Student can have multiple number of notes.
For displaying some information about the student on a particular page I need only the studentName, count of notes and all the notes.
I created a StudentDTO for that and it looks something like this:
public class StudentDTO {
private Long count;
private String name;
private List<Note> notes;
/* Getters and setters */
}
And I am using the following code to map the Student and Notes returned from the DB to the StudentDTO
private static void testDTO() {
Session session = getSessionFactory().openSession();
String queryString = "SELECT count(n) as count, s.studentName as name, s.studentNotes as notes " +
"from Student s join s.studentNotes n where s.id = 3";
Query query = session.createQuery(queryString);
List<StudentDTO> list = query.setResultTransformer(Transformers.aliasToBean(StudentDTO.class)).list();
for (StudentDTO u : list) {
System.out.println(u.getName());
System.out.println(u.getCount());
System.out.println(u.getNotes().size());
}
}
The above code fails when there are notes fetched in the query but if I remove the notes and get only name and count it works fine.
When notes is included in the query, this is the error that is fired by Hibernate:
select
count(studentnot2_.NOTE_ID) as col_0_0_,
. as col_3_0_,
studentnot3_.NOTE_ID as NOTE_ID1_2_,
studentnot3_.NOTE_CONTENT as NOTE_CON2_2_,
studentnot3_.STUDENT_ID as STUDENT_3_2_
from
students studentx0_
inner join
notes studentnot2_
on studentx0_.STUDENT_ID=studentnot2_.STUDENT_ID
inner join
notes studentnot3_
on studentx0_.STUDENT_ID=studentnot3_.STUDENT_ID
where
studentx0_.STUDENT_ID=3;
And this is the error message that I get:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'as col_3_0_, studentnot3_.NOTE_ID as NOTE_ID1_2_, studentnot3_.NOTE_CONTENT as N' at line 1
Now I can see where the query is wrong but it is generated by Hibernate, not something that I have control on. Is there something that I need to change in my queryString to acheive the result that I need.
I do not want to manually map the results to my DTO, is there a way that I can directly map my studentNotes in Student.java to notes in StudentDTO.java
Looks like this query is wrong. The better way is to get just the student. You can always get collection of notes from a student.
Session session = getSessionFactory().openSession();
String queryString = from Student s where s.studentId = 3;
Query query = session.createQuery(queryString);
Student student = query.getSingleResult();
sysout(student.getNotes().size())
Also, I never retrieved collection this way in SELECT clause; so, not sure but do you really need
join s.studentNotes
in your query? Not sure if my answer is helpful.
Your query is wrong as you would need two joins to also select the count of notes, but that's not even necessary, as you could determine the count by just using the size of the notes collection.
I created Blaze-Persistence Entity Views for exactly that use case. You essentially define DTOs for JPA entities as interfaces and apply them on a query. It supports mapping nested DTOs, collection etc., essentially everything you'd expect and on top of that, it will improve your query performance as it will generate queries fetching just the data that you actually require for the DTOs.
The entity views for your example could look like this
#EntityView(Student.class)
interface StudentDTO {
#Mapping("studentName")
String getName();
#Mapping("studentNotes")
List<NoteDTO> getNotes();
default int getCount() { return getNotes().size(); }
}
#EntityView(Note.class)
interface NoteDTO {
// attributes of Note that you need
#IdMapping Integer getId();
String getNoteText();
}
Querying could look like this
StudentDTO student = entityViewManager.find(entityManager, StudentDTO.class, studentId);
I'm trying to create a JPQL for one to many relationship, namely user and device as 1 user can have one to many devices. I want to find the whole object of the device if it is owned by the user and the device name is correct.
If it is a SQL query then I can just do the query only for the device as follow:
select * from DEVICE where USER_ID = 2 and DEVICENAME = "mypc";
where USER_ID is the foreign key in DEVICE table. But how can I do the JPQL query for the user and device table? Here are some info for User and Device class
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
public int id;
public String username;
public String firstname;
public String lastname;
public String hashPassword;
#OneToMany(mappedBy = "user")
public List<Device> devices = new ArrayList<Device>();
}
#Entity
public class Device {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
public int id;
public String deviceName;
#ManyToOne
public User user;
String deviceOS;
String deviceType;
String attributes;
boolean standard;
}
Example JPA Query:Documention
Query query = entityManager.createQuery("SELECT di FROM Device di JOIN di.user u WHERE u.id=:userId and di.deviceName=:deviceName");
query.setParameter("userId",userId );
query.setParameter("deviceName",deviceName);
List<Device> result = query.getResultList();
You can use JPQL path expressions in your query to navigate from one entity to another, or to refer to attributes of an entity.
More specifically, path expressions are useful to navigate along #ManyToOne or #OneToOnerelationships, e.g.:
SELECT d FROM Device d WHERE d.user.id = :userId AND d.deviceName = :deviceName
Here, d.user.id is a path expression.
For a general overview of JPQL, have a look at the great Wikibooks article.