I have a problem with lazy initialisation. I can't find a solution.
Exception:
[pool-1-thread-12] ERROR:12:20:14.840 o.h.LazyInitializationException - failed to lazily initialize a collection of role: de.beeld.forges.domain.Server.applications, no session or session was closed
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: de.beeld.forges.domain.Server.applications, no session or session was closed
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380)
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:372)
[pool-2-thread-1] ERROR:12:20:14.840 o.s.s.support.MethodInvokingRunnable - Invocation of method 'readStatusCache' on target class [class de.beeld.forges.task.annotation.ScheduledProcessor$$EnhancerByCGLIB$$ee649dc3] failed
java.util.ConcurrentModificationException: null
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
hibernate.xml
<!-- Hibernate SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
p:dataSource-ref="standardDataSource" p:lobHandler-ref="defaultLobHandler">
<property name="annotatedClasses">
<list>
<value>de.beeld.forges.domain.Server</value>
<value>de.beeld.forges.domain.Application</value>
<value>de.beeld.forges.domain.Forge</value>
</list>
</property>
</property>
</bean>
<bean id="defaultLobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" />
<!-- Read in DAOs from the hibernate package -->
<context:component-scan base-package="de.beeld.forges.dao" />
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory" />
<bean id="transactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<context:component-scan base-package="de.beeld">
<context:exclude-filter expression="org.springframework.stereotype.Controller"
type="annotation" />
<context:exclude-filter expression="org.springframework.stereotype.Repository"
type="annotation" />
</context:component-scan>
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
readStatusCache method:
public void readStatusCache() {
String execCommand = "java -jar ...";
List<Future<Map<Long, Integer>>> list = new ArrayList<Future<Map<Long, Integer>>>();
String serverName = null;
for (Server server : serviceFacade.getServers()) {
serverName = server.getName();
Callable<Map<Long, Integer>> worker = new ApplicationStatusReader2(server.getApplications(),
sshConnector, execCommand, serverName);
Future<Map<Long, Integer>> submit = this.serviceFacade.getExecutor().submit(worker);
list.add(submit);
}
for (Future<Map<Long, Integer>> future : list) {
//do stuff
}
}
Server.java
#Entity
#org.hibernate.annotations.Entity(dynamicUpdate = true)
public class Server implements DomainObject, Comparable<Server> {
private static final long serialVersionUID = -8920952435734596243L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(unique = true, nullable = false)
#NotEmpty
private String name;
#Column(nullable = false)
#NotEmpty
#Pattern(regexp = "^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])$", message = "The ip must be in format xxx.xxx.xxx.xxx")
private String ip;
#Column(nullable = false)
#NotEmpty
private String fqdn;
#OneToMany(mappedBy = "server", fetch = FetchType.LAZY)
private List<Application> applications;
#Version
private int version;
//getter and setter
}
Application.java
#Entity
public class Application implements DomainObject {
private static final long serialVersionUID = -8127137156319959239L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
private Server server;
#Column(nullable = false)
#NotEmpty
private String name;
#Column(nullable = false)
#NotEmpty
private String location;
#Column(nullable = false)
#NotEmpty
private String binDir;
private String confDir;
private boolean isContainer = false;
private String containerDir;
private String startup = "startup.sh";
private String shutdown = "shutdown.sh";
#ManyToOne(fetch = FetchType.LAZY)
#Fetch(FetchMode.JOIN)
private Forge forge;
#ManyToOne(fetch = FetchType.LAZY)
#Fetch(FetchMode.JOIN)
private Application parent;
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
private List<Application> offsprings;
#NotEmpty
private String blueprint;
private Integer replaceable = 0;
private Integer running = 0;
#Version
private int version;
//getter and setter
}
I don't really know why I cant read the list of applications from the server
If anybody could help it would be great.
Most likely because you're setting applications collection to lazy load. So when you return from the initial call to serviceFacade.getServers(), my guess is that you no longer have the session opened that was used to fetch the list of servers.
As a result, when you iterator through the applications, the session was closed, so it can't load the actual contents of the collection.
You have three possibilities:
Keep the session open (google for binding hibernate session to thread, or if the readStatusCache method is in a spring managed object, just add an #Transactional annotation to it, or the entry point from which it is called).
Change the applications collection to be an eager load
have your dao layer fully initialize the applications collection (see Hibernate.initialize method) while the session is still open.
The two loops
for (Server server : serviceFacade.getServers()) {
serverName = server.getName();
Callable<Map<Long, Integer>> worker = new ApplicationStatusReader2(server.getApplications(),
sshConnector, execCommand, serverName);
Future<Map<Long, Integer>> submit = this.serviceFacade.getExecutor().submit(worker);
list.add(submit);
}
for (Future<Map<Long, Integer>> future : list) {
//do stuff
}
are being called by two separate threads simultaneously. You can check if this is the case by synchronizing the readStatusCache() method. But this should be done in the Spring layer. Are you using transactions etc. properly?
Please read this answer for more on thread safety with spring and hibernate. Spring-Hibernate used in a webapp,what are strategies for Thread safe session management
When I got the same exception, the solution was to add the #Transactional annotation to the method in the controller.
Related
I'm kinda new with the Spring's transaction management. I think I'm missing some configuration but I can't deal with it.
The error is that I'm getting failed to lazily initialize a collection of role exception.
I'm using Spring Data for my DAO. Also, I know about setting the fetchType to Eager but this is what I'm trying to avoid.
DAO:
public interface CourseDao extends CrudRepository<CourseEntity, Long> {
CourseEntity findByName(String name);
}
Service:
#Service
public class CourseMaterialSearchService {
private final CourseDao courseDao;
private final CourseMaterialEntityTransformer courseMaterialEntityTransformer;
#Autowired
public CourseMaterialSearchService(CourseDao courseDao, CourseMaterialEntityTransformer courseMaterialEntityTransformer) {
super();
this.courseDao = courseDao;
this.courseMaterialEntityTransformer = courseMaterialEntityTransformer;
}
#Transactional
public List<CourseMaterial> findMaterialsFor(final Long courseId) {
final CourseEntity entity = courseDao.findOne(courseId);
final List<CourseMaterialEntity> materials = entity.getCourseMaterialEntityList();
return courseMaterialEntityTransformer.transformEntities(materials);
}
}
And my application-context.xml is (of course this is just the relevant part):
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="example" />
<property name="dataSource" ref="exampleDataSource" />
<property name="packagesToScan" value="com.example.example.**.repository" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
</bean>
</property>
</bean>
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
When I call the findMaterialsFor method in CourseMaterialSearchService, the exception occurs. How should I solve this?
Any suggestions will be appreciated.
Thank you guys.
Stacktrace:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.example.example.course.repository.domain.CourseEntity.courseMaterialEntityList, could not initialize proxy - no Session
org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:575)
org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:214)
org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:554)
org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:142)
org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:294)
com.example.example.course.service.transform.CourseMaterialEntityTransformer.transformEntities(CourseMaterialEntityTransformer.java:15)
com.example.example.course.service.CourseMaterialSearchService.findMaterialsFor(CourseMaterialSearchService.java:29)
CourseMaterialEntityTransformer:
public class CourseMaterialEntityTransformer {
public List<CourseMaterial> transformEntities(final Iterable<CourseMaterialEntity> entities) {
final List<CourseMaterial> result = new ArrayList<>();
for (final CourseMaterialEntity entity : entities) {
result.add(transformEntity(entity));
}
return result;
}
public CourseMaterial transformEntity(final CourseMaterialEntity entity) {
final CourseMaterial result = new CourseMaterial();
result.setId(entity.getId());
result.setName(entity.getName());
result.setCourseName(entity.getCourseEntity().getName());
result.setCurrentFileName(entity.getCurrentFileName());
result.setOriginalFileName(entity.getOriginalFileName());
result.setCategory(entity.getMaterialCategoryEntity().getName());
result.setUploader(entity.getUserEntity().getName());
return result;
}
}
com.example.example.course.service.transform.CourseMaterialEntityTransformer.transformEntities(CourseMaterialEntityTransformer.java:15):
for (final CourseMaterialEntity entity : entities) {
CourseEntity:
#Entity(name = "courses")
public class CourseEntity {
#Id
#GeneratedValue
private Long id;
private String name;
#OneToMany(mappedBy = "courseEntity")
private List<CourseMaterialEntity> courseMaterialEntityList;
public Long getId() {
return id;
}
public void setId(final Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public List<CourseMaterialEntity> getCourseMaterialEntityList() {
return courseMaterialEntityList;
}
public void setCourseMaterialEntityList(final List<CourseMaterialEntity> courseMaterialEntityList) {
this.courseMaterialEntityList = courseMaterialEntityList;
}
}
Spring applies transactions using AOP. AOP on beans is only applied to beans in the same application context.
You have a <tx:annotation-driven /> in your application-context.xml, which is loaded by the ContextLoaderListener. This file contains a <context:component-scan /> which detects the #Service.
However if you also have the same <context:component-scan /> in the file loaded by the DispatcherServlet, or at least if the same #Service is detected it results in another instance of this service which will not have AOP applied.
As a general rule-of-thumb you want to load everything BUT #Controllers in your ContextLoaderListener and only web related things (view resolvers, #Controllers) in your DispatcherServlet.
See context depended scan-component filter.
You are trying to access a lazy-loaded attribute outside of a transaction. The object then has no connection with the EntityManager (database), since it's outside of a transaction, and thus cannot go fetch the lazy-loaded attribute.
By default Collections are lazy-loaded . You can replace below your code :
#OneToMany(mappedBy = "courseEntity")
private List<CourseMaterialEntity> courseMaterialEntityList;
With :
#OneToMany(fetch = FetchType.EAGER, mappedBy = "courseEntity", cascade = CascadeType.ALL)
private List<CourseMaterialEntity> courseMaterialEntityList;
You need to change #OneToMany(mappedBy = "courseEntity") to #OneToMany(mappedBy = "courseEntity", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
The default fetch type is lazy so when you try and access the collection it hasn't been loaded from the database.
I am using EclipseLink JPA as ORM and web logic 10.3 as an application server in my project. Everything working fine until i got a bug for data refresh. Here is the case one of my entity table row is updated to new value but my entity manager or JPA did not pick that value. For this we have lite rely re started the server. then it picked up the value.
Here is my persistence.xml file and here is the way i am using entity manager in my class.
<persistence-unit name="BasePersistenceUnit" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/CTH_DS</jta-data-source>
<class>org.test.partyrequest.model.dataobject.RqstTrc</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="eclipselink.target-server" value="WebLogic_10" />
<!-- Logging level is set to INFO, Need to change in Production -->
<property name="eclipselink.logging.level" value="FINE" />
<property name="eclipselink.persistence-context.flush-mode" value="COMMIT" />
<property name="eclipselink.persistence-context.close-on-commit" value="true" />
<property name="eclipselink.cache.shared.default" value="false" />
</properties>
</persistence-unit>
SPRING JPA XML FILE
<context:load-time-weaver aspectj-weaving="on" />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="BasePersistenceUnit" />
</bean>
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter" />
<bean id="transactionManager" class="org.springframework.transaction.jta.WebLogicJtaTransactionManager" />
<!-- ========================= BUSINESS OBJECT DEFINITIONS ========================= -->
<!-- Instruct Spring to perform declarative transaction management automatically on annotated classes. -->
<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>
<!-- Post-processor to perform exception translation on #Repository classes
(from native exceptions such as JPA PersistenceExceptions to Spring's DataAccessException hierarchy).
-->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
My Entity calss
#Entity
#Table(name = "PRTY_RQST")
public class PrtyRqst implements Serializable {
private static final long serialVersionUID = -4679712398918736694L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "PRTY_RQST_PRTYRQSTID_GENERATOR")
#SequenceGenerator(name = "PRTY_RQST_PRTYRQSTID_GENERATOR", allocationSize = 1, sequenceName = "PRTY_RQST_SEQ")
#Column(name = "PRTY_RQST_ID")
private Long prtyRqstId;
#Column(name = "CHLD_RQST_IND")
private String chldRqstInd;
#Column(name = "PARNT_PRTY_RQST_ID")
private BigDecimal parntPrtyRqstId;
#Column(name = "PROCES_REFR")
private String procesRefr;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "RQST_DT_TM")
private Date rqstDtTm;
#Column(name = "UPDT_BY")
private String updtBy;
// bi-directional many-to-one association to PrtyKey
#OneToMany(mappedBy = "prtyRqst", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<PrtyKey> prtyKeys;
// bi-directional many-to-one association to PrtyRqstHist
#OneToMany(mappedBy = "prtyRqst", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#OrderBy("rqstDtTm DESC")
private List<PrtyRqstHist> prtyRqstHists;
#OneToOne(mappedBy = "prtyRqst", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private RqstPayload rqstPayload;
// bi-directional many-to-one association to RqstTrc
#OneToMany(mappedBy = "prtyRqst", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<RqstTrc> rqstTrcs;
// bi-directional many-to-one association to AddtnRqstInfo
#OneToMany(mappedBy = "prtyRqst", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<AddtnRqstInfo> addtnRqstInfos;
// bi-directional many-to-one association to BusnApplc
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
#JoinColumn(name = "BUSN_APPLC_ID")
private BusnApplc busnApplc;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
#JoinColumn(name = "INTN_PROCES_TYP_ID")
private IntnProcesTyp intnProcesTyp;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
#JoinColumn(name = "INTN_STATS_ID")
private IntnStat intnStat;
#Column(name = "ORCHESTRATION_ID")
private String orchestrationId;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
#JoinColumn(name = "ALLW_CHNL_ID")
private AllwChnl allwChnl;
// bi-directional many-to-one association to RqstTyp
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
#JoinColumn(name = "RQST_TYP_ID")
private RqstTyp rqstTyp;
#Column(name = "TRACK_RQST_IND")
private String trackRqstInd;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "SUBMIT_DT_TM")
private Date submitDtTm;
#Temporal(TemporalType.DATE)
#Column(name = "EFFECTIVE_DT")
private Date effectiveDt;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "RQST_CREATE_DT_TM")
private Date rqstCreateDtTm;
In my DAO IMPL class I have this.persist(prtyRqstDO);
#Transactional(readOnly = true, propagation=Propagation.REQUIRED)
private PartyRequestBO createRequest(PartyRequestBO partyRequestBO, boolean isParent) throws RuntimeException {
if (log.isDebugEnabled()) {
log.debug("Enter: PartyRequestsDAOImpl:createRequest()");
}
partyRequestBO.setOrchestrationID(generateOrchestrationId());
PrtyRqst prtyRqstDO = PartyRequestEntityMapper.partyRequestMapper(partyRequestBO, isParent, true);
try {
this.persist(prtyRqstDO);
partyRequestBO.setRequestIdentifier(prtyRqstDO.getPrtyRqstId());
} catch (Exception e) {
if(log.isDebugEnabled()) {
log.debug("PartyRequestsDAOImpl:createRequest : " + PartyRequestConstants.UNABLE_TO_INSERT, e);
}
throw new PartyRequestDataException(PartyRequestConstants.UNABLE_TO_INSERT, e);
}
if (log.isDebugEnabled()) {
log.debug("Exit: PartyRequestsDAOImpl:createRequest()");
}
return partyRequestBO;
}
#Transactional(readOnly = true, propagation=Propagation.REQUIRED)
public void persist(T entity) {
if (log.isDebugEnabled()) {
log.debug("Enter: BaseDAO:persist() : " + entity);
}
this.getEntityManager().persist(entity);
if (log.isDebugEnabled()) {
log.debug("Exit: BaseDAO:persist()");
}
}
public EntityManager getEntityManager() {
if (log.isDebugEnabled()) {
log.debug("Enter: BaseDAO:getEntityManager() : " + this.entityManager);
}
return this.entityManager;
}
Here the problem is, if i update the row in one of the table through back end my application container is not picking the change.
Can any one Tell me? thank you in advance.
EDIT:
Thank you both of you. I have modified according to your comments added following lines of code.
this.entityManager.clear();
this.entityManager.close();
//this.getEntityManager().refresh(entityManager);
Here i could able to get the update value what i have done it through backend with out restarting server. But the problem is it hold all the changed values.
for example i have changed value to FulOrderWSA it was working. changed to FulorderWSB it was working again.Now i have tried for FulOrderWSZ it didn't work(DB values is FulorderWSB ).
Finally i tried here with old value that is FulorderWSA as per DB it should not work but it worked for me. what i noticed that it is holding all the DB changed values here.
How to get ride of this. I have used both clear and close for entityManager. can any one help me on this.
thank you.
Vijay.
You have turned off the EclipseLink shared cache (AKA second level cache), so the issue is likely that you are holding onto long lived EntityManagers. Once an entity becomes managed by the EM, JPA requires that EM to return that exact instance from every find/query operation as it was when first read and any changes your app might have made to it.
There are a number of options. The best is to look at your EntityManager lifecycle and only obtain an EntityManager when needed, and close it when done. Or, just call em.clear() at points to prevent them from filling up, which will detach all entities associated to the em. Make sure to flush changes though if you wish to keep the changes before calling clear.
If there is a specific entity you need to refresh, em.refresh(entity) will work. This will clear any changes the application might have made though, and can be dangerous with cascade refresh settings mixed with lazy access - so use carefully or you may unintentionally wipe out changes to a whole tree at a later time.
You have caching disabled, so you should see any database changes.
My guess is that you are hold onto a single EntityManager in your DAO. This is very bad, as an EntityManger should be created per transaction, or per request, not held for the duration of the application. It is also not thread safe, so holding onto a single one does not make sense, as it is a transactional object.
You seem to also be using Spring, so it might be proxying the EntityManager underneath and creating one per transaction, but perhaps you have not configured Spring or your transactions correctly.
Include the code the creating/configures the EntityManager.
Thanks,
for all your support. Basically we are not using Cache from EclipseLink. we had bean that handles all the metadata initialization as init method. what we have done is, used JDK Timer to reload the particular Bean to refresh the data. It was working fine.
I have checked the time taking to refresh the all the methods are less than 500 milliseconds. I can foresee only issue when this thread is executing and there is a request.since it is taking less than 500 millisecs its ok for us.
I hope this will be helpful if someone is not using cache you can try this approach.
Thank you.
I have created a sample web application. I am using MS SQL Server 2008 as database and hibernate + annotations to access the database. My hibernate configuration xml is as below.
The problem is, the Criteria.list() returns an empty list, and also I am seeing a '?' in the generated HSQL instead of the parameter I am passing in the Criteria.
<session-factory name="">
<property name="hibernate.connection.driver_class">sun.jdbc.odbc.JdbcOdbcDriver</property>
<property name="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</property>
<property name="hibernate.connection.url">jdbc:odbc:dbname</property>
<property name="connection.pool_size">10</property>
<property name="current_session_context_class">thread</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<property name="hibernate.connection.username"></property>
<property name="hibernate.connection.password"></property>
<mapping class="com.demo.Person" />
</session-factory>
This is my annotated bean
#Entity
#Table(name = "person")
public class Person implements Serializable {
public Person(){
}
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Basic(optional = false)
#Column(name = "personid")
private Integer personid;
#Basic(optional = false)
#Column(name = "firstname")
private String firstname;
#Column(name = "lastname")
private String lastname;
#Basic(optional = false)
#Column(name = "phone")
private String phone;
#Column(name = "mobile")
private String mobile;
#Column(name = "street")
private String street;
#Basic(optional = false)
#Column(name = "city")
private String city;
#Basic(optional = false)
#Column(name = "country")
private String country;
#Basic(optional = false)
#Column(name = "bussinessowner")
private int bussinessowner;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "resultid1")
private Collection<Recent> recentCollection;
//setters & getters
}
And the code I am running is
Session session;
List list = new ArrayList();
try{
session = HibernateUtil.getSessionFactory().openSession();
Criteria criteria = session.createCriteria(Person.class);
criteria.add(Restrictions.like("firstname", name, MatchMode.START));
list= criteria.list();
System.out.println(list.size());
}catch (Exception e) {
e.printStackTrace();
}
Generated HSQL :
Hibernate: select this_.personid as personid2_0_, this_.city as city2_0_, this_.country as country2_0_, this_.firstname as firstname2_0_, this_.lastname as lastname2_0_ from person this_ where this_.firstname like ?
Other than this I am not getting any exception either. Can you please help me out with this.
Thanks !
Does changing it to criteria.add(Restrictions.like("firstname", name + "%")); help?
And, by the way, ? is correct parameter placeholder for jbdc statements.
'?' is the place holder for parameters in JDBC statements. You can't see its actual value, that's all. There is no problem with it, just the tracing with hibernate remains incomplete.
If you want to see the actual value for '?', then you have to use an extra product like log4jdbc.
#Rakesh Try below code:
criteria.add(Restrictions.ilike("firstname", name +"%"));
Instead of this line
criteria.add(Restrictions.like("firstname", name, MatchMode.START));
try
criteria.add(Restrictions.like("firstname", name, MatchMode.ANYWHERE));
OR
criteria.add(Restrictions.ilike("firstname", name, MatchMode.ANYWHERE));
It will gerenerate SQL as firstname like '%abcd%' , if you pass firstname as "abcd"
Can you simply try this
session = HibernateUtil.getSessionFactory().openSession();
List<Person> personList = session.createQuery("Select * from Person p where p.firstname like 'rake%'").list();
System.out.println("Person result :" + personList .size());
Basically, how would I make it so that I can add a new TestEntity to the test set after the person has already been created? Also, how can I add a person that has a collection of TestEntity? I'm new to Hibernate so I feel I must be missing something since this would seem like a very common use case.
Some things I've tried:
Attempt #1:
PersonEntity person = createPerson("username");
TestEntity test = new TestEntity();
test.setTestId("2342");
test.setTestName("test name");
personDao.add(person);
person.addTest(test);
This results in the person being saved but no test information. Switching add and addTest does not change anything.
Attempt #2:
Adding a method like this to my Dao (based on http://docs.jboss.org/hibernate/core/3.3/reference/en/html/example-parentchild.html):
public void addTest(String personId, TestEntity test)
{
PersonEntity entity = (PersonEntity) getHibernateTemplate().getSessionFactory().getCurrentSession().load(PersonEntity.class, personId);
if (entity != null)
{
test.setPerson(entity);
entity.getTest().add(test);
getHibernateTemplate().getSessionFactory().getCurrentSession().save(entity);
getHibernateTemplate().getSessionFactory().getCurrentSession().flush();
}
}
And calling like this:
personDao.add(person);
personDao.addTest("username", test);
However, I get this error: org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
Attempt #3:
Added #Transaction annotation to my dao and entity classes and added the following config to my app context:
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<!-- org.springframework.transaction.jta.JtaTransactionManager org.springframework.jdbc.datasource.DataSourceTransactionManager -->
<property name="dataSource" ref="dataSource" />
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven />
Now, using that method I created in attempt #2 and calling it in the same way, I get a stackoverflow error.
Edit Update: However, if I remove the test set from my hashCode method in PersonEntity, it works. I can also use person.addTest(test) and that will take care of adding a collection to the person entity before persisting the person entity. However, this really doesn't seem like it would be the best way to do it, no? What would be the best way to make this work? That dao method I added seems like it would be making more calls than necessary?
My classes:
PERSON
#Entity
#Table(name = "PERSON")
public class PersonEntity implements Serializable
{
private static final long serialVersionUID = -1699435979266209440L;
#Id
#Column(name = "PERSON_ID", length = 25, nullable = false)
private String personId;
#LazyCollection(value = LazyCollectionOption.FALSE)
#Cascade(CascadeType.ALL)
#OneToMany(targetEntity = TestEntity.class, mappedBy = "person")
#Where(clause="1=1")
private Set<TestEntity> test;
public void addTest(TestEntity testEntity)
{
testEntity.setPerson(this);
test.add(testEntity);
}
}
TEST
#Entity
#Table(name = "TEST")
public class TestEntity implements Serializable
{
private static final long serialVersionUID = -6524488155196023818L;
#Id
#Column(name = "TEST_ID", length = 36, nullable = false)
private String testId;
#ManyToOne
#Cascade(CascadeType.ALL)
#Index(name = "TEST_PERSON_ID_INDEX")
#JoinColumn(name = "PERSON_ID")
#ForeignKey(name = "FKT1_PERSON_ID")
private PersonEntity person;
#Column(name = "TEST_NAME", length = 60, nullable = false)
private String testName;
}
PersonDaoHibernate
public class PersonDaoHibernate extends HibernateDaoSupport implements PersonDao
{
public String add(PersonEntity person)
{
getHibernateTemplate().merge(person);
return person.getPersonId();
}
public void delete(String id)
{
Object entity = getHibernateTemplate().get(PersonEntity.class, id);
if (entity != null)
{
getHibernateTemplate().delete(entity);
}
}
public PersonEntity getById(String id)
{
return getHibernateTemplate().get(PersonEntity.class, id.toUpperCase());
}
}
I think you will have to change the method name to setTest(...), since hibernate follows java bean convention while trying to do the operation on properties.
Change that and I hope it should work fine.
Rest of code looks fine.
I need help with lazy blob loading in Hibernate.
I have in my web application these servers and frameworks: MySQL, Tomcat, Spring and Hibernate.
The part of database config.
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="driverClass" value="${jdbc.driverClassName}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="initialPoolSize">
<value>${jdbc.initialPoolSize}</value>
</property>
<property name="minPoolSize">
<value>${jdbc.minPoolSize}</value>
</property>
<property name="maxPoolSize">
<value>${jdbc.maxPoolSize}</value>
</property>
<property name="acquireRetryAttempts">
<value>${jdbc.acquireRetryAttempts}</value>
</property>
<property name="acquireIncrement">
<value>${jdbc.acquireIncrement}</value>
</property>
<property name="idleConnectionTestPeriod">
<value>${jdbc.idleConnectionTestPeriod}</value>
</property>
<property name="maxIdleTime">
<value>${jdbc.maxIdleTime}</value>
</property>
<property name="maxConnectionAge">
<value>${jdbc.maxConnectionAge}</value>
</property>
<property name="preferredTestQuery">
<value>${jdbc.preferredTestQuery}</value>
</property>
<property name="testConnectionOnCheckin">
<value>${jdbc.testConnectionOnCheckin}</value>
</property>
</bean>
<bean id="lobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" />
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="/WEB-INF/hibernate.cfg.xml" />
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
</props>
</property>
<property name="lobHandler" ref="lobHandler" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
The part of entity class
#Lob
#Basic(fetch=FetchType.LAZY)
#Column(name = "BlobField", columnDefinition = "LONGBLOB")
#Type(type = "org.springframework.orm.hibernate3.support.BlobByteArrayType")
private byte[] blobField;
The problem description. I'm trying to display on a web page database records related to files, which was saved in MySQL database. All works fine if a volume of data is small. But the volume of data is big I'm recieving an error java.lang.OutOfMemoryError: Java heap space
I've tried to write in blobFields null values on each row of table. In this case, application works fine, memory doesn't go out of. I have a conclusion that the blob field which is marked as lazy (#Basic(fetch=FetchType.LAZY)) isn't lazy, actually!
I'm confused. Emmanuel Bernard wrote in ANN-418 that #Lob are lazy by default (i.e. you don't even need to use the #Basic(fetch = FetchType.LAZY) annotation).
Some users report that lazy loading of a #Lob doesn't work with all drivers/database.
Some users report that it works when using bytecode instrumentation (javassit? cglib?).
But I can't find any clear reference of all this in the documentation.
At the end, the recommended workaround is to use a "fake" one-to-one mappings instead of properties. Remove the LOB fields from your existing class, create new classes referring to the same table, same primary key, and only the necessary LOB fields as properties. Specify the mappings as one-to-one, fetch="select", lazy="true". So long as your parent object is still in your session, you should get exactly what you want. (just transpose this to annotations).
I would suggest you to use inheritance to handle this scenario. Have a base class without the blob and a derived class containing the byte array. You would use the derived class only when you need to display the blob on the UI.
Of course you could extract that value and put it into a new table with a "#OneToOne" relation that is lazy, however in our application the LOBs are loaded lazily on demand using just this configuration
#Lob
#Fetch(FetchMode.SELECT)
#Type(type="org.hibernate.type.PrimitiveByteArrayBlobType")
byte[] myBlob;
This is tested in our project simultaneously on PostgreSQL, MySQL, SQLServer and Oracle, so it should work for u
Lazy property loading requires buildtime bytecode instrumentation.
Hibernate docs: Using lazy property fetching
If you want to avoid bytecode instrumentation one option is to to create two entities that use same table, one with the blob one without. Then only use the entity with blob when you need the blob.
I had the same issue and this was my fix:
My Entity:
#Entity
#Table(name = "file")
public class FileEntity {
#Id
#GeneratedValue
private UUID id;
#NotNull
private String filename;
#NotNull
#Lob #Basic(fetch = FetchType.LAZY)
private byte[] content;
...
Added plugin to pom.xml:
<plugin>
<groupId>org.hibernate.orm.tooling</groupId>
<artifactId>hibernate-enhance-maven-plugin</artifactId>
<executions>
<execution>
<phase>compile</phase>
<configuration>
<failOnError>true</failOnError>
<enableLazyInitialization>true</enableLazyInitialization>
</configuration>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
</plugin>
For me lazy load only worked by compiling and then running it, didn't work on eclipse or intellij for example.
I'm using gradle then I did the following to get it working
Annotate entity
Setup Hibernate gradle plugin
build.gradle
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.hibernate:hibernate-gradle-plugin:5.4.0.Final"
}
}
apply plugin: 'java'
apply plugin: 'application'
apply plugin: 'org.hibernate.orm'
hibernate {
enhance {
enableLazyInitialization = true
enableDirtyTracking = true
enableAssociationManagement = true
}
}
Entity.java
#Entity
public class Person {
#Id
#GeneratedValue
private Integer id;
#Lob
#Basic(fetch = FetchType.LAZY)
#Column(length = 255, nullable = false)
private String name;
Testing
./gradlew run
Full working example
Lazy loading works for me if I use Blob type instead of byte[].
#Column(name = "BlobField", nullable = false)
#Lob
#Basic(fetch = FetchType.LAZY)
private Blob blobField;
This one gets lazily loaded and if you need to retrieve its value access this field:
String value = IOUtils.toByteArray(entity.getBlobField().getBinaryStream());
A simple workarround using #OneTone notation based on the response of #MohammadReza Alagheband (Why does #Basic(fetch=lazy) doesn't work in my case?) but without the requirement of create a new table for each required lazy attribute is the following:
#Getter
#Setter
#Entity
#Table(name = "document")
#AllArgsConstructor
#NoArgsConstructor
public class DocumentBody implements java.io.Serializable{
#Column(name = "id", insertable = false)
#ReadOnlyProperty
#Id
#PrimaryKeyJoinColumn
private Integer id;
#Column(name = "body", unique = true, nullable = false, length = 254)
#JsonView({JSONViews.Simple.class, JSONViews.Complete.class})
private String content;
}
#Getter
#Entity
#Setter
#Table(name = "document")
#AllArgsConstructor
#NoArgsConstructor
public class DocumentTitle implements java.io.Serializable{
#Column(name = "id", insertable = false)
#ReadOnlyProperty
#Id
private Integer id;
#Column(name = "title", unique = true, nullable = false, length = 254)
#JsonView({JSONViews.Simple.class, JSONViews.Complete.class})
private String content;
}
public class Document implements java.io.Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
#JsonView({JSONViews.Simple.class, JSONViews.Complete.class})
private Integer id;
//Also it is posssible to prove with #ManyToOne
#OneToOne(fetch = FetchType.LAZY, optional = false, cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinColumn(name = "id", referencedColumnName = "id", nullable = false)
#JsonView({JSONViews.Simple.class, JSONViews.Complete.class})
private DocumentTitle title;
#OneToOne(fetch = FetchType.LAZY, optional = false, cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinColumn(name = "id", referencedColumnName = "id", nullable = false)
#JsonView({JSONViews.Simple.class, JSONViews.Complete.class})
private DocumentBody body;
}