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.
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.
I am following the code for the running SQL queries in the Ignite cache, but am able to fully realize the use of the CacheConfiguration.setIndexedTypes API.
I am following the only help that I could find at the ignite site.
The documentation here says to use
CacheConfiguration.setIndexedTypes(MyKey.class, MyValue.class).
Now lets say in the Person class
#QuerySqlField(index = true)
private long id;
#QuerySqlField
private String firstName;
Which are the parameters that I should be passing in the setIndexedType method?
setIndexedTypes takes an even number of parameters. Every odd parameter corresponds to a key type, and every even - to a value type. In your case you should probably use id parameter as a key, so you should call it this way:
cacheConfig.setIndexedTypes(Long.class, Person.class);
Javadoc for setIndexedTypes method contains a pretty good explanation of this method: https://ignite.apache.org/releases/latest/javadoc/org/apache/ignite/configuration/CacheConfiguration.html#setIndexedTypes(java.lang.Class...)
UPD:
There will be registered a table in SQL for each pair of parameters that you provide to setIndexedTypes method.
Your SQL entities will map to cache records and they will have _key and _val columns in addition to the ones that you configured as QuerySqlField-s. So, you should specify types of keys and values that will be used in cache for each table.
You can refer to this page for more information: https://apacheignite.readme.io/docs/dml#basic-configuration
In your case it will be
cacheConfig.setIndexedTypes(KeyType.class, Person.class)
where KeyType is the type you use for keys while calling cache.put(key, person) or insert into Person(_key, ...) ...
Please refer to this documentation section
I'm using Hibernate Envers in my app to track changes in all fields of my entities.
I'm using #Audited(withModifiedFlag=true) annotation to do it.
The records are been correcty recorded at database and the _mod fields correctly indicate the changed fields.
I want to get a particular revision from some entity and the information of what fields have been changed. I'm using the follow method to do it:
List<Object[]> results = reader.createQuery()
.forRevisionsOfEntity(this.getDao().getClazz(), false, true)
.add(AuditEntity.id().eq(id))
.getResultList();
This method returns an list of an object array with my entity as first element.
The problem is that the returned entity doesn't have any information about the changed fields. So, my question is: how to get the information about the changed fields?
I know that this question is a bit old now but I was trying to do this and didn't really find any answers.
There doesn't seem to be a nice way to achieve this, but here is how I went about it.
Firstly you need to use projections, which no longer gives you a nice entity model already mapped for you. You'll still get back an array of Objects but each object in the array corresponds to each projection that you added (in order).
final List<Object[]> resultList = reader.createQuery()
.forRevisionsOfEntity(this.getDao().getClazz(), false, true)
// if you want revision properties like revision number/type etc
.addProjection(AuditEntity.revisionNumber())
// for your normal entity properties
.addProjection(AuditEntity.id())
.addProjection(AuditEntity.property("title")) // for each of your entity's properties
// for the modification properties
.addProjection(new AuditProperty<Object>(new ModifiedFlagPropertyName(new EntityPropertyName("title"))))
.add(AuditEntity.id().eq(id))
.getResultList();
You then need to map each result manually. This part is up to you, but I'm use a separate class as a revision model as it contains extra data to the normal entity data. If you wanted you could probably achieve this with #Transient properties on your entity class though.
final List<MyEntityRevision> results = resultList.stream().map(this::transformRevisionResult)
.collect(Collectors.toList());
private MyEntityRevision transformRevisionResult(Object[] revisionObjects) {
final MyEntityRevision rev = new MyEntityRevision();
rev.setRevisionNumber((Integer) revisionObjects[0]);
rev.setId((Long) revisionObjects[1]);
rev.setTitle((String) revisionObjects[2]);
rev.setTitleModified((Boolean) revisionObjects[3]);
return rev;
}
I have a method in Dao Class that returns List<Object[]> back and I am using named Query
public List<Object[]> getListByCustomer(Session session, int customerId, List<Integer> strIds) {
Query namedQuery = session.createSQLQuery(QueryConstants.EXPORT);
namedQuery.setParameter("customer", customerId);
namedQuery.setParameter("stringId", strIds);
List<Object[]> objects = namedQuery.list();
return objects;
}
I want to pass List<Integer> strIds in stringId into the named query as follows :
public class QueryConstants {
public static final String EXPORT =
"SELECT sv.NAME, sv.TYPE, sv.CLIENT_ADDRESS, sv.NAME_REDUNDANT, sv.DEPARTURE_DATE, s1.CODE,sv.STATE, sv.CODE "
+ "FROM VIEW sv, PROCESS p1, SET s1 "
+ "WHERE sv.R_ID = p1.R_ID and p1.ISSUER_ID = s1.USER_ID and sv.CUSTOMER_ID = :customer and sv.R_ID IN (:stringId)";
}
But I get ORA-00932: inconsistent datatypes: expected NUMBER got BINARY.
Also when I remove sv.R_ID IN (:stringId) from the query it works fine and
when I pass Integer(strIds) instead of List<Integer> strIds into the query it works fine.
I'm using Oracle 10g.
This is a very misleading error, and may root from different causes, for me I was setting a parameter that it was supposedly a number but at runtime it was setting null, hence it was binary. On a separate occasion got this error due to bean creation error in spring and was not setting the parameter correctly as well.
I think you just need to use
IN :stringId
instead of
IN (:stringId)
For JPA
namedQuery.setParameter("stringId", strIds);
is correct, but for Hibernate you should use
namedQuery.setParameterList("stringId", strIds);
I encountered this same exception and found the below reason for that -
In my entity, a field was mapped to a custom object (Parent child relationship - #ManyToOne). Later, the relationship annotation was removed by developer but the datatype was not changed.
After removing the #ManyToOne annotation, the #Column annotation should have been used with appropriate data type (Integer).
Case your param is list. If list is empty then raise error, you must check that list not empty to avoid error.
Case your param is single value. Let use TO_NUMBER(:your_param) to avoid error.
It working on me.
In my case, I was using HQL (in the repository of spring data) with an Entity mapped with #Enumerated (ORDINAL). I was trying to use the the enum object directly in the where clausule. The solution was to use TO_NUMBER(:your_param) as mentioned by the member above.
I got the same error but for a different reason. In my case it was due to the order the parameters were supplied being different from the order defined in the query. I assumed (wrongly) that because the parameters were named the order didn't matter. Sadly, it seems like it does.
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.