I need to apply a SQL Query similiar to this.
SELECT
id as id,
c03 as c03,
c34 as c34
FROM
(SELECT
id,
c03,
c34
FROM
students
where
c34 in(
?, ?,?,?
)
order by
id desc) o
group by
c34;
And My Java code.
private final void retrieveStudents(){
final List result = currentSession()
.createSQLQuery("SELECT id as id,c03 as c03,c34 as c34 FROM (SELECT id,c34,c03 FROM students where c34 in(:filters) order by id desc) o group by c34;")
.setParameterList("filters",Arrays.asList(74,1812))
.list();
result.forEach(this::consumer1);
}
The query is Just OK. Returns a array of objets which i can iterate but i would like to return a Student object so i add.
.addEntity(Student.class)
But a error is throw which says
Column 'ACTIVE' not found.
Also i have try with .addScalar but the same thing happens.
.addScalar("id",org.hibernate.type.IntegerType.INSTANCE)
.addScalar("c03",org.hibernate.type.DateType.INSTANCE)
Which is a column from Student but is not on the projection my question how can i do that i just thought that applying in some way the alias would Hibernate populate the student entity.
All i want is a Student object with id,c03,c34 values populate.
What i am doing wrong is that possible?
For this use case, you don't want hibernate to treat Student as an entity, but as a DTO.
For this, do not use the addEntity method, but the setResultTransfomer :
final List result = currentSession()
.createSQLQuery("SELECT id as id,c03 as c03,c34 as c34 " +
"FROM (SELECT id,c34,c03 FROM students " +
"where c34 in(:filters) " +
"order by id desc) o group by c34")
.setParameterList("filters",Arrays.asList(74,1812))
.addScalar("id",org.hibernate.type.IntegerType.INSTANCE)
.addScalar("c03",org.hibernate.type.DateType.INSTANCE)
.addScalar("c34",org.hibernate.type.DateType.INSTANCE)
.setResultTransformer(Transformers.aliasToBean(Student.class))
.list();
This is working for non entity classes, provided the class has setters that match the names of the projected columns, and there is a no-arg constructor. I never tested this on an entity class.
If you do not have setters, but a constructor for those 3 fields, you can use :
// choose here the right constructor
java.lang.reflect.Constructor constructor = Student.class.getConstructors()...
// ...
.setResultTransformer(new AliasToBeanConstructorResultTransformer(constructor));
instead.
EDIT :
I would not use an entity as a DTO (but create a specific DTO for this use case) : what if one of your service think the entity was loaded the regular way (so is fully initialized), do some modifications on it (update a field for example), and save the entity ?
In the best case, a not-nullable field (on db side) will not have been initialized, and you'll get a ConstraintViolationException and trigger a rollback, keeping your data safe.
In the worst case, you'll be corrupting your data by setting to null all the fields of the entity but the three loaded ones.
Related
I have a query:
#Query(
value = "select name, age, now() from received.scheme ;",
nativeQuery = true
)
public {???} selectData()
I cannot create or return an entity for such a scheme as there is no natural id in it, so is there a way to return something like List<Triple<String, Int, LocalDateTime>>?
you can create another class with the required properties which you want to retrieve from the database and then you can return that class as List<Class>.
In your code you get the data from: scheme
So the Entity SchemeEntity should contains those three fields:
name
age
now (creationDate for example it depend on your logic)
Then your method should be like this:
#Query(value = "select name, age, now() from received.scheme ;",
nativeQuery = true
)
public List<SchemeEntity> selectData();
You can provide a PROJECTION entity which will have 3 attributes and must provide a parametrized constructor with those 3 attributes you want to fetch ,or you can still get them as a List<Object[]> and then hydrate your entity.
I've tried those things but what worked for me is using ctid as an #Id. Since I only needed some sort of a mock id, it worked fine.
I'm new to ORM interface, and I'm trying to connect to my databases with Hibernate.
What I've figured out so far is:
With a serializable object, I can get a persistent object with
Person p = session.get(Person.class, serializable);
I can get all the objects by a list with
List people = session.createQuery("FROM Person").list();
What I need is to find a row that meets a certain condition, such as SELECT * FROM person WHERE name="Kim" AND age=30;
However, the above two aren't the ways to achieve this.
#Entity
#Table(name = "person")
public class Person {
#Id
private Integer id; // I can use this variable when using session.get(Person.class, serializable) , but I cannot know the id of my target row.
private String name;
private Integer age;
...
Should I iterate all the objects in people, and check whether all the member variables match what I want?
Is there any simple way to achieve this?
First and most importantly, never put user input in a query like this
SELECT * FROM person WHERE name="Kim" AND age=30;
You have to use Prepared Statements. Learn why from Bobby Tables.
Secondly, you should use the JPA interface EntityManager instead of Hibernate's Session as the second one anchors you to a specific implementation, rather than the wider standard.
With the EntityManager you get an object by id like this:
Person p = em.find(Person.class, id);
To get a list of People you can create a JPQL query like this:
TypedQuery<Person> query = em.createQuery("SELECT p FROM Person p WHERE p.name = :name AND p.age = :age", Person.class);
query.setParameter("name", "Kim"); // :param1 defines a parameter named "param1" in the query
query.setParameter("age", 30);
List<Person> results = query.getResultList();
You could also do this in one chain if you don't need to reuse the query with different parameters on a loop.
List<Person> results = em.createQuery(..., Person.class)
.setParameter("name", "Kim")
.setParameter("age", 30)
.getResultList();
The reason to put every call on a new row is in case an exception occurs it will give you the proper row to look for. If they're all in one row, then that's not very useful.
If your query is a SELECT, and it needs to return exactly one result every time, you can use getSingleResult() instead of getResultList(). If you do that and the query did return more than one result, it will throw a NonUniqueResultException. If the query did not return any results it will throw a NoResultException instead of returning null.
If your query is NOT a SELECT, then you have to use executeUpdate() to invoke it after setting the parameters.
There are many resources to get you started, but generally if its for a Hibernate version before 5.2 you should consider it outdated, and it will likely be more difficult.
This should be straight-forward though can't get my Hibernate entities to play nice for the following scenario with a simple two table structure:
I'm attempting to get all config names and matching config values for a given currency code (and null's where not matching).. so have written a native query to retrieve the following like so:
SELECT * FROM CONFIG_NAME LEFT JOIN CONFIG_VALUE ON CONFIG_NAME.ID =
CONFIG_VALUE.CONFIG_ID AND CONFIG_VALUE.CURRENCY_CODE = '<CURRENCY_CODE>'
ORDER BY CONFIG_NAME.ID
This query doesn't seem to play nice with my Hibernate mapping as it appears to be essentially ignoring the CURRENCY_CODE clause in the join.
Essentially, for the following subset of data:
CONFIG_NAME:
CONFIG_VALUE:
There is no value defined for 'FREE_SHIPPING_ENABLED' for 'USD' so running the query above for both currency code returns as expected:
QUERY RESULTS FOR 'CAD':
QUERY RESULTS FOR 'USD':
I'm running the above query as a native query in a JpaRepository for the ConfigName entity. But what I appear to be getting is that it seems to ignore the currency_code clause in the JOIN condition. As the list of config values defined has both values for USD and CAD where they're populated. Is there an Hibernate annotation to factor this in that I'm unaware of?
It's worth bearing in mind there will only ever be ONE value defined for each config for a given currency - there's a unique constraint across CONFIG_VALUE.CONFIG_ID/CONFIG_VALUE.CURRENCY_CODE so potentially ConfigValue on the ConfigName entity would not need to be a map.
Mappings as are follows:
ConfigName - Entity
#OneToMany(mappedBy = "config")
private Set<ConfigValue> configValue;
ConfigValue - Entity
#ManyToOne(optional = false)
#JoinColumn(name="CONFIG_ID")
#Property(policy=PojomaticPolicy.NONE)
private ConfigName config;
Doesn't need to be strictly unidirectional either.. as I'm only concerned with the values from the ConfigName entity either being populated or null.
Think I'm missing something simple, so hope someone can help.
EDIT: Am querying using JpaRepository:
Am using JpaRepository to query:
#Repository
public interface ConfigNameRepository extends JpaRepository<ConfigName, Long>
{
static final String SQL_QUERY = "SELECT * FROM CONFIG_NAME "
+ "LEFT JOIN CONFIG_VALUE ON CONFIG_NAME.ID = CONFIG_VALUE.CONFIG_ID "
+ "AND CONFIG_VALUE.CURRENCY_CODE = ?1 ORDER BY CONFIG_NAME.ID";
#Query(value = SQL_QUERY, nativeQuery = true)
List<ConfigName> findConfigValuesByCurrencyCode(final String currencyCode);
}
As mentioned by #Ouney, your JPA relations are not taken in account if you use a native query.
You declared a SELECT * and List<ConfigName> (the real sql result contains ConfigName+ConfigValue). So with this query, Hibernate fetchs all the ConfigName. Then, when you try to access to the set of configValue, it fetchs all the related ConfigValue.
I think this should be better/easier to use a JPQL query instead (but you need Hibernate 5.1+) :
SELECT n, v
FROM ConfigName n
LEFT JOIN ConfigValue v
ON v.config = n AND v.currencyCode = :currencyCode
ORDER BY n.id
With this method signature :
List<Object[]> findConfigValuesByCurrencyCode(#Param("currencyCode") String currencyCode);
Where the result will be :
o[0] // ConfigName
o[1] // ConfigValue (nullable)
You may want to do this prettier with a wrapper :
SELECT new my.package.MyWrapper(n, v)
...
MyWrapper constructor :
public MyWrapper(ConfigName configName, ConfigValue configValue) {
...
}
Method signature with the wrapper :
List<MyWrapper> findConfigValuesByCurrencyCode(#Param("currencyCode") String currencyCode);
(update)
I think in this case, your query can be :
SELECT n, v // or new my.package.MyWrapper(n, v)
FROM ConfigName n
LEFT JOIN n.configValue v
WITH v.currencyCode = :currencyCode
ORDER BY n.id
In hibernate I am creating custom query like below.
Query query = getSession()
.createSQLQuery(
"select t2.* from ( select (select id from ctc_ver_assets ti where ti.ctcassettag = t1.ctcassettag order by ti.createddate desc limit 1) lid from "
+ "( select distinct ctcassettag from ctc_ver_assets) t1) ro, ctc_ver_assets t2 where t2.id = ro.lid and date(createddate) ")
.setResultTransformer(Transformers.aliasToBean(VerificationAsset.class));
My verificationAsset class contains field namely ctcAssetTag but the field name in table is ctcassettag. How can I map this to the object automatically?
I can rename the individual fields in the query itself like t2.ctcassettag as ctcAssetTag. But I do not want to do that for each field because I already annotated each field in my Bean class like below
#Column(name = "ctcassettag", unique = true, nullable = false)
private String ctcAssetTag;
When I use criteria it is working fine. How to achieve the same with query?
solution 1:
you can use alias_to_entity_map so you don't need to declare model name. it will map your result according to your query.
solution 2:
you can create #transient fields in your model to map fields as you wanted.
solution3:
you can create DTO and use it in aliasToBean(VerificationAssetDTO.class) instead of your solution.
I have a hibernate call in my DAO that looks like this
List<Associate> associate = (List<Associate>)session.createSQLQuery("SELECT * FROM associates WHERE fk_id = :id AND fk_associate_id = (SELECT id FROM users WHERE fk_user_type = 2)").setParameter("id", id).list();
I am getting an error saying that I cannot cast the resulting list to the model type Associate. I don't understand why this is happening. I am returning only the fields that are in the associates table.
You need to specify the class of entity the result should be converted to using addEntity(), because you are executing SQL query that doesn't know anything about entities:
List<Associate> associate = (List<Associate>) session.createSQLQuery(
"SELECT * FROM associates WHERE fk_id = :id AND fk_associate_id = (SELECT id FROM users WHERE fk_user_type = 2)")
.addEntity(Associate.class)
.setParameter("id", id).list();
See also:
18.1.2. Entity queries
It is possible to apply a ResultTransformer to native SQL queries, allowing it to return non-managed entities.
sess.createSQLQuery("SELECT NAME, BIRTHDATE FROM CATS")
.setResultTransformer(Transformers.aliasToBean(CatDTO.class))
The above query will return a list of CatDTO which has been instantiated and injected the values of NAME and BIRTHNAME into its corresponding properties or fields.
source : Link