!!!Key / Id Property ermitteln {{{ Session session = (Session)entityManager.getDelegate(); ClassMetadata metadata = session.getSessionFactory().getClassMetadata(type); String keyName = metadata.getIdentifierPropertyName(); Class<?> keyType = metadata.getIdentifierType().getReturnedClass(); }}} !!!Spring JPA applicationContext.xml: {{{ <context:component-scan base-package="com.intersult"/> <aop:config proxy-target-class="true"/> <aop:aspectj-autoproxy/> <bean id="dataSource" class="${dataSource.class}" destroy-method="close"> <property name="URL" value="${dataSource.url}"/> <property name="user" value="${dataSource.username}"/> <property name="password" value="${dataSource.password}"/> </bean> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="loadTimeWeaver"> <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/> </property> </bean> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory"/> </bean> <tx:annotation-driven/> }}} !!Local Container Manager vs. Local Entity Manager Neben dem LocalContainerEntityManagerFactoryBean gibt es noch den LocalEntityManagerFactoryBean, der einfacher zu konfigurieren und zunächst ohne Container, das heißt JNDI auskommt: {{{ <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean" lazy-init="true"> <property name="persistenceUnitName" value="subflowDb"/> <property name="jpaProperties"> <props> <prop key="hibernate.connection.url">jdbc:hsqldb:file:${project.build.directory}/hsql/data</prop> <prop key="hibernate.connection.driver_class">org.hsqldb.jdbcDriver</prop> <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop> <prop key="hibernate.connection.username">sa</prop> <prop key="hibernate.connection.password"></prop> <prop key="hibernate.show_sql">true</prop> </props> </property> </bean> }}} Allerdings ist es auch möglich, den LocalContainerEntityManagerFactoryBean lokal zu konfigurieren, sodass er z.B. aus einem JUnit-Test mit Spring heraus läuft: {{{ <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="persistenceUnitManager"> <bean id="persistenceUnitManager" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager"> <property name="persistenceXmlLocations" value="classpath*:META-INF/persistence.xml"/> <property name="defaultDataSource" ref="dataSource"/> </bean> </property> </bean> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <property name="maxActive" value="100"/> <property name="maxIdle" value="30"/> <property name="maxWait" value="16000"/> <property name="minIdle" value="0"/> </bean> }}} Der Test sieht dann so aus: {{{ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:META-INF/testApplicationContext.xml") // @TransactionConfiguration(defaultRollback = false) public class SpringDBTest { @Test @Transactional public void test() { Criteria criteria = ((Session)entityManager.getDelegate()).createCriteria(Test.class); ... } } }}} !!!Lazy-Loading und Transaction Management Es ist praktikabel, das JPA Transaction Management zusammen mit Spring zu verwenden (Annotation <tx:annotation-driven/>). Für die View kann dann der OpenEntityManagerInViewFilter verwendet werden, um Paging und Lazy-Loading zu realisieren: {{{ <filter> <filter-name>OpenEntityManagerInViewFilter</filter-name> <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>OpenEntityManagerInViewFilter</filter-name> <servlet-name>Faces Servlet</servlet-name> </filter-mapping> }}} Der EntityManager kann allerdings die Session nur öffnen, wenn der Hibernate-Parameter dafür gesetzt ist (etwa in der persistence.xml): {{{ <property name="hibernate.enable_lazy_load_no_trans" value="true"/> }}} !!!Criteria Query In Hibernate sind die Criteria Querys eins der mächstigsten Instrumente, da sie rein durch ein Programm erzeugt werden können. {{{ Criteria criteria = ((Session)entityManager.getDelegate()).createCriteria(Rule.class); ProjectionList projections = Projections.projectionList(); projections.add(Projections.alias(Projections.groupProperty(selector.property()), "goal")); projections.add(Projections.alias(Projections.rowCount(), "count")); criteria.setProjection(projections); criteria.add(Restrictions.like(selector.property(), text, MatchMode.ANYWHERE)); criteria.addOrder(Order.desc("count")); criteria.setMaxResults(5); criteria.setResultTransformer(Transformers.aliasToBean(Suggestion.class)); return criteria.list(); }}} !!Unabhängige Subquery Subquerys können nur verwendet werden, wenn statt Restrictions über Property.forName(...) gegangen wird: {{{ Criteria criteria = session.createCriteria(Transition.class); DetachedCriteria subquery = DetachedCriteria.forClass(Participant.class); subquery.add(Restrictions.eq("user", user)); subquery.setProjection(Projections.property("role")); criteria.add(Property.forName("actor").in(subquery)); }}} !!Abhängige Subquery Wenn Subquerys über Properties mit der Hauptquery verbunden sind, geht das nur über Aliase. Diese können beim erzeugen von Criterias und Subcriterias angegeben werden, sowie für Properties festgelegt. !!!UserType Durch Implementieren von org.hibernate.usertype.UserType können Mapper für [eigene Datentypen|Hibernate EnumSet] geschrieben werden. Hibernate konvertiert diese dann von/zu primitiven Datenbanktypen. Ein UserType kann auch automatisch angewandt werden, indem er mit entsprechenden Annotationen versehen wird: {{{ @MappedSuperclass @TypeDef(defaultForType = EnumSet.class, typeClass = EnumSetType.class) public class EnumSetType<Type extends Enum<Type>> implements UserType, ParameterizedType { ... } }}} !!!Database Connections Hibernate bringt zwar ein Connection Management in der Default Configuration mit, das ist allerdings nicht besonders ausgereift. Inzwischen in das Hibernate Projekt integriert ist das C3P0-Connection Management: {{{ <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-c3p0</artifactId> </dependency> }}} Der wichtigste Parameter besteht darin, in der persistence.xml das Connection-Verifying zu aktivieren: {{{ <property name="hibernate.c3p0.testConnectionOnCheckout" value="true"/> }}} !!!Collections Ein Vorteil bei Einsatz ist [Hibernate] das Handling von Collections, also 1:n Beziehungen. Allerdings entsteht dadurch die eine oder andere Verwicklung. !!Mergen von DAGs Beim Mergen reiner Baumstrukturen arbeitet Hibernate 4 inzwischen recht zuverlässig. Werden allerdings Directed Acyclic Graphs (DAGs) gespeichert, kommt es beim Mergen zu Fehlern. Dies kann durch [JPA Fix] beseitigt werden. !!Delete before Insert In der Klasse org.hibernate.engine.internal.Cascade, Methode cascadeCollectionElements ist fest verankert, dass Orphan-Delete nach dem Insert ausgeführt wird. Bei Unique-Constrains kann dies zu Konflikten führen, da der Delete noch nicht ausgeführt wird. In Oracle kann dies gelöst werden, indem dem Unique Key ein DEFERRABLE INITIALLY DEFERRED hinzugefügt wird. Dies führt dazu, dass der Unique Key erst beim Commit geprüft wird. Zu diesem Zeitpunkt ist die vorübergehende Inkonsistenz der Daten wieder beseitigt.