I have these entities (is an example because i cant share real name entities):
#Entity
public class User { #Id private BigDecimal id; private String name, private Color favouriteColor }
#Entity
public class Color { #Id private Long colorId; private String colorName;}
In the table I have this data:
USER
ID|NAME|FavColor
1 |John| 1
2 |Sarah| 2
3 |Mike| 1
COLOR
1|Red
2|Blue
Now I want make a query that recover all my user data without select Color entity, only its ids.
#Query("new myDto(u.iduser,u.username,u.favcolor) from user u where favcolor in :listcolors")
This makes me an query of the two tables, I want a unique query because i dont need color entities, only the ids.
--
Other option that I am testing is making a implementation of a nativequery like this:
final List<MyDTO> result = new ArrayList<>();
Query q = entityManager.createNativeQuery("SELECT " +
" USER_ID, " +
" USER_NAME, " +
" FAV_COLOR " + +
"FROM USER " +
"WHERE FAV_COLOR IN (?)");
q.setParameter(1, colors.toString().replace("[","").replace("]",""));
Long TRUE = new Long(1L);
final List<Object[]> resultList = q.getResultList();
for (Object[] objects : resultList) {
MyDTOdto = new MyDTO();
dto.userId(((((BigDecimal) objects[0]) != null) ? ((BigDecimal) objects[0]).longValue() : null));
dto.userName(((((String) objects[0]) != null) ? ((String) objects[0]).longValue() : null));
dto.favColor(((((BigDecimal) objects[0]) != null) ? ((BigDecimal) objects[0]).longValue() : null));
result.add(dto);
}
return result;
In this case, I am getting error code (ORA-1722 - Number Not valid). I don't know what I can test now. Some ideas? Thanks
I am guessing you have issues with the SQL generated and your use of the inner join: when you call "u.favcolor" in the select clause, you are telling JPA to perform an inner join from User to Color based on the favcolor relationship. As favcolor is a Color reference, you are going to get the full color row, where as your native query implies you just want the foreign key value. If all you want is the fk/ID value from Color, the query should be:
"SELECT new myDto(u.iduser, u.username, color.id) FROM user u join u.favcolor color WHERE color.id in :listcolors"
This still might perform an inner join from user to color, but it should be in a single statement.
If you want to ensure you avoid the join:
Use EclipseLink's COLUMN JPQL extension to access the foreign key column directly. Something like:
"SELECT new myDto(u.iduser, u.username, COLUMN('FAV_COLOR', u) FROM user u WHERE COLUMN('FAV_COLOR', u) in :listcolors"
Use EclipseLink native query key functionality to access the "FAV_COLOR" foreign key column in the USER table directly for your JPQL queries. This requires a descriptor customizer to access, but allows you to use the foreign key value in JPQL queries directly without having to map it, and without the COLUMN mechanism tying your JPQL queries to a particular database table detail. This would allow a query of the form:
"SELECT new myDto(u.iduser, u.username, u.favColorVal FROM user u join u.favcolor color WHERE u.favColorVal in :listcolors"
Just map the FAV_COLOR as a basic mapping, in addition to the existing favColor reference mapping (or replacing it if you want):
#Basic
#Column(name="FAV_COLOR", updatable=false, insertable=false)
BigDecimal favColorId
This then allows you to use query "SELECT new myDto(u.iduser, u.username, u.favColorId FROM user u join u.favColorId color WHERE u.favColorId in :listcolors" to the same effect, but you can also just return the User instance (marking favColor as lazy and not serializable) as it will have the same data anyway.
Related
Currently I am doing it like this:
List<Table1Entity> findAllMatchingEntities(Table1Entity table1Entity) {
String queryString = "SELECT table1.* FROM table1 "
+ "JOIN table2 t2 ON table1.id=t2.table1_id";
if (table1Entity.getName() != null) {
queryString +=" where name like ?";
}
Query query = em.createNativeQuery(queryString, Table1Entity.class);
if (table1Entity.getName() != null) {
query.setParameter(1, table1Entity.getName())
}
return query.getResultedList();
}
If I want to check more parameters in this join this will quickly turn into a lot of if statements and it would be really complicated to set parameters correctly.
I know I can check parameters with criteria Builder API like this:
if(table1Entity.getName() != null) {
table1EntitySpecification = (root, query, criteriaBuilder)
-> criteriaBuilder.like(
criteriaBuilder.lower(root
.get("name")),
("%" + table1Entity.getName() + "%")
.toLowerCase());;
}
and after that get them all with:
findAll(table1EntitySpecification) with findAll from simpleJPARepository. Now I can chain them together with .or or .and etc. and avoid setting the parameter and checking for null second time.
But how do I do join with criteria APi?
I know I can have in my #Repository something like this:
#Query(value = "SELECT table1.* FROM table1 JOIN table2 t2 ON table1.id=t2.table1_id", nativeQuery = true)
List<Table1Entity> findAllMatchingEntities(Table1Entity table1Entity);
But since name is optional (can be null) I can't just leave it in #Query.
What is the best solution here to avoid using native query and in case of having to check many parameters to avoid using if statements?
I don't know if I fully get your question, but regarding the possibility of nulls, and using the CRUD repository, you can always do a null check before like:
#Query(value = "SELECT table1.* FROM table1 JOIN table2 t2 ON table1.id=t2.table1_id WHERE table1.id is not null", nativeQuery = true)
List<Table1Entity> findAllMatchingEntities(Table1Entity table1Entity);
Depending on what you are trying to achieve, you can always compose the query with similar checks like (not related to your code):
#Query("SELECT c FROM Certificate c WHERE (:id is null or upper(c.id) = :id) "
+ "and (:name is null or upper(c.name) = :name)")
List<Table1> findStuff(#Param("id") String id,
#Param("name") String name);
I'm making the switch away from ORMs in Java and I was wondering what was the best way of dealing with many-to-one and many-to-one relationships in a non-ORM setting.
In my Customer.java class I have:
private Long id;
private String name;
private Date dob;
//About 10 more fields
private List<Pet> pets;
In Pet.java I have:
private String id;
private String name;
private Customer owner;
My database table for Pet looks like this
id BIGSERIAL PRIMARY KEY,
name VARCHAR(20),
owner_id BIGSERIAL REFERENCES...
Now I realize that if I run a query that joins the two tables, I get a "flat" data structure returned which contains the fields for both Customer and Pet as well as any foreign keys. What is the common/most efficient way to treat the data in this scenario?
a. Rebuild the object graph manually by calling customer.setName(resultSet.getString(("name"))...?
b. Use the returned data as is by converting it to a Map<String, Object>?
The data flow is: Data is read from the database -> rendered to JSON for use by an AngularJS front end -> modified data is sent back to the server for validation -> domain logic applied -> saved to database.
If you want to read both Customer and Pet in a single query for better performance, you can do something like this:
List<Customer> customers = new ArrayList<>();
String sql = "SELECT c.id AS cust_id" +
", c.name AS cust_name" +
", c.dob AS cust_dob" +
", p.id AS pet_id" +
", p.name AS pet_name" +
" FROM Customer c" +
" LEFT JOIN Pet p ON p.owner_id = c.id" +
" WHERE c.name LIKE ?" +
" ORDER BY c.id";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, "%DOE%");
try (ResultSet rs = stmt.executeQuery()) {
Customer customer = null;
while (rs.next()) {
long cust_id = rs.getLong("cust_id");
if (customer == null || customer.getId() != cust_id) {
customer = new Customer();
customer.setId(cust_id);
customer.setName(rs.getString("cust_name"));
customer.setDob(rs.getDate("cust_dob"));
customers.add(customer);
}
long pet_id = rs.getLong("pet_id");
if (pet_id != 0) {
Pet pet = new Pet();
pet.setId(pet_id);
pet.setName(rs.getString("pet_name"));
pet.setOwner(customer);
customer.addPet(pet);
}
}
}
}
The best option at this time are:
Spring JDBC (it has convenience of ORM like bean to object mapping etc.)
iBatis (allows to write SQL queries manually although it is ORM but a thin layer)
Write your own DAO layer implementation.
In all these cases you write your own sql queries and mostly they will result in join queries. By the way the example you have given are not nested objects.
My entity User has a basic collection as such:
#ElementCollection
private Set<String> completedQuests = Sets.newHashSet();
How can I remove some values from that set for all/multiple users? What is the proper JPQL for this pseudoquery?
DELETE FROM User.completeQuests
WHERE value IN (:collectionOfValues)
(A Hibernate-only alternative, though not preferred, is also welcome.)
If all else fails, native SQL.
em
.createNativeQuery(
"DELETE FROM user_completedquests " +
"WHERE completedquests IN (:daily)"
)
.setParameter("daily", dailyQuests)
.executeUpdate();
(Haven't tested it yet)
I've stack over one small thing:
I have two tables in database, which are related - User and UserReference.
In UserReference i have field userid, which is related to id in User.
i have query to mysql with join left:
"FROM User as user left join fetch user.userReferences WHERE login='"+login+"' or email='"+login+"' AND password = '" +password+ "'" ;
The query is ok and i get results.
I send the results to controller, changing it into list: query.list();
In controller i receive the results and push it into user list.
From the user list i can get info from table User
In table User there is:
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
public Set<UserReference> getUserReferences() {
return this.userReferences;
}
And now - i want to get data from table UserReference which are in user list, becouse my query have JOIN.
How can i do it?
i was trying to do something like this:
List<UserReference> userReference = (List<UserReference>) user.get(0).getUserReferences();
System.out.println(userReference.get(0).getAge());
But it doesn't work.
Can you help me ?
You can't cast a Set to a List, iterate over the Set to get userRefences instead.
I have (non-Hibernated) database tables that contain ids for Hibernate entities. I can query them (using createSQLQuery), which gives me the ids, from which I can then load the entities.
I'd like to do that in one step, and I think I can do that with addEntity, but I am not sure how exactly. (Hibernate's documentation web site is down. Again.) I can use addEntity when all the columns for the entity table are present, but I have only the id now.
This complains about the missing columns:
return (List<MyEntity>) session.createSQLQuery(
"select entity_id from the_table where foreign_key_value = ?")
.addEntity("entity_id", MyEntity.class)
.setLong(0, foreignKey).list();
I think you want something like:
session.createSQLQuery("select {entity.*} from entity_table {entity} where ....")
.addEntity("entity", Entity.class).(bind-parameters).list();
Hibernate will expand "{entity.*}" to be the relevant columns from entity_table.
Although if you already have the IDs, you can simply use session.load() to convert those to actual instances (well, lazy-load proxies).
i would use a join
select *
from entity_table
where entity_id = (select entity_id
from non_hibernate_table
where id = ?)
For oracle dialect. If u have problem with mapping database column type to java data type u can set it manually like that: .addScalar("integerFieldName", Hibernate.INTEGER)
public class LookupCodeName
{
private String code;
private String name;
/*... getter-setters ... */
}
public class someBL {
public List<LookupCodeName> returnSomeEntity() {
SQLQuery sqlQuery = (SQLQuery)((HibernateSession)em).getHibernateSession()
.createSQLQuery( "SELECT st.name as name, st.code as code FROM someTable st")
.addScalar("code")
.addScalar("name")
.setResultTransformer(Transformers.aliasToBean(LookupCodeName.class));
}
return (List<LookupCodeName>)sqlQuery.list();
}