I have an Entity. And sometimes I need this object also contains some value, call it 'depth'. The query may look like 'select b.id, b.name, b..., count(c.id) as depth from Entity b, CEntity c where ...'. So I've created class NonHibernateEntity which extends Entity. And then results of query written above are perfectly stored as List of NonHibEntity, which consists of all the fields of Entity (as they are extended), and property 'depth'. I make it by setting aliasToBean results transformer: .setResultTransformer(Transformers.aliasToBean(NHEntity.class)).
But, it is annoying and inconvenient - to specify all the aliases of all the needed fields.
And then, if I want to save one of this object to DB - session.saveOrUpdate((Enity)nHibEntity) - there are an exception about nHibEntity isn't Hibernate Entity.
I heard about storing 'entity' as field in NonHibEntity (aggregation, not inheritance). But it seems this is rather inconvenient too.
What do you think? What is an appropriate solution?
A Formula column mapping may be suitable for your needs. I would try this first.
If this causes performance issues as you fear, you might try a mapped class hierarchy with this field only in the child, and mapping both to the same table. Not sure this will actually work though...
As a last resort, do what you've got now using a non-mapped class, but with the entity as a field in your other class - aggregation instead of inheritance as you say, and make sure there's a way of retrieving the mapped entity from the unmapped one so that you can save. It be sensible to make it a Decorator, so that it's both a subclass and aggregate and you can continue to ignore the distinction in much of your code.
With the non-mapped subclass and/or aggregate, however, you'll have to pull out the entity in order to save.
If somebody want to know - I solved this problem in such way:
just made this calculated field as #Transient, and then
List<BaseEntry> result = new ArrayList<BaseEntry>();
Iterator it = session()
.createQuery("select b, (select count(p.id) as depth from BaseEntry p " +
" where ... ) as d" +
" from BaseEntry b " +
" where ... ")
.list().iterator();
while ( it.hasNext() ) {
Object[] row = (Object[]) it.next();
BaseEntry entry = (BaseEntry) row[0];
Long d = (Long) row[1];
entry.setD(d);
result.add(entry);
}
return result;
It works good, and seems that it can be easily supported in future
Related
Assume that we have a persisted Entity object which has 10 variables, if I do for example repository.read(id) or repository.findById(id) I will get back an Entity object with every variable which is set from the repository.
Is there any way using JPAQuery or EntityManager or any other possible way, that I can make the call on the repository and get back the Entity object BUT without a specific variable being fetched as well?
I have tried the following, but it doesnt seem to do anything, still brings the Set within the response:
JPAQuery<Fruit> query = new JPAQuery<>(entityManager);
QFruit fruit = QFruit.Fruit;
Set<Apple> apple = new HashSet<Apple>();
query.select(fruit).from(fruit).where(fruit.id.eq(fruitId))
.createQuery().getParameters().remove(apple);
return query.fetchOne();
You can use any custom POJO to get your results in and specify what is selected.
https://docs.oracle.com/html/E13946_05/ejb3_langref.html#ejb3_langref_constructor
public interface AppleRepository extends CrudRepository<Apple, Long> {
#Query("SELECT new com.mypackage.Apple(a.field1, a.field2) FROM " +
" Apple a ")
List<Apple> findCustomApples();
}
Other way is to make any particular column to be Lazy Loaded. You can do that with annotation.
Eventually I was trying to read specific data from an entry of a Table, because the specific table has so many data it was harassing the performance, thus bringing the whole entity just for 1 or 2 variables was not correct.
Eventually what helped me was Tuple.
Using JPAQuery you have the advantage that you can select specific variables to be brought back from the search.
e.g.
JPAQuery<MyObject > query = new JPAQuery<>(entityManager);
MyObject myObject = QMyObject.MyObject ;
Tuple response = query.select(myObject.id, myObject.version)
.where(myObject.id.eq("12345")).or(myObject.version.eq("12345")).fetchaAll();
Then you can easily retrieve the Tuple object and handle the values as an array.
Given entity classes:
class A { String name ... }
class B { #OneToOne A a = ... ; String gender; ... }
I am able to create a hibernate criteria and query it and all of it's associations.
However, B is not referenced from A at all. It's populated in a separate fashion and I would like to keep it as such.
Is there a way to perform a hibernate criteria query on A, and say, where this also is in B's relation, or where it is in B's relation and some field in B is this and that?
Here is some non-working pseudo code:
criteria(A.class).add(
Restrictions.eq("name", "something")
).createAlias(
B.class, "..."
).add(
Restrictions.eq("gender", "Female")
);
Note, I would prefer not having to create a collection on A containing B's and using addAll.
I might consider adding a dead reference though, meaning something that is never intended to be accessed or updated but needed for hibernate to pull this off. It could be something like saying that this is maintained by another table.
You can use a subquery, as follows:
// create a 'DetachedCriteria' query for all the items in 'B'
DetachedCriteria dc = DetachedCriteria.forClass(B.class).setProjection(
Projections.projectionList().add(Projections.property("propertyInB"))
);
// then: search for A.id by adding 'dc' as asubquery:
session.createCriteria(A.class).add(
Subqueries.propertiesIn( new String[]{"propertyInA"}, dc)
).list();
This is roughly equivalent to: 'SELECT * FROM A a WHERE a.id in(SELECT id FROM B).
I hope my SQL is valid ;) .
See hibernate documentation:
https://docs.jboss.org/hibernate/orm/3.3/reference/en-US/html/querycriteria.html
Generally my questian is very simple I think, nevertheless I couldn't find a good solution. Let's say I have an Entity class called MyEntity which has a OneToMany relation to an Entity class called EntityAttribute, so it has a list or set attributes with objects of this class. EntityAttribute has an attribute name of type String.
Now I want to implement a method which takes attribute names and returns all entities that contains for each name in attributes at least one attribute with that name. Although this sounds very straight forward, the only solution I found was to execute a query for each attribute name and merge the results like this:
for (String name : attributeNames) {
CriteriaQuery<MyEntity> cq = cb.createQuery(MyEntity.class);
Root<MyEntity> entity = cq.from(MyEntity.class);
Join<MyEntity, EntityAttribute> attributeJoin = entity.join(MyEntity_.attributes);
cq.where(attributeJoin.get(EntityAttribute_.name).equals(name));
cq.select(entity);
... // get result list and merge
}
This code isn't tested but generally is one solution. This doesn't seem to be the most efficient one.
Another solution I testet was to use multiple joins like
CriteriaQuery<MyEntity> cq = cb.createQuery(MyEntity.class);
Root<MyEntity> entity = cq.from(MyEntity.class);
List<Predicate> predicates = new ArrayList<>();
for (String name : attributeNames) {
Join<MyEntity, EntityAttribute> attributeJoin = entity.join(MyEntity_.attributes);
predicates.add(attributeJoin.get(EntityAttribute_.name).equals(name));
}
cq.select(predicates.toArray(new Predicate[] {}));
... // get result list
This seems to be more efficient, but it iterates over the cartesian products... So it's highly inefficient.
I could also imagine to nest subqueries, but this seems to be very complicated.
The question simply is: What is the best solution for this problem? Afterwards I would also like to implement AND and OR, so I can query for all entities with attributes x and (y or z) or something like that. But for now I only want to make the AND case.
Thanks in advance
Maybe you could achieve this using in clause + group by + having + count, if I understand your question correctly. The idea is to count the number of matches for each MyEntity. If the count is equal to the number of attributes passed in, it means that each of them was found for that entity (assuming they are unique). In JPQL the query would look like this:
select e from MyEntity e join e.attributes a
where a.name in (:attributeNames)
group by e having count(*) = :attributeCount
where :attributeCount is the value of attributeNames.size().
I'm not very familiar with the criteria API, but you can experiment with something like this:
...
cq.groupBy(entity);
cq.having(cb.equal(cb.count(entity), attributeNames.size()));
// TODO: add IN clause
...
I have a object A which maps to table A in DB
class A {
Integer id;
String field2,field2;field3 ,... fieldN;
//lots of other attribute
}
Now i want to write a DAO api that just updates a single field.One approach is that i can first load the object then changes the attribute i need and then use merge api
//start transcation
A a = session.load(A.class, id);
A.setfieldP(newValue)
session.merge(A)
//commit transcation
Now if i use following code
//start transcation
A a = new A();
a.setId(id); //set a id by which object A exists in DB
A.setfieldP(newValue)
session.merge(A)
//commit transaction
Now second approach all fields except id and fieldP are set to null
1)Now is there any other approach?
2)Can i use update instead of merge ?
If you need to update lots of entities at once the most efficient way is to use a query:
Query query = session.createQuery("update EntityName set fieldP = 'newValue' "
+ "where id IN (75, 76)");
query.executeUpdate();
This allows you to change field values without loading the entity or entities into memory.
It is best practice is to use named queries and named parameters - the above implementation is just an example.
I usually prefer session.get vs session.load, as session.get will return null as opposed to throwing an exception, but it depends on the behavior you want.
loading the object, setting your field, and calling either
session.merge(myObject)
is the standard way, although you can also use
session.saveOrUpdate(myObject)
as long as the object hasn't been detached, which in your case, it won't have been detached. Here is a good article explaining the differences in merge and saveOrUpdate.
In your second example, you are editing the primary key of the object? This is generally bad form, you should delete and insert instead of changing the primary key.
Using JPA you can do it this way.
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaUpdate<User> criteria = builder.createCriteriaUpdate(User.class);
Root<User> root = criteria.from(User.class);
criteria.set(root.get("fname"), user.getName());
criteria.set(root.get("lname"), user.getlastName());
criteria.where(builder.equal(root.get("id"), user.getId()));
session.createQuery(criteria).executeUpdate();
One more optimization here could be using dynamic-update set to true for the entity. This will make sure that whenever there is an update, only field(s) which are changed only gets updated.
I have the following section of code that I want to use to return a collection of my object:
Session session = HibernateUtil.getSession();
List<MyObj> myObjList = (List<MyObj>)
session.createCriteria(MyObj.class)
.add(Restrictions.eq("searchField", searchField)).list();
Iterator<MyObj> myObjIt = myObjList.listIterator();
log.debug("list size: " + myObjList.size());
while(myObjIt.hasNext()){
MyObj myObj = myObjIt.next();
log.debug(myObj.getMyField());
}
However, my log keeps printing the same record as many times as the size of the list. If I refactor slightly, my code works correctly like this:
SQLQuery query = session.createSQLQuery(
"select my_field from my_table where search_field = :searchField"
);
query.setParameter("myField", myField);
List result = query.list();
for(Iterator iter = result.iterator(); iter.hasNext();){
Object[] row = (Object[]) iter.next();
log.debug(row[0]);
}
Am I doing something wrong in my first code segment? I should be able to go about this either way, and since I'm using Hibernate, I'd rather the ORM be working as expected, so I'd prefer the former method over the latter. Anyone have any thoughts?
Fwiw, I am using Hibernate 3.5.4 final, Hibernate-validator 4.2.0 Final, hibernate-search 3.4.0 Final, and hibername-c3p0 3.6.5 final, all from the maven repos.
Edited to clarify based on comments.
From what you have described in the question, your both code segments should return the same results. Assuming that in first code segment Hibernate executes pretty the same query as in second segment (you can check it in log, just enable 'hibernate.show_sql' config parameter) - problem is somewhere in converting result set to MyObj list. It is pretty unlikely that it happens due to a bug in hibernate, so it can be due to incorrect entity class mapping. If you do not see any problems with mapping, please add more details to the question (your entity class with mappings, db table schema and data sample) so anyone can reproduce the issue.
Likely your MyObj class does not have Id column mapping properly defined. For example if the field/property mapped as Id has the same value for all the objects in the result list, hibernate will return same objects (as in your case).
Regarding using primitives as Id type: hibernate allow using primitive types, but it has the following line in the docs:
We recommend that you declare consistently-named identifier properties on persistent classes and that you use a nullable (i.e., non-primitive) type.
Summarizing possible Id mapping issues:
1. Id mapped column is not unique in db.
2. Corresponding setter for Id property getter mapped is not specified or does not really save passed argument value.
3. Not-nullable type of Id field.