HQL with a collection in the WHERE clause - java

I've been trying for the this whole a query who is officially giving me nightmares. The system is a user and contact management. So I have UserAccount, Contact and Phone.
UserAccount has a bidirectional one-to-many relationship with Contact and an unidirectional one on phone all mapped by a Set:
//UserAccount mapping
#OneToMany(targetEntity=PhoneImpl.class, cascade= {CascadeType.ALL})
#org.hibernate.annotations.Cascade(value=org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
private Set<Phone> phones = new HashSet<Phone>();
#OneToMany(targetEntity=ContactImpl.class, cascade={CascadeType.ALL}, mappedBy="userAccount")
#org.hibernate.annotations.Cascade(value=org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
private Set<Contact> contacts = new HashSet<Contact>();
Contact now has a one-to-many unidirectional with phones
#OneToMany(targetEntity=PhoneImpl.class, cascade={CascadeType.ALL})
private Set<Phone> phones = new HashSet<Phone>();
I'm writing a method to check the existence of the same number for the same contact of a particular user by the email unique field.
I know I could have overridden the equals and hashcode for that but since phone in a entity mapped by set I don't know at this moment how to do that. So I wanted to provide a method to rather check for that uniqueness for me before each entry on the contact page
public boolean checkForExistingPhone(String userEmail, String formatedNumber) {
List<Contact> result = null;
Session sess = getDBSession().getSession();
String query = "select Contact ,cphones.formatedNumber from Contact c inner join Contact.phones cphones where c.UserAccount.email = :email and cphones.formatedNumber= :number";
// try {
result = (List<Contact>) sess.createQuery(query)
.setParameter("email", userEmail)
.setParameter("number", formatedNumber).list();
// } catch (HibernateException hibernateException) {
// logger.error("Error while fetching contacts of email " + userEmail + " Details:" + hibernateException.getMessage());
// }
if(result == null)
return false;
else
return true;
}
I keep on having this error:
org.hibernate.hql.ast.QuerySyntaxException: Contact is not mapped [select
cphones.formatedNumber from Contact c inner join Contact.phones cphones where
c.UserAccount.email = :email and cphones.formatedNumber= :number].
I can't really figure out what happens and first i don't know how to treat collections in HSQ.thanks for reading

HQL query would be probably something along these lines:
select c
from Contact c
join c.phones cphones
where c.userAccount.email = :email
and cphones.formatedNumber = :number
Also you may want to handle results of query like this. The list() method returns always a list, never a null.
return !result.isEmpty();

Related

Hibernate: Criteria & createSQLQuery: how to get proper JSON?

When I try this I get the proper JSON as a result, but it takes a lot of time:
Criteria c = sessionFactory.getCurrentSession().createCriteria(User.class);
List<User> users = c.list();
List<User> specialUsers = new ArrayList<>();
for (User user : users) {
List<Perm> userPerms = user.getProfile().getPerms();
for (Perm perm : userPerms) {
if (perm.getId().equals(SPECIAL_ID)) {
specialUsers.add(user);
}
}
}
return specialUsers;
and the JSON is like:
[{"id":111,"name":"Name111"},{"id":222,"name":"Name222"}]
In attempt to improve performance I tried code below. In SQL app the results are OK, a few records of users:
String sql = "SELECT u.id, u.name FROM app.user u inner join app.perms p where u.profile = p.profile AND p.right= :rightId";
List<User> specialUsers= (List<User>)sessionFactory.getCurrentSession()
.createSQLQuery(sql)
.setParameter("rightId", SPECIAL_ID)
.list();
return specialUsers;
Now the 'JSON' however looks like this:
[[111,"Name111"],[222,"Name222"]]
I tried several things, like select *, criteria.add(Restrictions...) but to no effect. What I noticed is that in the first case specialUsers.toString returns proper data, in the second case it returns meaningless Strings like Ljava.lang.Object;#23e1469f.
Any hints how to solve this?
I managed to solve this in this way, may not be perfect:
// get ids of all special users
String sql = "SELECT u.id FROM app.user u inner join app.perms p where u.profile = p.profile AND p.right= :rightId";
List<Integer> intIds = sessionFactory.getCurrentSession()
.createSQLQuery(sql)
.setParameter("rightId", SPECIAL_ID)
.list();
// convert to long values
List<Long> longIds = intIds.stream()
.mapToLong(Integer::longValue)
.boxed().collect(Collectors.toList());
// get all special users
Criteria c = sessionFactory
.getCurrentSession()
.createCriteria(User.class)
.add(Restrictions.in("id", longIds));
List<User> specialUsers = c.list();
return specialUsers;
}

Java EE, Integer to User?

I have recently asked quite similar question here, but answer does not solve my new problem.
I have two tables: User and Book, they are in ManyToOne relation. The Book table has attribute called user_id that connects both tables. Using Eclipse I generated entity classes, and "user_id" was created not as Integer like in database, but:
#JoinColumn(name="user_id")
private User user;
So now when i try to create new "Book" like this:
public String saveData() {
if(!validate()){
...
if (book != null) {
System.out.println(getUser());
setName(book.getName());
System.out.println("Post3");
setSurname(book.getSurname());
setAdress(book.getAdress());
setSize(book.getSize());
setContact(book.getContact());
setPrice(book.getPrice());
setOthers(book.getOthers());
setIsReady(book.getIsReady());
setRooms(book.getRooms());
setUser(book.getUser());
System.out.println(user);
}
private boolean validate() {
FacesContext ctx = FacesContext.getCurrentInstance();
boolean result = false;
System.out.println("validate");
if (ctx.getMessageList().isEmpty()) {
book.setName(name.trim());
book.setSurname(surname.trim());
book.setAdress(adress.trim());
book.setSize(size.trim());
book.setContact(contact.trim());
book.setPrice(price);
book.setOthers(others.trim());
book.setIsReady(isReady.trim());
book.setRooms(rooms);
book.setUser(user);
result = true;
}
return result;
}
...
bookDAO.create(book);
I'm getting
Column 'user_id' cannot be null
I am not sending 'user_id' in form but I have it stored in session BUT as integer.
So now when I am trying to force that int into the setUser I get an error that I can use only User objects there.
So my question is, are there any ways to convert Integer(which my id_user is) into the User?
You need to get User object from your database using the user_id in your session.
If you are using JPA entitymanager
User user = entityManager.find(User.class, user_id);
book.setUser(user);
You must select User by user_id from your database, or just
User user = new User();
user.setUserId(id_user);
book.setUser(user);

Returnings two results sets using JPA

I am using a JPA query to get a result set, then within the same class, I would like to conditionally get more data. Here's what it looks like:
public SchoolUser getCandidatesAsJson(#PathParam("applicationId") String applicationId, #PathParam("userPassword") String userPassword ) {
EntityManager em = createEM();
Query query = em.createQuery("SELECT su FROM SchoolUser su WHERE su.applicationId LIKE :applicationId and su.userPassword LIKE :userPassword", SchoolUser.class);
query.setParameter("applicationId", applicationId);
query.setParameter("userPassword", userPassword);
List <SchoolUser> schoolUser = query.getResultList();
if(!schoolUser.isEmpty()) {
SchoolUser loginRecord = schoolUser.get(0);
int teacherId = loginRecord.getTeacherId();
int studentId = loginRecord.getStundentId();
if(teacherId!=0){
TypedQuery<Classroom> query2 = em.createQuery("SELECT c FROM Classroom c where c.teacherId = :teacherId ORDER BY c.period", Classroom.class);
query2.setParameter("teacherId", teacherId);
List <Classroom> teacherClassList = query2.getResultList();
if(!teacherClassList.isEmpty()){
//put 2nd results set in SchoolUser object - line is commented because it causes an erro
//loginRecord.setClassRooms(teacherClassList);
}
} else if(studentId!=0){
TypedQuery<ClassroomStudent> query3 = em.createQuery("SELECT cs FROM ClassroomStudent cs where cs.statusId = 1 AND cs.studentId = :studentId", ClassroomStudent.class);
query3.setParameter("studentId", studentId);
//put results in SchoolUser object
}
return loginRecord;
} else {
SchoolUser emptyRecord = new SchoolUser();
return emptyRecord;
}
}
The error comes from putting the Classroom JPA object into the SchoolUser object - since these two objects don't have a direct relationship.
Any way that I can accomplish this with JPA?
If you do not want to persist the classroom (or any other attribute for that matter) then the #Transient annotation allows you to ignore a particular field so that JPA won't try to map it.
This annotation specifies that the property or field is not
persistent. It is used to annotate a property or field of an entity
class, mapped superclass, or embeddable class.
Example:
#Entity
public class Employee {
#Id int id;
#Transient User currentUser;
...
}

Populate List of entity class objects from sql ResultSet

So I have this sql query:
"select company, surveys.surveyID, questionID, question, questionScore FROM surveys INNER JOIN (SELECT * FROM companies INNER JOIN "
+ "(SELECT * FROM questions INNER JOIN categories "
+ "ON questions.catID = categories.catID) AS cats "
+ "ON companies.questionID = cats.questionID) AS yes "
+ "ON yes.surveyID = surveys.surveyID WHERE company=?"
'cats' and 'yes' have no meaning and are just victums of my extremely verbose naming scheme.
company is just a string.
The table it returns would look like this:
---------------------------------------------
|companyName|surveyID|questionID|questionScore|
---------------------------------------------
The primary key for this table is (companyName, surveyID AND QuestionID) because each company can have multiple surveys, each survey has a number of questions.
Then I have this class:
public class Company
{
private String companyName;
private String surveyorName;
private String surveyorTitle;
private int surveyID;
private Map<Integer,Integer> questions;
public Company(String name, String surveyor, String surveyerTitle,
int surveyID, Map<Integer, Integer> questions)
{
this.companyName = name;
this.surveyorName = surveyor;
this.surveyorTitle = surveyerTitle;
this.surveyID = surveyID;
this.questions = questions;
}
With a all the getters and setters
For example, say there is a company Mens Insamen. SurveyID is 1.
So
Mens Insamen 1 q1 3
Mens Insamen 1 q2 1
etc...
Now I need to populate a List of Company objects with the data from the sql query. I have tried for a while but couldn't really come up with anything.
If it is (unsurprisingly) unclear (fokol (no) coffee) I can improve it at some point.
Thanks in advance
create class CompanyMapper have a method inside it, pass that method the SQL ResultSet as input, loop thru it and create a list of Company and return it.
class CompanyMapper{
public List<Company> getCompanies(ResultSet rs){
ArrayList<Company> compList = new ArrayList<Company>();
while(...rs has entry...){
//Add new Company to the list
}
return compList;
}
It's really difficult to tell, what you have so far from the question, but if you want to populate a list of your custom objects, then you need to do something like this:
public List<Company> getCompanies() throws SQLException
{
List<Company> result = new ArrayList<Company>();
Connection connection = DriverManager.getConnection(...);
Statement statement = connection.createStatement();
ResultSet set = statement.executeQuery("your query here");
set.beforeFirst();
while(set.next())
{
String textData = set.getString("columnname");
int integerData = set.getInt("columnname");
// you can also use set.getString(columnindex);
Company company = new Company();
company.set(...);
result.add(company);
}
connection.close();
return result;
}
You can achive this simply using arraylist and your while loop
Create your arraylist for compines just outside the loop and add each Company object to this array list using the loop
ArrayList<Company> compList = new ArrayList<Company>();
while{
/*read values to variables and create an object of Company
then add to arraylist as follows **/
Company comp = new Company (name, surveyor, surveyerTitle,surveyID,questions);
compList.add(comp);
}
1- You should create a connection to the database using,
Class.forName(DB_LIBRARY);
connection = DriverManager.getConnection(CONNECTION_PARAMETRS);
Note that you need to replace DB_LIBRARY with the valid driver's library, and CONNECTION_PARAMETERS with your database connection setting (eg. host name, user name, password, ..etc).
2- create a statement, and return a result set
Statement statement = connection.createStatement();
ResultSet rs = statement.executeQuery(your_select_query);
3- loop through the result set and fetch data
Company company;
while(rs.next()){
company = new Company(rs.getString(1),
rs.getString(2),
rs.getString(3),
rs.getInt(4),
new HashMap<Integer, Integer>().put(rs.getInt(5), rs.getInt(6)
}
Note that you can use columns name or their number, also you may use an ArrayList if you want to fetch more than one record, as following
ArrayList<Company> companies = new ArrayList<Company>();
while(rs.next()){
companies.add(new Company(rs.getString(1),
rs.getString(2),
rs.getString(3),
rs.getInt(4),
new HashMap<Integer, Integer>().put(rs.getInt(5), rs.getInt(6));
}

Query in a transaction with an ancestor filter in Google App Engine (Java)

The Java documentation says that an app can perform a query during a transaction, but only if it includes an ancestor filter, but there is no documentation for how to do it. Can anyone provide some example code that shows how to do this in the most concise way possible?
I struggled with this one for a while, but the following will work! The short answer is that JDO used with App Engine can definitely support Ancestor Queries, so you don't have to resort to the low level API unique to App Engine, especially in the case where you want to stick with JDO throughout your application.
Step 1:
In the child class make sure to have the following instance variable. The actual name of the variable does not matter. What is important is annotation.
#Persistent
#Extension(vendorName="datanucleus", key="gae.parent-pk", value="true")
private String mParentEncKey;
Step 2:
When querying for the child object(s) by the parent key, you want to then filter on the parentKey via the name of the instance variable you created... in this case This example is for if you want to query a set of child objects that have a property, given that you know the parent key and that property.
public static List<ChildObject> queryYourChildObjects(String parentKey, String someProperty) {
PersistenceManager pm = PMF.get().getPersistenceManager();
Query q = pm.newQuery(ChildObject.class);
q.setFilter("mParentEncKey == parentKeyParam && property == propertyParam");
q.declareParameters("String parentKeyParam, String propertyParam");
List<ChildObject> results = (List<ChildObject>) q.execute(parentKey, someProperty);
return results;
}
That's it! Hopefully that helps.
Try this approach:
// PersistenceManager pm = ...;
Transaction tx = pm.currentTransaction();
User user = userService.currentUser();
List<Account> accounts = new ArrayList<Account>();
try {
tx.begin();
Query query = pm.newQuery("select from Customer " +
"where user == userParam " +
"parameters User userParam");
List<Customer> customers = (List<Customer>)
query.execute(user);
query = pm.newQuery("select from Account " +
"where parent-pk == keyParam " +
"parameters Key keyParam");
for (Customer customer : customers) {
accounts.addAll((List<Account>)
query.execute(customer.key));
}
} finally {
if (tx.isActive()) {
tx.rollback();
}
}
More information is available here:
http://code.google.com/appengine/docs/java/datastore/transactions.html#Uses_For_Transactions
If you are using the low level datastore, it is easy, as "ancestor" is a datastore concept and not a JDO/JPA concept AFAIK.
here is a link to the javadoc showing the Query constructor that takes an ancestor key

Categories

Resources