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
delete
means if parent entity is deleted, delete the related entities.delete-orphan
means: 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.