I have a JPA 2.1 project, database has 2 tables. I have created an interface "UsedClasses.java" and have both Entities implement the interface., i.e.
#Entity
#Table(name = "runRanges")
#NamedQuery(name = "RunRange.findAll", query = "SELECT r FROM RunRange r")
public class RunRange extends AbstractTimestampEntity implements UsedClasses, Serializable ....
and
#Entity
#Table(name = "users")
#NamedQuery(name = "User.findAll", query = "SELECT u FROM User u")
public class User extends AbstractTimestampEntity implements UsedClasses, Serializable ...
I am curious to know if I can use the interface to grab the NamedQuery for either of the Entities.
What I am trying to accomplish if getting the results list according to the class.
public List<UsedClasses> getAllItems() {
open();
Query query = EntityManagerHandler.INSTANCE.getEntityManager().createQuery(UsedClasses.class.getResource(NamedQueries));
List<UsedClasses> aList = query.getResultList();
return aList;
}
You can do it but you should use reflection to retrieve the NamedQuery annotation and you would need to have the UsedClasses instance and not the interface like in your example :
Query query = EntityManagerHandler.INSTANCE.getEntityManager().createQuery(UsedClasses.class.getResource(NamedQueries));
Besides, if you add multiple annotations, it will not work any longer.
I think that a more simple and clean solution would be to add a method in your interface to get the namedQuery String name.
For example
public interface UsedClasses(){
String getNamedQueryForGetAll();
}
And you could implement it in this way :
#Entity
#Table(name = "users")
#NamedQuery(name = "User.findAll", query = "SELECT u FROM User u")
public class User implements UsedClasses(){
public String getNamedQueryForGetAll(){
return "User.findAll";
}
}
Then you can use it in this way :
Query query = EntityManagerHandler.INSTANCE.getEntityManager().createQuery(usedClassInstance.getNamedQueryForGetAll());
Now, I find that it creates a little of duplication and it is not straight readable.
NamedQuery brings a extremely tiny performance improvement.
I am not sure that the complexity/indirection level introduced in the code to gain so few in terms of execution time makes you winning.
Related
I have the following (simplified) data model:
#Entity
public class Person
{
#ManyToOne Animal likes;
}
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
public class Animal{}
#Entity
public class Pet extends Animal
{
#ManyToOne Home home;
}
#Entity
public class Domestic extends Animal
{
#ManyToOne Farm farm;
}
#Entity public class Home {}
#Entity public class Farm {}
Now I want a group the persons by home and count on different homes:
CriteriaBuilder cB = em.getCriteriaBuilder();
CriteriaQuery<Tuple> cQ = cB.createTupleQuery();
Root<Person> person = cQ.from(Person.class);
Join<Person,Pet> jPet = person.join("likes");
Join<Pet,Home> jHome = jPet.join("home");
Here I'm getting the error
Unable to locate Attribute with the the given name [home] on this ManagedType [Animal] the generated SQL query obviously explains why:
INNER JOIN animal a ON person.likes_id = animal.id. The next approach was to use .treat:
Join<Person,Pet> jPet = cB.treat(debit.join("likes), Pet.class);
Join<Pet,Home> jHome = jPet.join("home");
This seemed to be promising with the new .treat feature of JPA 2.1, but the generated SQL looks like this
INNER JOIN animal ON person.likes_id = animal.id
LEFT OUTER JOIN pet ON pet.id = animal.id
LEFT OUTER JOIN domestic ON domestic.id = animal
In fact it is joining all subclasses of Animal where I'm only interested in Pet. So I tried to limit to the specific class with a Predicate cB.equal(jPet.type(),Pet.class). This generates some interesting SQL (which I have never seen before and where I wonder what is inserted in END=?)
WHERE CASE
WHEN pet.id IS NOT NULL THEN 1
WHEN domestic.id IS NOT NULL THEN 2
WHEN animal IS NOT NULL THEN 0
END = ?
But it still does not work as expected. How can I count the different home with JPA criteria queries where I start from a Person?
You may use multiple roots (mine: I believe that is the most common scenario) in Criteria API query:
Create and add a query root corresponding to the given entity, forming a cartesian product with any existing roots.
So, your query might look like:
Root<Person> person = cQ.from(Person.class);
Root<Pet> pet = cQ.from(Pet.class);
cQ.where(cB.equal(pet, person.get("likes")));
Moreover, that is not required to define X-TO-Y associations in order to write a query - we may join "unrelated" entities as well.
There are many existing questions for this issue but my case is little different because I am trying to use named query in EntityManager.
I am trying to use named query(Defined in jpa-named-queries.properties not using #NamedQuery) in EntityManager but I am always getting No query defined for "name".
I have a class User
#Entity
#Table(name = "USER") // Note have column configuration also, not bring here to clear the code
class User{
// My Properties
}
I have its repository which is extended from JpaRepository as well as from CustomUserRepository.
Here is my customer user repository.
interface CustomUserRepository{
List<User> executeQuery(String named, List<Object> params);
}
// its implementation class is something like this.
class CustomUserRepositoryImpl implements CustomUserRepository{
#PersistenceContext
private EntityManager manager;
List<User> executeQuery(String named, List<Object> params){
javax.persistence.Query namedquery = getEntityManager().createNamedQuery(query);
int[] idx = { 1 };
parameters.stream()
.filter(p-> p!=null)
.forEach((m)->
namedquery.setParameter(idx[0]++, m));
return namedquery.getResultList();
}
}
I am using this executeQuery method from user repository.
Everything works fine if I declare named query above User class as follows.
#Entity
#Table(name = "USER") // Note have column configuration also, not bring here to clear the code
#NamedQuery(name = "getUserByCity",
query = "from User u where u.city= ?1")
class User{
// My Properties
}
But not working If I am adding named query in src/main/resources/META-INF/jpa-named-queries.properties with this I am getting no named query found for this key. following is my jpa-named-queries.properties file contents
getUserByCity=from User u where u.city= ?1
# also tried User.getUserByCity=from User u where u.city= ?1
Is there a way to select only some columns from a table using jpa?
My tables are huge and I am not allowed to map all the columns in my entities. I tried to create an entity (as a side note, I don't have PKs in my tables):
#Entity
#Table(name = "SuperCat")
#Getter
#Setter
public class Cat{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
#Column(name="nameCat")
private String name;
}
and then in my repository to
public interface CatRepository extends
CrudRepository<Cat, Long> {
#Query(
"SELECT name FROM Cat")
Page<Cat> getAlCats(Pageable pageable);
This is only a simple example, but the idea is the same. I have searched a lot and I found projections, but there you need to map the whole table, then I found native queries, but still doesn't apply. I know I can return an Object and the other solution is to use query with NEW and create my own object (no #entity, like a pojo). But is there a way that I can do this using jpa, to be able to use repository and services, if I am creating my own pojo then i will create a #transactional class put the queries (with NEW) there and this is it. I don't like this approach and I don't think that the jpa does't allow you to select only some columns, but I didn't find a proper way.
Maybe you will ask what is the result if I am doing like this:
I get this error: "Cannot create TypedQuery for query with more than one return using requested result type [java.lang.Long]"
(For new queries, I am talking about : http://www.java2s.com/Tutorials/Java/JPA/4800__JPA_Query_new_Object.htm maybe I was not clear)
You can do the same by using below approach.
Just create a constructor in entity class with all the required parameters and then in jpa query use new operator in query like below.
String query = "SELECT NEW com.dt.es.CustomObject(p.uniquePID) FROM PatientRegistration AS p";
TypedQuery<CustomObject> typedQuery = entityManager().createQuery(query , CustomObject.class);
List<CustomObject> results = typedQuery.getResultList();
return results;
And CustomObject class should look like below with the constructor.
public class CustomObject {
private String uniquePID;
public CustomObject(String uniquePID) {
super();
this.uniquePID = uniquePID;
}
public String getUniquePID() {
return uniquePID;
}
public void setUniquePID(String uniquePID) {
this.uniquePID = uniquePID;
}
}
spring-data-jpa projection not need to map the whole table, just select the necessary fileds :
// define the dto interface
public interface CatDto {
String getName();
// other necessary fields
...
}
#Query(value = "select c.name as name, ... from Cat as c ...)
Page<CatDto> getAllCats(Pageable pageable);
By this way, CatDto is an interface and it only includes some fileds part of the whole table. Its fields name need to match the select field's alias name.
If I have one table named A_client, and another one named B_client. A_client has ID's and different status values, while B_client is holding personal data, such as name and adress.
How do I do this if I followed this guide?
Examples below to show you where I am
I have a A_Client.java that looks like this:
#Entity
#Table(name = "A_client")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "AClient.findAll", query = "SELECT e FROM AClient e"),
#NamedQuery(name = "AClient.findById", query = "SELECT e FROM AClient e WHERE e.Id = :Id"})
And I have a A_ClientFacadeREST that looks like this:
#Stateless
#Path("test")
public class AClientFacadeREST extends AbstractFacade<AClient> {
#PersistenceContext(unitName = "com.123_MavenProjectTest_war_1.0-SNAPSHOTPU")
private EntityManager em;
public AClientFacadeREST() {
super(AClient.class);
}
#GET
#Path("id")
#Produces({"application/xml", "application/json"})
public List<AClient> findById() {
List<AClient> results = em.createNamedQuery("AClient.findById", AClient.class)
.setParameter("Id", 1)
.getResultList();
return results;
}
etc etc
How do I manage to get data from B_Client aswell as A_Client using REST?
Just set OneToOne relations between tables in your AClient entity
Imagine that I have a Debtor class. With Hibernate, I will define the class like that:
#Entity
#Table(name = "T_DEBTOR")
public class Debtor {
#Id
#Column(name = "ID_DEBTOR")
private String idDebtor;
...
My DAO will then looks like:
public class DebtorDaoImpl implements DebtorDao {
#PersistenceContext
private EntityManager em;
#SuppressWarnings("unchecked")
public List<Debtor> findAllDebtors() {
Query q = em.createQuery("select d from Debtor d");
return (List<Debtor>) q.getResultList();
}
This works well. However, I am in a configuration where I need to access differents schemas (as pointed here). Of course, in each schema the table that hosts the debtor list has not the same name. In addition to that, they may not have the exact same structure. That why I have x differents Debtor class (where x is the number of schemas I manipulate).
In the case where I have two differents schemas, I will have two different Debtor class: DebtorOne and DebtorTwo.
As I want to ease my developments, I created an Interface (or an Abstract class, it does not change my problem here) that is implemented by both DebtorOne and DebtorTwo:
public interface Debtor {
String getIdDebtor();
}
and:
#Entity
#Table(name = "T_DEBTOR_ONE")
public class DebtorOne implements Debtor {
#Id
#Column(name = "ID_DEBTOR")
private String idDebtor;
...
If I let my DAO as it is, I get the following error from Hibernate:
Caused by: org.hibernate.hql.ast.QuerySyntaxException: Debtor is not mapped [select d from Debtor d]
If I change my DAO to have this:
public List<Debtor> findAllDebtors() {
Query q = em.createQuery("select d from DebtorOne d");
return (List<Debtor>) q.getResultList();
}
then it works, but it is specific to DebtorOne schema...
One solution I see is to define a named query on the DebtorOne and DebtorTwo classes and call this named query from my DAO.
In others words:
#Entity
#Table(name = "T_DEBTOR_ONE")
#NamedNativeQueries( { #NamedNativeQuery(name = "findAllDebtors", query = "select d from DebtorOne d") })
public class DebtorOne implements Debtor {
and in the DAO:
#SuppressWarnings("unchecked")
public List<Debtor> findAllDebtors() {
Query q = em.createNamedQuery("findAllDebtors");
return (List<Debtor>) q.getResultList();
}
I didn't try it yet, but I think it will work...
EDIT I just tried, this will work... except that the NamedQuery must be named differently for DebtorOne and DebtorTwo...
However, I am wondering if there is a way to solve my problem without using the latter solution?
Edit regarding the first answer, which suggests to use the #MappedSuperclass. This annotation seems to be the perfect solution for me, but I think I forgot something, as I still get the same error.
The main Debtor:
#MappedSuperclass
public class Debtor {
#Id
#Column(name = "IDDEBTOR")
protected String idDebtor; // With getter and setter
}
one of the extended Debtor class:
#Entity
#Table(name = "DEBTOR_ONE")
public class DebtorOne extends Debtor {
...
and in my DAO:
public List<Debtor> findAllDebtors() {
return (List<Debtor>) em.createQuery("select d from Debtor d").getResultList();
}
is still returning me the error Caused by: org.hibernate.hql.ast.QuerySyntaxException: Debtor is not mapped [select d from Debtor d]
What did I miss this time?
I think this is not possible with an interface, but only with a common abstract base class, that will be annotated with #MappedSuperclass (see Hibernate documentation for further details)
I think for this to work you'd either have to actually map the Debtor table to a table or use the table per class strategy (union-subclass). #MappedSuperClass only seems to implement a very rudimentary mechanism to copy properties and wouldn't work because you can't query for instances of the superclass.
I take it from the link that you already have something in place to avoid mapping DebtorTwo in the hibernate session for the schema of DebtorOne (otherwise querying Debtor would pull in all records, including ones from the DebtorTwo table that wouldn't exist). In that case, follow the example from the documentation to map the subclass for either case.