Enum types? UserType example of mapping Enum to int is here, Enum to string is here.
Hibernate also provides the nice implementation, which is in hibernate-annotations (merged into core from v3.6.0). Sample of usage:
<hibernate-mapping> <class name="Relationship" table="relationships"> ... <property name="semanticType"> <type name="org.hibernate.type.EnumType"> <param name="enumClass">org.mycompany.dao.SemanticType</param> </type> </property> </class> </hibernate-mapping>
Session.merge(object) before accessing the lazy collection. Alternatively you can use the following example:
public void initializeLazyCollection(MyBean bean) { final Session session = getSessionFactory().openSession(); try { // Associate the bean with a session: session.lock(bean, LockMode.READ); Hibernate.initialize(bean.getChildren()); } finally { session.close(); } }
Alternatively you may use Glead (see also Using GWT with Hibernate).
Session.lock(entity, LockMode.NONE) would do the job. Otherwise do Session.merge(entity) (see What is the proper way to re-attach detached objects in Hibernate?).
Session.save() and Session.persist()?
equals() and hashCode() behave differently from lists and maps?
ConstraintViolationException. A→B→C) and the only way out is to use synthetic (autogenerated) ID. However Hibernate can't handle this case. As from here and here the order of issued SQL statements is:
session.save()session.delete()
and the only solution is to flush the session after collection.clear().
Alternative solution is to walk the entity tree and re-assign the IDs.
one-to-many association with composite ID? CREATE TABLE document ( id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, country VARCHAR(255) NOT NULL, indexed_date datetime NOT NULL, CONSTRAINT document_pk PRIMARY KEY (id) ) engine=InnoDB; CREATE TABLE image ( document_id INTEGER UNSIGNED NOT NULL, page_number INTEGER UNSIGNED NOT NULL, image_data longblob NOT NULL, CONSTRAINT image_pk PRIMARY KEY (document_id, page_number), CONSTRAINT image_fk FOREIGN KEY (document_id) REFERENCES document (id) ON DELETE cascade ) engine=InnoDB;
In our example we will not introduce the synthetic key for image table to show how to deal with composite ID, which in our case is (document_id, page_number). The mapping is:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.mycompany.myproject.common"> <class name="Document" table="document"> <id name="id"> <generator class="native" /> </id> <property name="country" /> <property name="indexedDate" column="indexed_date" /> <bag name="images" order-by="page_number" cascade="all-delete-orphan" lazy="true" inverse="true" fetch="select"> <key column="document_id" /> <one-to-many class="DocumentImage" /> </bag> </class> <class name="DocumentImage" table="image"> <composite-id> <key-many-to-one name="parentDocument" class="Document" column="document_id" /> <key-property name="pageNumber" column="page_number" /> </composite-id> <property name="imageData" column="image_data" /> </class> </hibernate-mapping>
<many-to-one> or <key-many-to-one>, i.e. they are bidirectional. So for our example the following mapping will not work:<class name="DocumentImage" table="image"> <composite-id> <key-property name="documentId" column="document_id" /> <key-property name="pageNumber" column="page_number" /> </composite-id> ... </class>
as Hibernate will not trace the relation back to document table by only knowing the column name document_id (see HHH-4012).
Java code example:
Document document = new Document(); document.setIndexedDate(new Timestamp(9876543210L)); document.setCountry("USA") Collection<DocumentImage> images = new ArrayList<DocumentImage>(); { final DocumentImage image = new DocumentImage(); image.setPageNumber(1); image.setImageData("ANdj17shOL12p".getBytes()); image.setParentDocument(document); images.add(image); } { final DocumentImage image = new DocumentImage(); image.setPageNumber(2); image.setImageData("(*&$@!9&!".getBytes()); image.setParentDocument(document); images.add(image); } document.setImages(images); Session session = getSessionFactory().openSession(); session.saveOrUpdate(document); session.flush(); session.close();
cascade="all-delete-orphan" for your association, you need to be careful when updating the lazily loaded collection of images, in particular, when you use the code document.setImages(images) you actually replace the Hibernate lazy collection instance with another implementation. This confuses Hibernate, who will throw the following exception:
org.hibernate.HibernateException: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: com.mycompany.myproject.common.Document.images
at org.hibernate.engine.Collections.processDereferencedCollection(Collections.java:118)
at org.hibernate.engine.Collections.processUnreachableCollection(Collections.java:62)
at org.hibernate.event.def.AbstractFlushingEventListener.flushCollections(AbstractFlushingEventListener.java:241)
at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:100)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1206)
Knowing that Hibernate will in any case initialize the collection (maybe with empty collection instance), you need to use the following code: document.getImages().addAll(images). We need to specify cascade="all" (or weaker) and to control the session orphans ourselves.
Session#saveOrUpdate() or to Session#persist().saveOrUpdate event be triggered on parent entity and all child relations, causing Hibernate to check each entity if it exists in the database (so Hibernate will issue as many select from image statements plus either update image or insert into image as there are entities in collection).saveOrUpdate event be triggered on parent entity and persist event will be triggered for all child entities, so only insert into image statements will be issued. Database will trigger the exception in case the child entity with the same ID already exists.Session#persist() the side effect is that Hibernate will initialize/load the lazy collection with paragraphs (will issue one select * from paragraph), but persist event will trigger no action for these newly loaded entities. In this case you need to set the collection which you don't want to be processed to null: document.setParagraphs(null); document.setImages(images); session.persist(document);
Session#saveOrUpdate() the side effect is that Hibernate will check each entity if it exists in the database.
For more examples of how to use bidirectional associations with composite keys, see examples that come together with Hibernate distribution (e.g. have a loot at hibernate-distribution.tar.gz/project\testsuite\src\test\java\org\hibernate\test\legacy\Middle.hbm.xml).
delete v.s. delete-orphan and all-delete-orphan v.s. all,delete-orphan ? Cascade
deletemeans if parent entity is deleted, delete the related entities.delete-orphanmeans: if an entity is removed from a related one-to-many collection, then not only disassociate it from the current entity, but delete it (does not allow it to stay orphan).
all-delete-orphan and all,delete-orphan are synonyms and should result the same behaviour (see here).
NULL when the parent entity is deleted? inverse property:inverse='false' then when collection entry is removed, the parent key is set to null (update child set parent_id = null where parent_id = ?)inverse='true' then when collection entry is removed, it is deleted by key (delete from child where id = ?)
@Transactional methods) to preserve L1 cache? <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"> <mvc:interceptors> <bean class="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor"> <property name="sessionFactory" ref="hibernateSessionFactory" /> </bean> </mvc:interceptors> </beans>
Session using disconnection” on p174 in chapter “Transactions, concurrency, and caching” of Hibernate In Action by Christian Bauer and Gavin King, Manning, 2005.
To Unit test it you need a helper method, that repeats the functionality of Spring DispatcherServlet:
public abstract class AbstractTest { @Autowired protected HandlerAdapter handlerAdapter; @Autowired protected HandlerMapping handlerMapping; protected MockHttpServletRequest request = new MockHttpServletRequest(); protected MockHttpServletResponse response = new MockHttpServletResponse(); /** * This function feeds the given request to Spring MVC. The code is a simplified flow from * {@link org.springframework.web.servlet.DispatcherServlet}. */ protected void handleRequest() throws Exception { final HandlerExecutionChain handler = handlerMapping.getHandler(request); final HandlerInterceptor[] interceptors = handler.getInterceptors(); if (interceptors != null) { for (int i = 0; i < interceptors.length; i++) { if (!interceptors[i].preHandle(request, response, handler.getHandler())) { throw new IllegalStateException("The interceptor " + interceptors[i] + " prevents the request from being processed"); } } } handlerAdapter.handle(request, response, handler.getHandler()); } /** * The same as {@link #handleRequest()} but sets a request URL beforehand. */ protected void handleRequest(String requestURI) throws Exception { request.setRequestURI(requestURI); handleRequest(); } }
Alternatively, you can use the following code (assuming that property HibernateTemplate.alwaysUseNewSession = false):
SessionFactoryUtils.initDeferredClose(getHibernateTemplate().getSessionFactory()); try { getHibernateTemplate().doSomething(); ... getHibernateTemplate().doAgain(); } finally { SessionFactoryUtils.processDeferredClose(getHibernateTemplate().getSessionFactory()); }
OutOfMemoryError caused by constantly growing Hibernate L1 cache? autoClear property on Session instance, which will clean the session after the transaction completion (from here):
final Session session = getSessionFactory().getCurrentSession(); if (session instanceof SessionImplementor) { ((SessionImplementor) session).setAutoClear(true); }
You may also wish to disable the 2nd level cache and analyse the heap dump (export JAVA_OPTS=-verbose:gc -XX:+PrintClassHistogram -XX:+PrintGCDetails -Xloggc:/tmp/jvm_loggc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp + Memory Analyzer). See the complete note.
char(3) but Hibernate maps it to java.lang.Character so only first character is returned. How to change the returned type to java.lang.String? <sql-query name="myQuery"> <query-param name="days" type="int" /> <return-scalar column="count" type="int" /> <return-scalar column="section_name" type="string" /> <![CDATA[select count(id) as count, section_name from document where days <= :days]]> </sql-query>
or programmatically via org.hibernate.SQLQuery#addScalar(String columnAlias, Type type).
"Девица не хочет лезть в Окно" – device not compatible with Windows.