hibernateAan de slag met winterslaap


Opmerkingen

De SessionFactory bean is verantwoordelijk voor het maken, onderhouden, sluiten en doorspoelen van alle databasesessies die de TransactionManager vraagt om te maken. Daarom sturen we de SessionFactory SessionFactory naar DAO's en laten we alle vragen erdoorheen lopen.

Een van de grootste vragen die nieuwe Hibernate-gebruikers stellen is: "Wanneer worden mijn wijzigingen doorgevoerd?" en het antwoord is logisch als je denkt hoe de TransactionManager werkt met de SesisonFactory . Uw databasewijzigingen worden @Transactional en @Transactional wanneer u de servicemethode verlaat die was geannoteerd met @Transactional . De reden hiervoor is dat een transactie verondersteld wordt een enkele 'eenheid' van ononderbroken werk te vertegenwoordigen. Als er iets misgaat met het apparaat, wordt aangenomen dat het apparaat is mislukt en dat alle wijzigingen moeten worden teruggedraaid. Dus de SessionFactory zal de sessie spoelen en wissen wanneer u de servicemethode verlaat die u oorspronkelijk hebt aangeroepen.

Dat wil niet zeggen dat het de sessie niet zal spoelen en wissen terwijl uw transactie gaande is. Als ik bijvoorbeeld een servicemethode aanroep om een verzameling van 5 objecten toe te voegen en het totale aantal objecten in de database SessionFactory zou de SessionFactory zich realiseren dat de query ( SELECT COUNT(*) ) een bijgewerkte status vereist om nauwkeurig te zijn, en dus zou de toevoeging van de 5 objecten worden leeggemaakt voordat de tellingquery wordt uitgevoerd. De uitvoering zou er ongeveer zo uit kunnen zien:

versies

Versie Documentatielink Publicatiedatum
4.2.0 http://hibernate.org/orm/documentation/4.2/ 2013/03/01
4.3.0 http://hibernate.org/orm/documentation/4.3/ 2013/12/01
5.0.0 http://hibernate.org/orm/documentation/5.0/ 2015/09/01

Eenvoudig winterslaapvoorbeeld met XML

Om een eenvoudig hibernate-project op te zetten met XML voor de configuraties, hebt u 3 bestanden nodig, hibernate.cfg.xml, een POJO voor elke entiteit en een EntityName.hbm.xml voor elke entiteit. Hier is een voorbeeld van elk gebruik van MySQL:

hibernate.cfg.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC 
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
   <session-factory>
   <property name="hibernate.dialect">
      org.hibernate.dialect.MySQLDialect
   </property>
   <property name="hibernate.connection.driver_class">
      com.mysql.jdbc.Driver
   </property>

   <property name="hibernate.connection.url">
      jdbc:mysql://localhost/DBSchemaName
   </property>
   <property name="hibernate.connection.username">
      testUserName
   </property>
   <property name="hibernate.connection.password">
      testPassword
   </property>

   <!-- List of XML mapping files -->
   <mapping resource="HibernatePractice/Employee.hbm.xml"/>

</session-factory>
</hibernate-configuration>
 

DBSchemaName, testUserName en testPassword zouden allemaal worden vervangen. Zorg ervoor dat u de volledige bronnaam gebruikt als deze in een pakket zit.

Employee.java

package HibernatePractice;

public class Employee {
    private int id;
    private String firstName;
    private String middleName;
    private String lastName;
    
    public Employee(){
        
    }
    public int getId(){
        return id;
    }
    public void setId(int id){
        this.id = id;
    }
    public String getFirstName(){
        return firstName;
    }
    public void setFirstName(String firstName){
        this.firstName = firstName;
    }
    public String getMiddleName(){
        return middleName;
    }
    public void setMiddleName(String middleName){
        this.middleName = middleName;
    }
    public String getLastName(){
        return lastName;
    }
    public void setLastName(String lastName){
        this.lastName = lastName;
    }
}
 

Employee.hbm.xml

<hibernate-mapping>
   <class name="HibernatePractice.Employee" table="employee">
      <meta attribute="class-description">
         This class contains employee information. 
      </meta>
      <id name="id" type="int" column="empolyee_id">
         <generator class="native"/>
      </id>
      <property name="firstName" column="first_name" type="string"/>
      <property name="middleName" column="middle_name" type="string"/>
      <property name="lastName" column="last_name" type="string"/>
   </class>
</hibernate-mapping>
 

Nogmaals, als de klasse in een pakket zit, gebruikt u de volledige klassenaam packageName.className.

Nadat u deze drie bestanden hebt, bent u klaar om de slaapstand in uw project te gebruiken.

XML-configuratie gebruiken om de slaapstand in te stellen

Ik maak een bestand met de naam database-servlet.xml ergens op het klassenpad.

Aanvankelijk ziet uw configuratiebestand er als volgt uit:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">

</beans>
 

Je zult merken dat ik de tx en jdbc Spring-naamruimten heb geïmporteerd. Dit komt omdat we ze behoorlijk veel gaan gebruiken in dit configuratiebestand.

Het eerste wat u wilt doen is annotatie-gebaseerd transactiebeheer inschakelen ( @Transactional ). De belangrijkste reden dat mensen Hibernate in Spring gebruiken, is omdat Spring al uw transacties voor u beheert. Voeg de volgende regel toe aan uw configuratiebestand:

<tx:annotation-driven />
 

We moeten een gegevensbron maken. De gegevensbron is in feite de database die Hibernate gaat gebruiken om uw objecten te behouden. Over het algemeen heeft één transactiebeheerder één gegevensbron. Als u wilt dat Hibernate met meerdere gegevensbronnen praat, hebt u meerdere transactiebeheerders.

<bean id="dataSource" 
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="" />
    <property name="url" value="" />
    <property name="username" value="" />
    <property name="password" value="" />
</bean>
 

De klasse van deze bean kan alles zijn dat javax.sql.DataSource implementeert javax.sql.DataSource zodat u uw eigen kunt schrijven. Deze voorbeeldklasse wordt geleverd door Spring, maar heeft geen eigen threadpool. Een populair alternatief is de Apache Commons org.apache.commons.dbcp.BasicDataSource , maar er zijn er nog veel meer. Ik zal elk van de onderstaande eigenschappen uitleggen:

  • driverClassName : het pad naar uw JDBC-stuurprogramma. Dit is een database-specifieke JAR die beschikbaar moet zijn op uw classpath. Zorg ervoor dat u over de meest recente versie beschikt. Als u een Oracle-database gebruikt, hebt u een OracleDriver nodig. Als u een MySQL-database hebt, hebt u een MySQLDriver nodig. Kijk of je de driver die je nodig hebt hier kunt vinden , maar een snelle google zou je de juiste driver moeten geven.

  • url : de URL naar uw database. Meestal zal dit zoiets zijn als jdbc\:oracle\:thin\:\path\to\your\database of jdbc:mysql://path/to/your/database . Als u rondkijkt naar de standaardlocatie van de database die u gebruikt, moet u erachter kunnen komen wat dit moet zijn. Als u een HibernateException met het bericht org.hibernate.HibernateException: Connection cannot be null when 'hibernate.dialect' not set en u deze gids volgt, is er een kans van 90% dat uw URL verkeerd is, een kans van 5% dat uw database niet is gestart en 5% kans heeft dat uw gebruikersnaam / wachtwoord onjuist is.

  • gebruikersnaam : de gebruikersnaam die moet worden gebruikt bij authenticatie met de database.

  • wachtwoord : het wachtwoord dat moet worden gebruikt bij verificatie met de database.

Het volgende is het opzetten van de SessionFactory . Dit is het ding dat Hibernate gebruikt om uw transacties te maken en te beheren en in feite met de database praat. Het heeft nogal wat configuratie-opties die ik hieronder zal proberen uit te leggen.

<bean id="sessionFactory"
    class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="packagesToScan" value="au.com.project />
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.use_sql_comments">true</prop>
            <prop key="hibernate.hbm2ddl.auto">validate</prop>
        </props>
    </property>
</bean>
 
  • dataSource : Uw gegevensbron bean. Als u de ID van de gegevensbron hebt gewijzigd, stelt u deze hier in.

  • packagesToScan : De te scannen pakketten om uw met JPA geannoteerde objecten te vinden. Dit zijn de objecten die de @Entity moet beheren, zijn over het algemeen POJO's en geannoteerd met @Entity . Voor meer informatie over het instellen van object relaties in de sluimerstand te zien hier .

  • annotatedClasses (niet weergegeven): u kunt ook een lijst met klassen opgeven die door de slaapstand kunnen worden gescand als ze niet allemaal in hetzelfde pakket zitten. U moet beide packagesToScan of annotatedClasses maar niet beide. De verklaring ziet er als volgt uit:

<property name="annotatedClasses">
    <list>
        <value>foo.bar.package.model.Person</value>
        <value>foo.bar.package.model.Thing</value>
    </list>
</property>
 
  • hibernateProperties : Er zijn hier talloze met liefde gedocumenteerd . De belangrijkste die u gaat gebruiken, zijn de volgende:
  • hibernate.hbm2ddl.auto : Een van de meest populaire Hibernate-vragen beschrijft deze eigenschap. Zie het voor meer info . Over het algemeen gebruik ik valideren en stel ik mijn database in met behulp van SQL-scripts (voor een geheugen), of maak ik de database vooraf aan (bestaande database).
  • hibernate.show_sql : Booleaanse vlag, indien true Hibernate zal alle SQL die het genereert naar stdout afdrukken. U kunt uw logger ook configureren om u de waarden te tonen die aan de query's zijn gebonden door log4j.logger.org.hibernate.type=TRACE log4j.logger.org.hibernate.SQL=DEBUG in uw log manager (ik gebruik log4j) ).
  • hibernate.format_sql : Booleaanse vlag, zorgt ervoor dat Hibernate uw SQL behoorlijk kan afdrukken naar stdout.
  • hibernate.dialect (niet voor een goede reden weergegeven): veel oude tutorials laten je zien hoe je het Hibernate-dialect instelt dat het zal gebruiken om te communiceren met je database. De slaapstand kan automatisch detecteren welk dialect moet worden gebruikt op basis van het JDBC-stuurprogramma dat u gebruikt. Aangezien er ongeveer 3 verschillende Oracle-dialecten en 5 verschillende MySQL-dialecten zijn, zou ik deze beslissing overlaten aan Hibernate. Voor een volledige lijst van dialecten Hibernate ondersteunt hier zien .

De laatste 2 bonen die u moet aangeven, zijn:

<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"
    id="PersistenceExceptionTranslator" />

<bean id="transactionManager" 
    class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
 

De PersistenceExceptionTranslator vertaalt database-specifieke HibernateException of SQLExceptions in Spring-uitzonderingen die kunnen worden begrepen door de toepassingscontext.

De bean TransactionManager beheert zowel de transacties als de roll-backs.

Opmerking: u zou uw SessionFactory bean SessionFactory naar uw DAO's moeten sturen.

Hibernate-configuratie zonder XML

Dit voorbeeld is hier overgenomen

package com.reborne.SmartHibernateConnector.utils;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class LiveHibernateConnector implements IHibernateConnector {

    private String DB_DRIVER_NAME = "";
    private String DB_URL = "jdbc:h2:~/liveDB;MV_STORE=FALSE;MVCC=FALSE";
    private String DB_USERNAME = "sa";
    private String DB_PASSWORD = "";
    private String DIALECT = "org.hibernate.dialect.H2Dialect";
    private String HBM2DLL = "create";
    private String SHOW_SQL = "true";
    
    private static Configuration config;
    private static SessionFactory sessionFactory;
    private Session session;
    
    private boolean CLOSE_AFTER_TRANSACTION = false;

    public LiveHibernateConnector() {
        
        config = new Configuration();

        config.setProperty("hibernate.connector.driver_class",         DB_DRIVER_NAME);
        config.setProperty("hibernate.connection.url",                 DB_URL);
        config.setProperty("hibernate.connection.username",         DB_USERNAME);
        config.setProperty("hibernate.connection.password",         DB_PASSWORD);
        config.setProperty("hibernate.dialect",                     DIALECT);
        config.setProperty("hibernate.hbm2dll.auto",                 HBM2DLL);
        config.setProperty("hibernate.show_sql",                    SHOW_SQL);
    
        /*
         * Config connection pools
         */

        config.setProperty("connection.provider_class", "org.hibernate.connection.C3P0ConnectionProvider");
        config.setProperty("hibernate.c3p0.min_size", "5");
        config.setProperty("hibernate.c3p0.max_size", "20");
        config.setProperty("hibernate.c3p0.timeout", "300");
        config.setProperty("hibernate.c3p0.max_statements", "50");
        config.setProperty("hibernate.c3p0.idle_test_period", "3000");
        
        
        /**
         * Resource mapping
         */
        
//        config.addAnnotatedClass(User.class);
//        config.addAnnotatedClass(User.class);
//        config.addAnnotatedClass(User.class);
    
        sessionFactory = config.buildSessionFactory();
    }


    public HibWrapper openSession() throws HibernateException {
        return new HibWrapper(getOrCreateSession(), CLOSE_AFTER_TRANSACTION);
    }


    public Session getOrCreateSession() throws HibernateException {
        if (session == null) {
            session = sessionFactory.openSession();
        }
        return session;
    }

    public void reconnect() throws HibernateException {
        this.sessionFactory = config.buildSessionFactory();
    }

    
}
 

Houd er rekening mee dat met de nieuwste Hibernate deze aanpak niet goed werkt (de Hibernate 5.2 release staat deze configuratie nog steeds toe)