Dyamic table name for JPQL / Hibernate query - java

I've a database with many thousands of tables that have been (and continue to be) created with a naming strategy - one table per calendar day:
data_2010_01_01
data_2010_01_02
...
data_2020_01_01
All tables contain sensor data from the same system in the same shape. So a single entity (lets call it SensorRecord) will absolutely map to all tables.
I'd imagined something like this would work:
#Query(nativeQuery = true, value = "SELECT * FROM \"?1\"")
Collection<SensorRecord> findSensorDataForDate(String tableName);
But it does not, and reading around the topic seems to suggest I am on the wrong path. Most posts on dynamic naming seem to state explicitly that you need one entity per table, but generating thousands of duplicate entities also seems wrong.
How can I use JPA (JPQL?) to work with this data where the table name follows a naming convention and can be changed as part of the query?

Parameters are only allowed in the where clause.
You can create custom repository method returns collection of SensorRecord dto. No need to map so many entities. You should get List<Object []> as query result and manually create dto objects.
#Autowired
EntityManager entityManager;
public List<SensorRecord> findSensorDataForDate(LocalDate date) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy_MM_dd");
String tableName = "data_" + date.format(formatter);
Query query = entityManager.createNativeQuery(
"select t.first_column, t.second_column from " + tableName + " t");
List<Object[]> queryResults = query.getResultList();
List<SensorRecord> sensorRecords = new ArrayList<>();
for (Object[] row : queryResults) {
SensorRecord record = new SensorRecord();
record.setFirstParameter((Integer) row[0]);
record.setSecondParameter((String) row[1]);
sensorRecords.add(record);
}
return sensorRecords;
}

Could it be just syntax error?
This has worked for me:
#Query(value = "select * from job where job.locked = 1 and job.user = ?1", nativeQuery = true)
public List<JobDAO> getJobsForUser(#Param("user") String user);

Related

Panache Query with distinct returns PanacheQuery<Entity> and not ArrayList<String>

I'm trying to get a distinct result of one Column from my Database with Panache+Hibernate. Normally in Hibernate you would get an ArrayList<String> back from the query.
List<String> list = repo
.find("select DISTINCT(a.country) from TMdBrandAll a order by a.country")
.page(Page.ofSize(1000)).list();
But if I try this Approach with Panache I get the ErrorMessage
Comiler Error Message
If I change the variable "list" to the returnType List<TMdBrandAll> the compile error is gone.
List<TMdBrandAll> list = brandAllRepo
.find("select DISTINCT(a.country) from TMdBrandAll a order by a.country")
.page(Page.ofSize(1000)).list();
When I now inspect the executed code in the debugger I get.
Debugger output
How can I tell Panache that the outcome of the query will be an ArrayList<Strings> and not an ArrayList<PanacheEntity>?
Thanks for your answers
EDIT:
Code of Repo:
#RequestScoped
#Transactional
public class BrandAllRepo implements PanacheRepositoryBase<TMdBrandAll, Long> {
public void updateBrand(String brandName, String soundexCode, String countryCode, Long active, Long isNew, String user, Long brandAllId) {
update("set brandName = ?1, soundexCode = soundex(pkg_util.f_escape_special_char1(?2))," +
" countryCode = ?3, active = ?4, isNew = ?5, modifiedAt = sysdate, modified_by = ?6 where brandAllId = ?7",
brandName, soundexCode, countryCode, active, isNew, user, brandAllId);
}
}
Working Code from Repo:
#Inject
EntityManager em;
public List<String> findCountries() {
List<String> qres = em
.createQuery("select DISTINCT(a.countryCode) from TMdBrandAll a order by a.countryCode", String.class)
.getResultList();
return new ArrayList<>(qres);
}
With injected EntityManager and standard hibernate query it works.
This is a limitation of Panache.
Have a look at the code https://github.com/quarkusio/quarkus/blob/master/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/PanacheRepositoryBase.java
It always returns a List of the entity.
Either create a finder method in BrandAllRepo that returns a List of Strings or use an untyped list:
List list = brandAllRepo
.find("select DISTINCT(a.country) from TMdBrandAll a order by a.country")
.page(Page.ofSize(1000)).list();
You know that there will be Strings in the list.
The second option is not so nice. I would use the first option.
I was running in the same problem, but I figured that it works if I use .project(String.class) and GROUP BY instead of DISTINCT:
List<String> list = repo
.find("select a.country from TMdBrandAll a "
+ "group by a.country "
+ "order by a.country")
.project(String.class)
.page(Page.ofSize(1000)).list();
It seems that Panache gets confused by the DISTINCT clause.

JPA: Using criteriabuilder to find entities: Attribute name different from annotation?

I have a mysql database with employee information, each employee has a technical id as primary key. In MySQL to selcet row(s) matching criteria, i can just use to get the following statement (works)
SELECT * FROM database_test.employee WHERE fist_name='First1';
In Java i can also use this as a native statement to get what i want (works):
List<EmployeeEntity2> objects = m_em.createNativeQuery(
"SELECT * database_test.employee WHERE first_name='First1'",
EmployeeEntity2.class).getResultList();
However, i wanted to use the Criteriabuilder to get the same result and later generalize it for multiple columnName=columnEntry selections.
public List<EmployeeEntity2> testNoParameter() {
//Based on https://www.objectdb.com/java/jpa/query/criteria
CriteriaBuilder cb = m_em.getCriteriaBuilder();
CriteriaQuery<EmployeeEntity2> q = cb.createQuery(EmployeeEntity2.class);
Root<EmployeeEntity2> c = q.from(EmployeeEntity2.class);
ParameterExpression<String> p = cb.parameter(String.class);
//Works
//q.select(c).where(cb.equal(c.get("firstName"), p));
//Won't work
q.select(c).where(cb.equal(c.get("first_name"), p));
TypedQuery<EmployeeEntity2> query = m_em.createQuery(q);
query.setParameter(p, "First1");
List<EmployeeEntity2> results = query.getResultList();
return results;
}
Using "fist_name" - the column name annotation from the Entity - will yield the following java.lang.IllegalArgumentException with:
Unable to locate Attribute with the the given name [first_name] on this ManagedType [xx.xxx.database.EmployeeEntity2]
EmployeeEntity2 has "fist_name" annotation:
#Column(name = "first_name", nullable = false)
#Override
public String getFirstName() {
return super.getFirstName();
}
So "first_name" should exist, however (with some debugging) i found out that the attribute expected is for some reason "firstName" instead - which i have not defined/annotated - so where does it come from - and how can i use the column names actually defined in the database (column = "first_name")?
You should use property name of entity (not column name) to use it in criteria builder so instead of
q.select(c).where(cb.equal(c.get("first_name"), p));
use
q.select(c).where(cb.equal(c.get("firstName"), p));
CriteriaBuilder is RDBMS schema agnostic, so you use your model (entities), not schema (table names etc).
In JPA you dont normally use SQL but JPQL. Equivalent of your SQL in JPQL would be something like
"SELECT e FROM EmployeEntity2 e WHERE e.firstName='First1'"
Both CriteriaQuery tree and JPQL string are transformed down to the same query tree later on (can't remember the name), so they both must comply to the very same rules.

Query not giving solution in DAO class

when i run my query in database visualizer its working perfectly, but i think there are some issues in syntax when i convert it in my DAO class method.
I want to get whole data against the name provided
In Visualizer:
SELECT first_name,last_name,nic,phone,email FROM x_hr_user where (first_name = 'Irum');
Now in Dao
public List<XHrUser> findXHrUserByNameInTable()
{
String name ="Irum";
Query query = em.createQuery("SELECT xHrNewUserObj.firstName,xHrNewUserObj.lastName, xHrNewUserObj.nic, xHrNewUserObj.phone, xHrNewUserObj.emil FROM XHrUser xHrNewUserObj where (xHrNewUserObj.firstName) = (name)");
List<XHrUser> list = query.getResultList();
return list;
}
Instead of showing single row, it displays whole data Table
Thank you
Your current query is not valid JPQL. It appears that you intended to insert the raw name string into your query, which could be done via a native query, but certainly is not desirable. Instead, use a named parameter in your JPQL query and then bind name to it.
String name = "Irum";
Query query = em.createQuery("SELECT x FROM XHrUser WHERE x.firstName = :name")
.setParameter("name", name);
List<XhrUser> list = query.getResultList();
You have to write query as below. where : is used for variable
Query query = em.createQuery("SELECT xHrNewUserObj.firstName,xHrNewUserObj.lastName, xHrNewUserObj.nic, xHrNewUserObj.phone, xHrNewUserObj.emil FROM XHrUser xHrNewUserObj where (xHrNewUserObj.firstName) = :name");

How to query a database using Hibernate?

I understand some might simply answer this question with "Why didn't you just Google it"... But I did, and the more I researched this the more confused I got. I'm trying to query my database with Hibernate, the query has a 'where' clause.
Now creating a database entry is easy enough, in the case where I have a 'User' class, I simply do this:
// Gets a new session
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
// Creates a new User object
User user = new User("John", "p#55w0rd*", "john#doe.com");
// Save and commit
session.save(user);
session.getTransaction().commit();
But what do I do when I what to for instance
select * from Users where id = '3';
My Google searches pointed to something called HQL, which makes me wonder why I couldn't of just used straight JDBC then. Also it doesn't seem very object oriented. And then there's something like
session.createCriteria(.......
But I'm not sure how to use this.. Any help? Thanks guys.
When you use Native Query (non HQL ) you need to tell hibernate explicitely to handle it like below :
In below query createSQLQuery is special function to handle native sql's
String sql = "SELECT * FROM EMPLOYEE WHERE id = :employee_id";
SQLQuery query = session.createSQLQuery(sql);
query.addEntity(User.class);
query.setParameter("employee_id", 3);
List<User> results = query.list();
This can be done using criteria as well for that following is good starting point:
Criteria criteria = sess.createCriteria( User.class);
List<User> users= criteria.list();
http://www.developerhelpway.com/framework/hibernate/criteria/index.php
First of all, you need a hibernate.cfg.xml which contains properties for hibernate. This is e.g url, username and password, the driver and dialect. This file is placed in a package called resources.
You have to choose between using Hibernate Annotations example
or using hbm.xml files example
This is how you tell hibernate what your database is like. It wil automatically create queries for you based on how you annotates or defines in e.g user.hbm.xml.
Create a HibernateUtil.java class which holds the session factory.
You can fetch data from the database with
Criteria crit = getSessionFactory().getCurrentSession().createCriteria(User.class);
Example using queries:
List<?> hibTuppleResultList = currentSession.createQuery(
"from Person p, Employment e "
+ "where e.orgno like ? and p.ssn = e.ssn and p"
+ ".bankno = ?")
.setString(0, orgNo).setString(1, bankNo).list();
for (Object aHibTuppleResultList : hibTuppleResultList)
{
Object[] tuple = (Object[]) aHibTuppleResultList;
Person person = (Person) tuple[0];
hibList.add(person);
}
In the end all I really wanted was to know that if you don't want to use HQL you get something called 'Criteria Queries', and that in my case I'd do something like this:
Criteria cr = session.createCriteria(User);
cr.add(Restrictions.eq("id", 3));
List results = cr.list();
Me: "Thanks!"
Me: "No problem :)"
PS - we can really delete this question.
Query q = session.createQuery("from User as u where u.id = :u.id");
q.setString("id", "3");
List result = q.list();
Query with Criteria:
Criteria cr = session.createCriteria(User.class);
List results = cr.list();
Restrictions with Criteria:
Criteria cr = session.createCriteria(User.class);
cr.add(Restrictions.eq("id", 3));
// You can add as many as Restrictions as per your requirement
List results = cr.list();
You could also use it like this
List results = session.createCriteria(User.class).add(Restrictions.eq("id", 3)).list();
Some example for Crieteria Rsetriction query
Criteria cr = session.createCriteria(Employee.class);
// To get records having salary more than 2000
cr.add(Restrictions.gt("salary", 2000));
// To get records having salary less than 2000
cr.add(Restrictions.lt("salary", 2000));
// To get records having fistName starting with zara cr.add(Restrictions.like("firstName", "zara%"));
// Case sensitive form of the above restriction.
cr.add(Restrictions.ilike("firstName", "zara%"));
// To get records having salary in between 1000 and 2000
cr.add(Restrictions.between("salary", 1000, 2000));
// To check if the given property is null
cr.add(Restrictions.isNull("salary"));
// To check if the given property is not null
cr.add(Restrictions.isNotNull("salary"));
// To check if the given property is empty
cr.add(Restrictions.isEmpty("salary"));
// To check if the given property is not empty
cr.add(Restrictions.isNotEmpty("salary"));
You can create AND or OR conditions using LogicalExpression restrictions as follows:
Criteria cr = session.createCriteria(Employee.class);
Criterion salary = Restrictions.gt("salary", 2000);
Criterion name = Restrictions.ilike("firstNname","zara%");
// To get records matching with OR condistions
LogicalExpression orExp = Restrictions.or(salary, name);
cr.add( orExp );
// To get records matching with AND condistions
LogicalExpression andExp = Restrictions.and(salary, name);
cr.add( andExp );
List results = cr.list();
I think this will help you

Named Query Possibility

I have a fields of customerName, MembershipNumber, nationality and some other field in my customer table and if i get one value from the above three. I need to write only one named query to get the value from the Customer table from the value i got. Is there any possibility to do that by named query without use of normal query in jpa?...
StringBuilder s=new StringBuilder();
s.append("select c from customerdetail c where )
if(customerName!=null)
{
s.append(" c.customerName = :customerName")
}
else if(memberShipNumber!=null)
{
s.append(" c.memberShipNumber = :memberShipNumber")
}
else if(nationality!=null)
{
s.append(" nationality = :nationality)
}
Here i use the same table with three conditions. So is there any possiblity to write only one named query or any other static query to satisfy all the three conditions in jpa?
Try reading ObjectDB's manual on JPA Queries. It provides information on selecting JPA entities and different variations with its custom fields. It has query examples expressed as in JPQL so with use of Criteria. And yes, you can define namedQuery using JPQL and later use-re-use it.
Named queries are static & their scope is persistence context, they can't be altered at runtime.
Below is the sample for adding parameter based on condition using Criteria API.
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<CustomerDetail> cq = cb.createQuery(CustomerDetail.class);
Metamodel m = em.getMetamodel();
EntityType<CustomerDetail> CustomerDetail_ = m.entity(CustomerDetail.class);
Root<CustomerDetail> detail = cq.from(CustomerDetail.class);
if(customerName != null)
cq.where(cb.equal(detail.get(CustomerDetail_.customerName), customerName));
if(memberShipNumber != null)
cq.where(cb.equal(detail.get(CustomerDetail_.memberShipNumber), memberShipNumber));
if(nationality != null)
cq.where(cb.equal(detail.get(CustomerDetail_.nationality), nationality));
cq.select(detail);
TypedQuery<CustomerDetail> q = em.createQuery(cq);
List<CustomerDetail> customerList= q.getResultList();
Else, you can go with building a query with string by appending conditions, instead of named query.
You can use projection with NamedQuery, this example will obtain a single customerName field if MembershipNumber is unique or a List with every customerName that matches the where condition:
#Entity
#NamedQuery(name="getCustomerName", query="SELECT c.customerName FROM Customer c WHERE c.membershipNumber = :memNum")
public class Customer {
...
}
Then you can call it with: (em is your EntityManager)
String customerName = em.createNamedQuery("getCustomerName")
.setParameter("memNum", myMembershipNumber)
.getSingleResult();

Categories

Resources