2.27.2013

Hibernate интересные моменты.

Замапленый объект может находиться в четырех состояниях:
  1. transient-объект создаётся командой new, может быть заполнен данными но, ни когда не сохранялся в сессии т.е. не ассоциирован со строкой в таблице БД.
  2. persistent-объект, объект в данный момент связанный с некоторой сессией, hibernate сессия "работает" с экземпляром объекта в данный промежуток времени, т.е. объект ассоциирован со строкой в таблице БД. Получить persistent-объект можно двумя способами:  1-й способ запросить объект такими методами как get(), load(),  list(), uniqueResult(), iterate(), scroll(), find(), getReference(). 2-й способ перевести наш transient-объект в persistent-объект путем вызова таких методов как save(), saveOrUpdate(), persist(), merge().
  3. detached-объект - это persistent-объект отсоединенный от сессии, это состояние объекта возникает после закрытия сессии close(), которая работала с объектом до этого или при вызовах методов evict(), clear() сессии. Переход из состояния detached в persistent объектом возможно при вызове методов сессии - update(), saveOrUpdate(), merge().
  4.  removed - объект - это persistent-объект удаленный в сессии методом delete() или remove() в jpa.
Еще хотелось бы сказать пару слов о detached-объектах, их можно использовать дальше, при работе с новой сессией, если к ним применить такие команды как update(); saveOrUpdate() или
merge() (в jpa), то они переходят в состояние persistent - в Hibernate такие операции называются reattached mode или merging mode - в jpa. 
Замапленный объект должен удовлетворять требованиям, тогда и только тогда он будет правильно взаимодействовать с hibernate:
  1.  Иметь конструктор по умолчанию без параметров.
  2.  Переопределять методы toString(), Equals(), HashCode().
  3.  Иметь поле "первичного ключа" (id) как минимум, как максимум сложный составной ключ.
  4.  Класс сущности не должен быть объявлен как final класс.
  5.  Класс сущности должен наследовать интерфейс Serializable.
Немножко отступим от темы и поговорим о Serializable. С каждым сериализуемым классом связан уникальный идентификационный номер. Если вы не указываете этот идентификатор явно, декларируя поле private static final long с названием serialVersionUID, система генерирует его автоматически, используя для класса сложную схему расчетов. При этом на автоматически генерируемое значение оказывают влияние название класса, названия реализуемых им интерфейсов, а также все открытые и защищенные члены. Если вы каким-то образом поменяете что-либо в этом наборе, например, добавите простой и удобный метод, изменится
и автоматически генерируемый serial version UID. Следовательно, если вы не будете явным образом декларировать этот идентификатор, совместимость с предыдущими версиями будет потеряна. Клиенты, которые пытаются сериализовать объект с помощью старой версии класса и десериализовать его уже с помощью новой версии, получат сбой программы. И так, как создать static final long serialVersionUID? Пусть у нас есть скомпилированный класс src/org/hibernate/tutorial/annotations/Person.class перейдем в папку src и запустим команду serialver которая входит в jdk:
C:\...\src>serialver org.hibernate.tutorial.annotations.Person
org.hibernate.tutorial.annotations.Person:
static final long serialVersionUID = -7593775012501239455L;
C:\...\src>
копируем и вставляем в наш класс:
package org.hibernate.tutorial.annotations;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;
import org.hibernate.annotations.Proxy;

import javax.persistence.*;
import javax.persistence.criteria.Fetch;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Entity
@Table(name = "Persons")
@ToString
@EqualsAndHashCode
//@Proxy(lazy = true)//@Proxy(lazy = false)
public class Person implements Serializable {
    static final long serialVersionUID = -7593775012501239455L;
    @Id
    @GeneratedValue()
    @Column(name = "personId")
    private Long id;
    @Column(name = "fName")
    private String firstName;
    @Column(name = "sName")
    private String sureName;
   /* @OneToMany(cascade = CascadeType.ALL
            , fetch = FetchType.EAGER,
            mappedBy = "person1")
    @Getter
    @Setter
    private List events = new ArrayList();
    */
    public String getSureName() {
        return sureName;
    }

    public void setSureName(String sureName) {
        this.sureName = sureName;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
}
Различие между командами load() и get(), оба метода предназначены для получения объекта из базы данных, отличие в том что если метод get() не находит объект в БД то возвращает null,  метод load() может вернуть прокси объект (если разрещена реалиализация lazy объектов - по умолчанию в Hibernate 3), вместо реального объекта. Заполнение данными proxy-объекта происходит, только  после вызова любого метода прокси объекта исключение составляет запрос первичного ключа (getId()). Hibernate выполняет sql запрос только тогда, когда нужны реальные данные, если существует такая запись в БД то она заполняет данными proxy-объект.  Все это называется ленивой загрузкой и её можно отменить выставив параметр @Proxy(lazy = false) у объекта. Если запись не найдена в БД, то происходит Exception (org.hibernate.ObjectNotFoundException.class). Смотрите коментарии в примерах:
@Test()
    public void loadProxyPersons(){
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        //в бд нет записи 10L load()  возвращает proxy  объект
        Person person2 = (Person) session.load(Person.class, 10L);
        //проверяем существование proxy  объекта
        assertNotNull(person2);
        session.getTransaction().commit();
        session.close();
    }

    @Test(expected = org.hibernate.ObjectNotFoundException.class)
    public void loadProxyPersonsException(){
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // в бд нет записи 10L load()  возвращает proxy  объект
        Person person2 = (Person) session.load(Person.class, 10L);
        //пытаемся заполнить проси объект данными, вызываем исключение  org.hibernate.ObjectNotFoundException.class
        person2.getFirstName();
        session.getTransaction().commit();
        session.close();
    }

    @Test()
    public void getNullPerson(){
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        //запращиваем объект
        Person person2 = (Person) session.get(Person.class, 10L);
        //проверяем что get возвращает null
        assertNull(person2);
        session.getTransaction().commit();
        session.close();
    }
По умолчанию Hibernate, всегда использует кеш первого уровня в транзакции, его не возможно отключить. Т.е когда мы производим операции над persistent-объектом изменения не сразу попадают в БД, это нужно для уменьшения количества выполняемых sql запросов к базе данных. Допустим объект модифицируется в одной сессии несколько раз, все изменения происходят в памяти (aka кеше первого уровня), в итоге генерируется 1 update sql запрос который скидывает только последние изменения по последнему состоянию, все предыдущие состояния объекта не учитываються и не сохраняюстя в БД. Расмотрим пример:
@Test
    public void testHibernateCacheLevelOne() {
        Person person = new Person();
        person.setFirstName("Vit");
        person.setSureName("Lopanov");
        Statistics statistics = sessionFactory.getStatistics();
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        session.saveOrUpdate(person);
        session.getTransaction().commit();
        session.close();
        //включаем статистику для сбора информации.
        statistics.setStatisticsEnabled(true);
        session = sessionFactory.openSession();
        session.beginTransaction();
        // загружаем объект из БД - выполняется sql:
        //Hibernate: select person0_.personId as personId16_0_, person0_.fName as fName16_0_, person0_.sName as sName16_0_ from Persons person0_ where person0_.personId=?
        person = (Person)session.load(Person.class,1L);
        // проверяем сохранен ли наш объект в кеше -  true
        System.out.println("contains in cache: "+session.contains(person));
        person.setFirstName("Tom");
        session.saveOrUpdate(person);
        //.... выполняем еще какие-то операции
        //запрашиваем заново наш объект, sql не выполняется, объект подсовывается из кеша
        person = (Person)session.load(Person.class,1L);
        // проверяем находиться ли наш объект в кеше -  true
        System.out.println("contains in cache: "+session.contains(person));
        person.setSureName("Aristov");
        session.saveOrUpdate(person);
        // выгружаем объект из кеша
        if (session.contains(person)) session.evict(person);
        // объект не найден в кеше, выполняем sql запрос
        //Hibernate: select person0_.personId as personId16_0_, person0_.fName as fName16_0_, person0_.sName as sName16_0_ from Persons person0_ where person0_.personId=?
        person = (Person)session.load(Person.class,1L);
        person.setFirstName("Oleg");
        session.saveOrUpdate(person);
        session.getTransaction().commit();
        session.close();
        System.out.println("InsertCount: "+statistics.getEntityInsertCount()+" " +
                "UpdateCount: "+statistics.getEntityUpdateCount()+" " +
                "FlushCount: "+statistics.getFlushCount()+" " +
                "TransactionCount: "+statistics.getTransactionCount()+" " +
                "SuccessfulTransactionCount: "+statistics.getSuccessfulTransactionCount());
    }
этот пример даёт такой лог, обратите внимание что мы меняли состояние объекта 3 раза (setSureName("Aristov"), setFirstName("Oleg") ... ), а sql - update выполнился лишь один раз:
Hibernate: insert into Persons (personId, fName, sName) values (null, ?, ?)
contains in cache: true
Hibernate: select person0_.personId as personId16_0_, person0_.fName as fName16_0_, person0_.sName as sName16_0_ from Persons person0_ where person0_.personId=?
contains in cache: true
Hibernate: select person0_.personId as personId16_0_, person0_.fName as fName16_0_, person0_.sName as sName16_0_ from Persons person0_ where person0_.personId=?
Hibernate: update Persons set fName=?, sName=? where personId=?
InsertCount: 0 UpdateCount: 1 FlushCount: 1 TransactionCount: 1 SuccessfulTransactionCount: 1
Отсюда вывод что: При использовании методов save(), update(), saveOrUpdate(), load(), get(), list(), iterate(), scroll() всегда будет задействован кеш первого уровня, для того что бы немедленно сохранить объект в БД, после вызовов методов save(), update(), saveOrUpdate(), нужно выполнить команду session.flush(). А для непосредственной загрузки объекта из БД sql-м, методами load(), get(), list(), iterate(), scroll(), а не из кеша первого уровня, нужно вызвать команду session.evict(person) - удаления объекта из кеша. Так же сушествует команда которая очищает полностью кеш первого уровня session.clear(); Продолжая тему кеша первого уровня, нельзя не упомянуть про Batch Inserts, загрузку множества объектов, для этого нужно контролировать кеш первого уровня от разростания и вовремя скидывать данные в БД, очищая при этом кеш:
@Test  
    public void batchInsert() {
        Person person;
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        for (int i = 0; i < 10000; i++) {
            person = new Person();
            person.setFirstName("Vit" + i);
            person.setSureName("Lopanov" + i);
            session.saveOrUpdate(person);
            if (i % 30 == 0) {
                //скидываем изменения из кеша первого уровня
                session.flush();
                //очищаем кеш первого уровня
                session.clear();
            }
        }
        session.getTransaction().commit();
        session.close();
    }
Не забудте установить параметр hibernate.jdbc.batch_size и отключить кеш второго уровня (second-level cache). И на последок вкладываю maven pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>org.hibernate.tutorials</groupId>
    <artifactId>hibernate-tutorials</artifactId>
    <version>4.1.7.Final</version>
    <packaging>pom</packaging>

    <name>Hibernate Getting Started Guide Tutorials</name>
    <description>Aggregator for the Hibernate tutorials presented in the Getting Started Guide</description>

    <properties>
        <!-- Skip artifact deployment -->
        <maven.deploy.skip>true</maven.deploy.skip>
    </properties>

    <modules>
        <module>annotations</module>
        <module>annotations0</module>
        <module>annotations1</module>
        <module>annotations2</module>
        <module>annotations3</module>
        <module>annotations4</module>
        <module>annotations5</module>
        <module>annotations6</module>
        <module>annotations7</module>
        <module>annotations8</module>
    </modules>

    <dependencies>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>4.1.7.Final</version>
        </dependency>

        <!-- Hibernate uses jboss-logging for logging, for the tutorials we will use the sl4fj-simple backend -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.6.1</version>
        </dependency>

        <!-- The tutorials use JUnit test cases to illustrate usage -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
        </dependency>

        <!-- The tutorials use the H2 in-memory database -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.2.145</version>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-ehcache</artifactId>
            <version>4.1.7.Final</version>
        </dependency>


        <!--<dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache-core</artifactId>
            <version>2.6.0</version>
        </dependency>-->

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-jdk14</artifactId>
            <version>1.6.6</version>
        </dependency>


        <!--<dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache-core</artifactId>
            <version>2.4.3</version>
        </dependency>  -->

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>0.11.0</version>
            <scope>provided</scope>
        </dependency>

        <!--<dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>-->

        <!-- Hibernate c3p0 connection pool -->
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-c3p0</artifactId>
   <version>4.1.7.Final</version>
  </dependency>

        <!-- Hibernate jbosscache-core -->
        <dependency>
          <groupId>org.jboss.cache</groupId>
          <artifactId>jbosscache-core</artifactId>
          <version>3.2.5.GA</version>
        </dependency>



    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
        </plugins>
        <testResources>
            <testResource>
                <filtering>false</filtering>
                <directory>src/test/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </testResource>
            <testResource>
                <directory>src/test/resources</directory>
            </testResource>
        </testResources>
    </build>

    <repositories>


        <repository>
            <id>sourceforge</id>
            <url>http://oss.sonatype.org/content/groups/sourceforge/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
        </repository>

        <repository>
            <id>terracotta-releases1</id>
            <url>http://www.terracotta.org/download/reflector/releases</url>
        </repository>

        <repository>
            <id>terracotta-releases</id>
            <url>http://www.terracotta.org/download/reflector/releases</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>

        <repository>
          <id>jboss-public-repository-group</id>
          <name>JBoss Public Maven Repository Group</name>
          <url>http://repository.jboss.org/nexus/content/groups/public-jboss/</url>
          <releases>
            <enabled>true</enabled>
          </releases>
          <snapshots>
            <enabled>true</enabled>
          </snapshots>
        </repository>


    </repositories>

</project>
hibernate.cfg.xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

    <session-factory>

        <!-- Database connection settings -->
        <property name="connection.driver_class">org.h2.Driver</property>
        <property name="connection.url">jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE</property>
        <property name="connection.username">sa</property>
        <property name="connection.password"></property>

        <!-- JDBC connection pool (use the built-in) -->
        <property name="connection.pool_size">1</property>

        <!-- SQL dialect -->
        <property name="dialect">org.hibernate.dialect.H2Dialect</property>

        <!-- Disable the second-level cache  -->
        <property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>

        <!-- Echo all executed SQL to stdout -->
       <!--<property name="show_sql">true</property> -->
        <!--<property name="format_sql">true</property>-->

        <!-- Drop and re-create the database schema on startup -->
        <property name="hbm2ddl.auto">create</property>
        <property name="hibernate.jdbc.fetch_size">2</property>
        <property name="hibernate.jdbc.batch_size">30</property>

        <!-- Names the annotated entity class -->
        <mapping class="org.hibernate.tutorial.annotations.Person"/>
        <mapping class="org.hibernate.tutorial.annotations.Event"/>


    </session-factory>

</hibernate-configuration>
AnnotationsIllustrationTest.java
package org.hibernate.tutorial.annotations;


import java.util.*;

import static junit.framework.Assert.*;

import lombok.extern.slf4j.Slf4j;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.stat.Statistics;
import org.junit.*;
import org.junit.Test;

@Slf4j
public class AnnotationsIllustrationTest {
    private SessionFactory sessionFactory;

    @Before
    public void setUp() {
        // A SessionFactory is set up once for an application
        sessionFactory = new Configuration()
                .configure() // configures settings from hibernate.cfg.xml
                .buildSessionFactory();
    }

    @After
    public void tearDown() {
        if (sessionFactory != null) {
            sessionFactory.close();
        }
    }

    @Test
    @Ignore
    public void testBasicUsage() {
        //transient object
        Person person = new Person();
        person.setFirstName("Vit");
        person.setSureName("Lopanov");
        /*Event event1 = new Event("Our very first event!", new Date());
        Event event2 = new Event("A follow up event", new Date());
        event1.setPerson1(person);
        event2.setPerson1(person);
        List events = new ArrayList();
        events.add(event1);
        events.add(event2);
        person.setEvents(events); */

        Session session = sessionFactory.openSession();
        session.beginTransaction();
        //persistent object
        session.saveOrUpdate(person);
        session.getTransaction().commit();
        session.close();
        //detached object  объект person перешел в состояние detached
        /*
        session = sessionFactory.openSession();
        session.beginTransaction();
        List result = session.createQuery("from Person p").list();
        for (Person person1 : (List) result) {
            log.info("Event (" + person1.getFirstName() + ") : " + person1.getSureName() + " \n"
                    + person1.getEvents().toString());
        }
        Person person2 = (Person) session.get(Person.class, 1L);
        //removed - объект
        session.delete(person2);
        result = session.createQuery("from Event").list();
        for (Event event : (List) result) {
            log.info(event.toString());
            event.getPerson1().toString();
        }
        session.getTransaction().commit();
        session.close();*/
    }

    @Test()
    @Ignore
    public void loadProxyPersons() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // в бд нет записи 10L load()  возвращает proxy  объект
        Person person2 = (Person) session.load(Person.class, 10L);
        //проверяем существование proxy  объекта
        assertNotNull(person2);
        session.getTransaction().commit();
        session.close();
    }

    @Ignore
    @Test(expected = org.hibernate.ObjectNotFoundException.class)
    public void loadProxyPersonsException() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // в бд нет записи 10L load()  возвращает proxy  объект
        Person person2 = (Person) session.load(Person.class, 10L);
        // вызываем исключение  org.hibernate.ObjectNotFoundException.class
        person2.getFirstName();
        session.getTransaction().commit();
        session.close();
    }

    @Test()
    @Ignore
    public void getNullPerson() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        //запращиваем объект
        Person person2 = (Person) session.get(Person.class, 10L);
        //проверяем что get возвращает null
        assertNull(person2);
        session.getTransaction().commit();
        session.close();
    }

    @Test
    @Ignore
    public void testHibernateCacheLevelOne() {
        Person person = new Person();
        person.setFirstName("Vit");
        person.setSureName("Lopanov");
        Statistics statistics = sessionFactory.getStatistics();
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        session.saveOrUpdate(person);
        session.getTransaction().commit();
        session.close();
        //включаем статистику для сбора информации.
        statistics.setStatisticsEnabled(true);
        session = sessionFactory.openSession();
        session.beginTransaction();
        // загружаем объект из БД - выполняется sql:
        //Hibernate: select person0_.personId as personId16_0_, person0_.fName as fName16_0_, person0_.sName as sName16_0_ from Persons person0_ where person0_.personId=?
        person = (Person) session.load(Person.class, 1L);
        // проверяем сохранен ли наш объект в кеше -  true
        System.out.println("contains in cache: " + session.contains(person));
        person.setFirstName("Tom");
        session.saveOrUpdate(person);
        //.... выполняем еще какие-то операции
        //запрашиваем заново наш объект, sql не выполняется, объект подсовывается из кеша
        person = (Person) session.load(Person.class, 1L);
        // проверяем находиться ли наш объект в кеше -  true
        System.out.println("contains in cache: " + session.contains(person));
        person.setSureName("Aristov");
        session.saveOrUpdate(person);
        // выгружаем объект из кеша
        if (session.contains(person)) session.evict(person);
        // объект не найден в кеше, выполняем sql запрос
        //Hibernate: select person0_.personId as personId16_0_, person0_.fName as fName16_0_, person0_.sName as sName16_0_ from Persons person0_ where person0_.personId=?
        person = (Person) session.load(Person.class, 1L);
        person.setFirstName("Oleg");
        session.flush();
        session.saveOrUpdate(person);
        session.getTransaction().commit();
        session.close();
        System.out.println("InsertCount: " + statistics.getEntityInsertCount() + " " +
                "UpdateCount: " + statistics.getEntityUpdateCount() + " " +
                "FlushCount: " + statistics.getFlushCount() + " " +
                "TransactionCount: " + statistics.getTransactionCount() + " " +
                "SuccessfulTransactionCount: " + statistics.getSuccessfulTransactionCount());
    }

    @Test  //@Ignore
    public void batchInsert() {
        Person person;
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        for (int i = 0; i < 10000; i++) {
            person = new Person();
            person.setFirstName("Vit" + i);
            person.setSureName("Lopanov" + i);
            session.saveOrUpdate(person);
            if (i % 30 == 0) {
                //скидываем изменения
                session.flush();
                //очищаем кеш
                session.clear();
            }
        }
        session.getTransaction().commit();
        session.close();
    }


}

2.25.2013

MongoDB + Java.

В этой статье хотелось бы кратко описать работу MongoDB + Java и расмотреть основные операции CRUD. Прежде всего установим MongoDB для этого идем на официальный сайт и скачиваем дистрибутив, у меня ОС winXP 32 - к ней подходит дистрибутив mongodb-win32-i386-2.0.8, скачайте правильно дистрибутив, ибо под разные версии ОС (xp - mongodb-win32-i386-2.0.XXX, win7 - mongodb-win32-i386-2.2.XXX) идут разные дистрибутивы MongoDB. Под winXP мне пришлось скачать старую версию дистрибутива. Создайте папку на с:\data\db - там по умолчанию MongoDB будет хранить все файлы баз данных, распакуйте архив, перейдите в папку c:\mongodb-win32-i386-2.0.8\bin. Наберите в командной строчке start mongod.exe и запустите, после выполнения команды у вас в окне должна последней строчкой появиться такая надпись:
...
Tue Feb 12 21:17:51 [initandlisten] waiting for connections on port 27017
MongoDB запушена и ждет подключения к порту 27017. Запустим клиет наберем команду start mongo.exe --host localhost, по умолчанию при подключени к MongoDB не нужно указывать ни пользователя, ни пароля. У вас на экране должны появиться строчки:
MongoDB shell version: 2.0.8
connecting to: localhost:27017/test
>  
Подключаемся по умолчанию к БД test. В MongoDB основной рабочей лошадкой является коллекция которая содержит документы, документы представляют собой пары ключ-значения в формате JSON. По умолчанию базы данных и коллекции создаются при вставке первого документа. В MongoDB структуру документов не нужно определять заранее, как делается в реляционных СУБД во время разработки (create table, alter ...), мета-данные создаются во время выполнения команд вставки и изменения. Переключимся на БД vit и вставим документ в коллекцию persons - этот документ содержит одну пару ключ значение для хранения имени - {name:'vit'} введем данные:
MongoDB shell version: 2.0.8
connecting to: test
> use vit
switched to db vit
> db.persons.insert({name:'vit'})
выведем только что вставленый документ командой find:
> db.persons.find()
{ "_id" : ObjectId("511bdbf296970d429769f39e"), "name" : "vit" }
В распечатке консоли вы можете видеть новое поле _id, его можно считать первичным ключом документа. В любом документе MongoDB должно присутствовать поле _id которое гарантированно будет уникальным среди всех значений _id в данной коллекции. Если _id не задано в момент создания документа, то MongoDB генирирует его за вас. Вставим новый документ задав _id вручную:
> db.persons.insert({_id:ObjectId("012345678901234567890123"),telHome:515595})
выведем данные на экран:
> db.persons.find()
{ "_id" : ObjectId("511bdbf296970d429769f39e"), "name" : "vit" }
{ "_id" : ObjectId("012345678901234567890123"), "telHome" : 515595 }
Усложним немного наш пример введем понятие селектор запроса - условие поиска документа. Селектор представляет собой пару-ключ значение допустим найдем документ который содержит в поле name - значение vit:
> db.persons.find({name:"vit"})
{ "_id" : ObjectId("511bdbf296970d429769f39e"), "name" : "vit" }
>
не всегда нам нужно возвращать множество полей, ограничить их можно в виде возвращаемого набора полей:
> db.persons.find({},{name:1})
{ "_id" : ObjectId("511bdbf296970d429769f39e"), "name" : "vit" }
{ "_id" : ObjectId("012345678901234567890123") }
Этот запрос возвращает только два поля из документа - _id и name, _id - всегда включено по умолчанию. Иногда нам нужно исключить набор возвращаемых полей из набора:
> db.persons.find({},{name:0})
{ "_id" : ObjectId("511bdbf296970d429769f39e") }
{ "_id" : ObjectId("012345678901234567890123"), "telHome" : 515595 }
и так попробуем изменить документ
> db.persons.update({telHome:515595},{$set: {name:"Joy"}})
> db.persons.find()
{ "_id" : ObjectId("511bdbf296970d429769f39e"), "name" : "vit" }
{ "_id" : ObjectId("012345678901234567890123"), "name" : "Joy", "telHome" : 515595 }
и удалим документ
> db.persons.remove({name:"vit"})
> db.persons.find()
{ "_id" : ObjectId("012345678901234567890123"), "name" : "Joy", "telHome" : 515595 } 
еще последние две важные команды: выводит какие БД у нас есть
> show dbs
local   0.03125GB
test    0.03125GB
tutorial        0.0625GB
vit     0.03125GB
выводит имена коллекций в текущей БД
> show collections
persons
system.indexes
и еще одна важная команда - завершение работы сервера MongoDB, сначала нужно перейти в БД admin и потом вызвать команду db.shutdownServer():
> use admin
switched to db admin
> db.shutdownServer()
Fri Feb 15 22:38:04 DBClientCursor::init call() failed
Fri Feb 15 22:38:04 query failed : admin.$cmd { shutdown: 1.0 } to: 127.0.0.1
server should be down...
Fri Feb 15 22:38:04 trying reconnect to 127.0.0.1
Fri Feb 15 22:38:05 reconnect 127.0.0.1 failed couldn't connect to server 127
0.1
Fri Feb 15 22:38:05 Error: error doing query: unknown shell/collection.js:151
И так переходим к программированию на Java, для доступа к MongoDB нужно подключить "драйвер" в maven я делаю так:
        <dependency>
   <groupId>org.mongodb</groupId>
    <artifactId>mongo-java-driver</artifactId>
    <version>2.9.1</version>
   </dependency>
и так смотрим коментарии в коде:
package com.vit;

import com.mongodb.*;

import java.net.UnknownHostException;
import java.util.List;
import java.util.Set;

public class MainDb {
    private final String dbName = "vit";
    private final String collectionName = "persons";

    public static void main(String[] args) {
        new MainDb();
    }

    public MainDb() {
        Mongo mongo = null;
        try {
            //и так подключимся к MongoDB host port
            mongo = new Mongo("localhost", 27017);
        } catch (UnknownHostException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        try {
            //получим все имена баз данных
            List databases = mongo.getDatabaseNames();
            System.out.println("Databases MongoDB:");
            for (String database : databases) System.out.println(database);
            //подключимся к БД vit
            DB db = mongo.getDB(dbName);
            System.out.println("\nCollections DB " + dbName + " MongoDB:");
            //запросим все коллекции в БД vit
            Set collections = db.getCollectionNames();
            for (String collection : collections) System.out.println(collection);
            DBCollection collection = db.getCollection(collectionName);
            //выведем все записи из коллекции
            DBCursor cursor;
            cursor = collection.find();
            PrintCursor(cursor);
            //вставка документа
            System.out.println("after Insert into MongoDB");
            BasicDBObject doc = new BasicDBObject();
            doc.put("name", "Vit");
            doc.put("telHome", 522318);
            collection.insert(doc);
            //выведем все записи из коллекции
            cursor = collection.find();
            PrintCursor(cursor);
            System.out.println("print Document with selector MongoDB");
            //выведем по условию поиска - "селектора" {"name","Joy"} из коллекции
            cursor = collection.find(
                    new BasicDBObject().append("name","Joy")
            , new BasicDBObject().append("name",1));
            PrintCursor(cursor);
            //изменение документа
            System.out.println("after update document MongoDB");
            doc = new BasicDBObject();
            doc.put("name", "Vit");
            doc.put("telHome", 522318);
            BasicDBObject doc1 = new BasicDBObject();
            doc1.put("$set",new BasicDBObject("name","Tom"));
            collection.update(doc, doc1, false, true);
            //выведем запись
            cursor = collection.find();
            PrintCursor(cursor);
            //удаление документа
            System.out.println("after delete document MongoDB");
            doc = new BasicDBObject();
            doc.put("name","Tom");
            doc.put("telHome", 522318);
            collection.remove(doc);
            //выведем все записи из коллекции
            cursor = collection.find();
            PrintCursor(cursor);
        } finally {
            // закрываем соединение с БД MongoDB
            if (mongo != null) mongo.close();
        }

    }

    /**
     * распечатываем документы возвращаемые курсором
     *
     * @param cursor
     */
    private void PrintCursor(DBCursor cursor) {
        DBObject doc;
        StringBuilder temp;
        while (cursor.hasNext()) {
            doc = cursor.next();
            temp = new StringBuilder();
            temp.append("{");
            for (String keyName : doc.keySet())
                temp.append(keyName + ":" + doc.get(keyName) + ", ");
            int i = temp.lastIndexOf(",");
            System.out.println(
                    temp.replace(i, i + ", ".length(), "}").toString());

        }
    }
}

11.27.2012

Маленькая статья по Hibernate Bidirectional Association Annotation.

Я здесь кратко описал все Bidirectional Association которые есть у Hibernate посредством Annotation.

1. Hibernate: Annotation one-to-many/many-to-one(foreign-key)

-=Relationship=-
person(one) <-> address(many)

-=DB Schema=-
create table Persons (
        personId bigint generated by default as identity,
        fName varchar(255),
        sName varchar(255),
        primary key (personId)
    )

create table EVENTS (
        id bigint not null,
        EVENT_DATE timestamp,
        title varchar(255),
        personId bigint not null,
        primary key (id)
    )

alter table EVENTS 
        add constraint FK7A9AD519B59AA19 
        foreign key (personId) 
        references Persons(personId)
-=Annotation=-
public class Person {
    @Id
    @GeneratedValue()
    @Column(name = "personId")
    private Long id;
    
    @OneToMany(cascade =CascadeType.ALL,fetch = FetchType.EAGER,
            mappedBy = "person1")
    private List events = new ArrayList();
}

public class Event {
    @Id @GeneratedValue(generator="increment")
 @GenericGenerator(name="increment", strategy = "increment")
    private Long id;
    
    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "personId",referencedColumnName = "personId",nullable = false)
    private Person person1;
}
-=Доступ к объекту=-
person1.getEvents().toString();
event.getPerson1().toString();
2. Hibernate: Annotation one-to-many/many-to-one(join table)

-=Relationship=-
Person(one) <-> Event(many)

-=DB Schema=-
create table Persons (
        personId bigint generated by default as identity,
        fName varchar(255),
        sName varchar(255),
        primary key (personId)
    )

create table EVENTS (
        eventId bigint not null,
        EVENT_DATE timestamp,
        title varchar(255),
        primary key (eventId)
    )

create table PersonEvent (
        personId bigint,
        eventId bigint not null,
        primary key (eventId)
    )

alter table PersonEvent 
        add constraint FK489E5C25B59AA19 
        foreign key (personId) 
        references Persons(personId)

alter table PersonEvent 
        add constraint FK489E5C25418989BB 
        foreign key (eventId) 
        references EVENTS(eventId)
-=Annotation=-
@Entity
@Table(name="Persons")
public class Person {
    @Id
    @GeneratedValue()
    @Column(name = "personId")
    private Long id;
    
    @OneToMany(cascade =CascadeType.ALL,fetch = FetchType.EAGER)
    @JoinTable(name = "PersonEvent"
    ,joinColumns = @JoinColumn(name = "personId")
    ,inverseJoinColumns = @JoinColumn(name = "eventId"))    
    private List events = new ArrayList();
}

@Entity
@Table(name = "EVENTS")
public class Event {
    @Id @GeneratedValue(generator="increment")
 @GenericGenerator(name="increment", strategy = "increment")
    @Column(name = "eventId")
    private Long id;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinTable(name = "PersonEvent"
    ,joinColumns = @JoinColumn(name = "eventId")
    ,inverseJoinColumns = @JoinColumn(name = "personId"))
    private Person person1;
}
-=Доступ к объекту=-
person1.getEvents().toString();
event.getPerson1().toString();
3. Hibernate: Annotation one-to-one(primary-key)

-=Relationship=-
Person(one) <-> Event(one)

-=DB Schema=-
create table Persons (
        personId bigint generated by default as identity,
        fName varchar(255),
        sName varchar(255),
        primary key (personId)
    )

create table EVENTS (
        eventId bigint not null,
        EVENT_DATE timestamp,
        title varchar(255),
        primary key (eventId)
    )
-=Annotation=-
@Entity
@Table(name="Persons")
public class Person {
    @Id
    @GeneratedValue()
    @Column(name = "personId")
    private Long id;
    
    @OneToOne(cascade = CascadeType.ALL)
    @PrimaryKeyJoinColumn    
    private Event events;
}

@Entity
@Table(name = "EVENTS")
public class Event {
    @Id @GeneratedValue(generator="increment")
 @GenericGenerator(name="increment", strategy = "increment")
    @Column(name = "eventId")
    private Long id;
   
    @OneToOne(cascade = CascadeType.ALL,mappedBy = "events")
    private Person person1;
}
-=Доступ к объекту=-
person1.getEvents().toString();
event.getPerson1().toString();
4. Hibernate: Annotation one-to-one(foreign-key)

-=Relationship=-
Person(one) <-> Event(one)

-=DB Schema=-
create table Persons (
        personId bigint generated by default as identity,
        fName varchar(255),
        sName varchar(255),
        eventId bigint,
        primary key (personId)
    )

create table EVENTS (
        eventId bigint not null,
        EVENT_DATE timestamp,
        title varchar(255),
        primary key (eventId)
    )

alter table Persons 
        add constraint FK3AC8679E418989BB 
        foreign key (eventId) 
        references EVENTS(eventId)
-=Annotation=-
@Entity
@Table(name="Persons")
public class Person {
    @Id
    @GeneratedValue()
    @Column(name = "personId")
    private Long id;
    
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name= "eventId",referencedColumnName = "eventId")    
    private Event events;
}

@Entity
@Table(name = "EVENTS")
public class Event {
    @Id @GeneratedValue(generator="increment")
 @GenericGenerator(name="increment", strategy = "increment")
    @Column(name = "eventId")
    private Long id;
    
    @OneToOne(cascade = CascadeType.ALL,mappedBy = "events")
    private Person person1;
}
-=Доступ к объекту=-
person1.getEvents().toString();
event.getPerson1().toString();
5. Annotation one-to-one(join table)

-=Relationship=-
Person(one) <-> Event(one)

-=DB Schema=-
create table Persons (
        personId bigint generated by default as identity,
        fName varchar(255),
        sName varchar(255),
        primary key (personId)
    )

create table EVENTS (
        eventId bigint not null,
        EVENT_DATE timestamp,
        title varchar(255),
        primary key (eventId)
    )

create table PersonEvent (
        eventId bigint,
        personId bigint not null,
        primary key (personId)
    )

alter table PersonEvent 
        add constraint FK489E5C25B59AA19 
        foreign key (personId) 
        references Persons(personId)

alter table PersonEvent 
        add constraint FK489E5C25418989BB 
        foreign key (eventId) 
        references EVENTS(eventId)
-=Annotation=-
@Entity
@Table(name="Persons")
public class Person {
    @Id
    @GeneratedValue()
    @Column(name = "personId")
    private Long id;
    
    @OneToOne(cascade = CascadeType.ALL)
    @JoinTable(name = "PersonEvent"
    ,joinColumns = @JoinColumn(name = "personId",referencedColumnName = "personId")
    ,inverseJoinColumns = @JoinColumn(name = "eventId",referencedColumnName = "eventId"))    
    private Event events;
}

@Entity
@Table(name = "EVENTS")
public class Event {
    @Id @GeneratedValue(generator="increment")
 @GenericGenerator(name="increment", strategy = "increment")
    @Column(name = "eventId")
    private Long id;
    
    @OneToOne(cascade = CascadeType.ALL,mappedBy = "events")
    private Person person1;
}
-=Доступ к объекту=-
person1.getEvents().toString();
event.getPerson1().toString();
6. Hibernate: Annotation many-to-many (join table)

-=Relationship=-
Person(many) <-> Event(many)

-=DB Schema=-
create table Persons (
        personId bigint generated by default as identity,
        fName varchar(255),
        sName varchar(255),
        primary key (personId)
    )

create table EVENTS (
        eventId bigint not null,
        EVENT_DATE timestamp,
        title varchar(255),
        primary key (eventId)
    )

create table PersonEvent (
        personId bigint not null,
        eventId bigint not null
    )

alter table PersonEvent 
        add constraint FK489E5C25B59AA19 
        foreign key (personId) 
        references Persons(personId)

alter table PersonEvent 
        add constraint FK489E5C25418989BB 
        foreign key (eventId) 
        references EVENTS(eventId)
-=Annotation=-
@Entity
@Table(name="Persons")
public class Person {
    @Id
    @GeneratedValue()
    @Column(name = "personId")
    private Long id;
    
    @ManyToMany(cascade = CascadeType.ALL)
    @JoinTable(name = "PersonEvent"
    ,joinColumns = @JoinColumn(name = "personId")
    ,inverseJoinColumns = @JoinColumn(name = "eventId"))    
    private List events = new ArrayList();
}

@Entity
@Table(name = "EVENTS")
public class Event {
    @Id @GeneratedValue(generator="increment")
 @GenericGenerator(name="increment", strategy = "increment")
    @Column(name = "eventId")
    private Long id;
    
    @ManyToMany(cascade = CascadeType.ALL,mappedBy = "events")
    private List person1;
}
-=Доступ к объекту=-
person1.getEvents().toString();
event.getPerson1().toString();

11.26.2012

Маленькая статья по Hibernate Unidirectional Association Annotation.

Я здесь хочу кратко описать все Unidirectional Association которые есть у Hibernate посредством Annotation.

1. Hibernate: Annotation one-to-many (foreign-key)

-=Relationship=-
Person(one) -> Event(many)

-=DB Schema=-
 
create table Persons (
        id bigint generated by default as identity,
        fName varchar(255),
        sName varchar(255),
        primary key (id)
    )

create table EVENTS (
        id bigint not null,
        EVENT_DATE timestamp,
        title varchar(255),
        events_ID bigint,
        primary key (id)
    )

alter table EVENTS  add constraint FK7A9AD519F2DCD0EA 
        foreign key (events_ID) references Persons(id)

-=Annotation=-
@Entity
@Table(name = "EVENTS")
public class Event { 
    @Id
    @GeneratedValue(generator="increment")
    @GenericGenerator(name="increment", strategy = "increment")
    private Long id;
}

@Entity
@Table(name="Persons")
public class Person {
    @Id
    @GeneratedValue()
    private Long id;

    @OneToMany(cascade = CascadeType.ALL)
    @JoinColumn(name = "events_ID")
    private Set events = new HashSet();
}
-=Доступ к объекту=-
person1.getEvents().toString();
2. Annotation one-to-many(join table)

-=Relationship=-
Person(one) -> Event(many)

-=DB Schema=-
create table EVENTS (
        id bigint not null,
        EVENT_DATE timestamp,
        title varchar(255),
        primary key (id)
    )

create table PersonEvent (
        personId bigint not null,
        eventId bigint not null,
        primary key (personId, eventId),
        unique (eventId)
    )

create table Persons (
        personId bigint generated by default as identity,
        fName varchar(255),
        sName varchar(255),
        primary key (personId)
    )

alter table PersonEvent 
        add constraint FK489E5C25B59AA19 
        foreign key (personId) 
        references Persons(personId)

alter table PersonEvent 
        add constraint FK489E5C25418989BB 
        foreign key (eventId) 
        references EVENTS(id)

-=Annotation=-
@Entity @Table(name = "Persons")
public class Person {
    @Id @GeneratedValue() @Column(name = "personId")
    private Long id;    

    @OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
    @JoinTable(name = "PersonEvent"
    ,joinColumns = @JoinColumn(name = "personId")
    ,inverseJoinColumns = @JoinColumn(name = "eventId"))
    private Set events;
}

@Entity @Table(name = "EVENTS")
public class Event {
    @Id @GeneratedValue(generator="increment")
    @GenericGenerator(name="increment", strategy = "increment")
    private Long id;
}
-=Доступ к объекту=-
person1.getEvents().toString();
3. Annotation one-to-one(foreign-key)

-=Relationship=-
Person(one) -> Event(one)

-=DB Schema=-
create table EVENTS (
        id bigint not null,
        EVENT_DATE timestamp,
        title varchar(255),
        primary key (id)
    )

create table Persons (
        personId bigint generated by default as identity,
        fName varchar(255),
        sName varchar(255),
        id bigint,
        primary key (personId)
    )

alter table Persons 
        add constraint FK3AC8679E939555A1 
        foreign key (id) 
        references EVENTS(id)
-=Annotation=-
@Entity @Table(name = "Persons")
public class Person {
    @Id @GeneratedValue() @Column(name = "personId")
    private Long id;   

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "id")
    private Event events;
 }

@Entity @Table(name = "EVENTS")
public class Event {
    @Id @GeneratedValue(generator="increment")
    @GenericGenerator(name="increment", strategy = "increment")
    private Long id;
}
-=Доступ к объекту=-
person1.getEvents().toString();
4. Annotation one-to-one(primary-key)

-=Relationship=-
Person(one) -> Event(one)

-=DB Schema=-
create table EVENTS (
        id bigint not null,
        EVENT_DATE timestamp,
        title varchar(255),
        primary key (id)
    )

create table Persons (
        personId bigint generated by default as identity,
        fName varchar(255),
        sName varchar(255),
        primary key (personId)
    )
-=Annotation=-
@Entity @Table(name = "Persons")
public class Person {
    @Id @GeneratedValue() @Column(name = "personId")
    private Long id;  

    @OneToOne(cascade = CascadeType.ALL)
    @PrimaryKeyJoinColumn(name = "personId",referencedColumnName = "eventId")
    private Event events;
}

@Entity @Table(name = "EVENTS")
@ToString  @EqualsAndHashCode
public class Event {
    @Id @GeneratedValue(generator="increment")
 @GenericGenerator(name="increment", strategy = "increment")
    private Long id;
}
-=Доступ к объекту=-
person1.getEvents().toString();
5. Annotation one-to-one(join table)

-=Relationship=-
Person(one) -> Event(one)

-=DB Schema=-
create table Persons (
        personId bigint generated by default as identity,
        fName varchar(255),
        sName varchar(255),
        primary key (personId)
    )

create table EVENTS (
        id bigint not null,
        EVENT_DATE timestamp,
        title varchar(255),
        primary key (id)
    )

create table PersonEvent (
        eventId bigint,
        personId bigint not null,
        primary key (personId)
    )

alter table PersonEvent 
        add constraint FK489E5C25B59AA19 
        foreign key (personId) 
        references Persons(personId)

alter table PersonEvent 
        add constraint FK489E5C25418989BB 
        foreign key (eventId) 
        references EVENTS(id)
-=Annotation=-
@Entity @Table(name = "Persons")
public class Person {
    @Id @GeneratedValue() @Column(name = "personId")
    private Long id;   

    @OneToOne(cascade = CascadeType.ALL)
    @JoinTable(name="PersonEvent"
            ,joinColumns = @JoinColumn(name = "personId")
            ,inverseJoinColumns = @JoinColumn(name = "eventId"))
    private Event events;
}

@Entity @Table(name = "EVENTS")
public class Event {
    @Id @GeneratedValue(generator="increment")
    @GenericGenerator(name="increment", strategy = "increment")
    private Long id;
}
-=Доступ к объекту=-
person1.getEvents().toString();
6. Hibernate: Annotation many-to-one(foreign-key)

-=Relationship=-
Person(many) -> Event(one)

-=DB Schema=-
create table EVENTS (
        id bigint not null,
        EVENT_DATE timestamp,
        title varchar(255),
        primary key (id)
    )

create table Persons (
        personId bigint generated by default as identity,
        fName varchar(255),
        sName varchar(255),
        eventId bigint,
        primary key (personId)
    )

alter table Persons 
        add constraint FK3AC8679E418989BB 
        foreign key (eventId) 
        references EVENTS(id)
-=Annotation=-
@Entity @Table(name = "Persons")
public class Person {
    @Id @GeneratedValue() @Column(name = "personId")
    private Long id;   

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "eventId"
            ,referencedColumnName = "id")
    private Event events;
}

@Entity @Table(name = "EVENTS")
public class Event {
    @Id @GeneratedValue(generator="increment")
    @GenericGenerator(name="increment", strategy = "increment")
    private Long id;
}
-=Доступ к объекту=-
person1.getEvents().toString();
7. Hibernate: Annotation many-to-one (join table)

-=Relationship=-
Person(many) -> Event(one)

-=DB Schema=-
create table Persons (
        personId bigint generated by default as identity,
        fName varchar(255),
        sName varchar(255),
        primary key (personId)
)

create table EVENTS (
        id bigint not null,
        EVENT_DATE timestamp,
        title varchar(255),
        primary key (id)
    )

create table PersonEvent (
        enentId bigint,
        personId bigint not null,
        primary key (personId)
    )

alter table PersonEvent 
        add constraint FK489E5C25B59AA19 
        foreign key (personId) 
        references Persons(personId)

alter table PersonEvent 
        add constraint FK489E5C2533E2C4C3 
        foreign key (enentId) 
        references EVENTS(id)
-=Annotation=-
@Entity @Table(name = "Persons")
public class Person {
    @Id @GeneratedValue() @Column(name = "personId")
    private Long id;    

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinTable(name = "PersonEvent"
    ,joinColumns = @JoinColumn(name = "personId",referencedColumnName = "personId")
    ,inverseJoinColumns = @JoinColumn(name = "enentId",referencedColumnName = "id"))
    private Event events;
}

@Entity @Table(name = "EVENTS")
public class Event {
    @Id @GeneratedValue(generator="increment")
    @GenericGenerator(name="increment", strategy = "increment")
    private Long id;
}
-=Доступ к объекту=-
person1.getEvents().toString();

11.12.2012

Spring mvс и Internationalization (I18N).

Для работы I18N в нашем Spring mvс приложении нужно правильно сконфигурировать компоненты которые отвечают за работу с локалью и интернациональными сообщениями, так называемыми файлами BundleMessage.properties. Для этого нам нужно настроить интерфейс org.springframework.context.MessageSource который позволяет нам работать с I18N файлами в зависимости от установленой локали. Определить текущую локаль запроса нам помогает интерфейс org.springframework.web.servlet.LocaleResolver, а изменять org.springframework.web.servlet.i18n.LocaleChangeInterceptor. Все что нужно запомнить 1-й интерфейс отвечает за место где хранятся файлы ресурсов, 2-й где хранится локаль пользователя, 3-й отвечает за смену локали. Давайте расмотрим все по порядку.
Нам нужны 2 файла Application.properties c данными:
Button.text=Click me english
и Application_ru_RU.properties:
Button.text=Click me По русски!
в зависимости от локали мы будем использовать тот или иной файл для вывода сообщений. За пути где лежат файлы ответственнен класс ReloadableResourceBundleMessageSource который наследуется от интерфейса MessageSource:
<beans:bean  class="org.springframework.context.support.ReloadableResourceBundleMessageSource"
            id="messageSource"
            p:basenames="WEB-INF/i18n/Application"
            p:fallbackToSystemLocale="false"
            p:fileEncodings="UTF8"
            p:defaultEncoding="UTF8"
            /> 
обратите внимание на параметры p:basenames="WEB-INF/i18n/Application" - указывает путь где нужно искать наши porperties файлы, p:fileEncodings="UTF8" p:defaultEncoding="UTF8" указываем  что исходная кодировка наших porperties файлов UTF8. И так перейдем к следующему интерфейсу - LocaleResolver который ответственен за установку Locale отображаемой страницы, есть несколько класов которые реализуют этот интерфейс и соответсвенно их можно использовать в настройке Spring контекста web приложения, давайте расмотрим их - как они реализуют смену и установку Locale. Первый из них AcceptHeaderLocaleResolver - он берет свою Locale из заголовка HTTP Header Accept-Language, как мы знаем обычно этот заголовок устанавливается браузером клиента и изменить его программно нельзя, используется поумолчанию:
<beans:bean class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver"
                id="localeResolver" />
Второй это CookieLocaleResolver использует для хранения Locale технологию Cookie, соответственно можно задать имя переменной Cookie, в нашем случае в p:cookieName="localeUser" будет храниться наша Locale и каждый пользователь может переустановить его на свое усмотрение.
<beans:bean class="org.springframework.web.servlet.i18n.CookieLocaleResolver"
                id="localeResolver" p:cookieName="localeUser" p:defaultLocale="en_US"/>
Если в Cookie нет установленного localeUser параметра то берется по умолчанию default значение p:defaultLocale="en_US" иначе используется Locale из заголовка HTTP Header Accept-Language. Следующий FixedLocaleResolver устанавливает фиксированную Locale на все время жизни web приложения параметр p:defaultLocale="en_US", в дальнейшем параметр не может изменяться программно.
<beans:bean class="org.springframework.web.servlet.i18n.FixedLocaleResolver"
                id="localeResolver" p:defaultLocale="en_US"/>
И последний SessionLocaleResolver хранит Locale в сессии программы (HttpSession)если нет сохраненного значения в сессии то берется по умолчанию default значение p:defaultLocale="en_US" а если не установлено p:defaultLocale то используется Locale из заголовка HTTP Header Accept-Language. Программно может меняться.
<beans:bean class="org.springframework.web.servlet.i18n.SessionLocaleResolver"
                id="localeResolver" p:defaultLocale="en_US"/>
И напоследок хочу упомянуть интерфейс который позволяет нам менять мокаль программно LocaleChangeInterceptor:
<interceptors>
        <beans:bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"
                    p:paramName="lang"/>
    </interceptors>
что бы сменить локаль нам нужно только отослать переменную lang c нужной локалью например ru_RU на http://localhost:8080/phone/spring/?lang=ru_RU название переменной настраивается параметром p:paramName="lang".
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
 
 xsi:schemaLocation="
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

 <!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
 <!-- Enables the Spring MVC @Controller programming model -->
 <annotation-driven />

 <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
 <resources mapping="/resources/**" location="/resources/" />

 <!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
 <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  <beans:property name="prefix" value="/WEB-INF/views/" />
  <beans:property name="suffix" value=".jsp" />
        <beans:property name="contentType" value="text/html;charset=UTF-8"/>
 </beans:bean>
 
 <!-- Imports user-defined @Controller beans that process client requests -->
 <beans:import resource="controllers.xml" />

    <beans:bean  class="org.springframework.context.support.ReloadableResourceBundleMessageSource"
            id="messageSource"
            p:basenames="WEB-INF/i18n/Application"
            p:fallbackToSystemLocale="false"
            p:fileEncodings="UTF8"
            p:defaultEncoding="UTF8"
            />
    <!-- 1 -->
    <!--<beans:bean class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver"
                id="localeResolver" />-->
    <!-- 2 -->
    <!--<beans:bean class="org.springframework.web.servlet.i18n.CookieLocaleResolver"
                id="localeResolver" p:cookieName="localeUser" p:defaultLocale="en_US"/>
    -->
    <!-- 3 -->
    <!--<beans:bean class="org.springframework.web.servlet.i18n.FixedLocaleResolver"
                id="localeResolver" p:defaultLocale="en_US"/>-->
    <!-- 4 -->
    <beans:bean class="org.springframework.web.servlet.i18n.SessionLocaleResolver"
                id="localeResolver" p:defaultLocale="en_US"/>
    <interceptors>
        <beans:bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"
                    p:paramName="lang"/>
    </interceptors>
 
</beans:beans>
Если вы используете для отображения страницы jsp то вы должны использовать Spring Tag Library для работы с internationalization (I18N), для этого вам необходимо в начало страницы добавить <%@ taglib uri="http://www.springframework.org/tags" prefix="msg" %> в нашем случае мы создаем переменную и присваиваем ей значение Button.text выводим значение переменной ${text}
<%@ page contentType="text/html;charset=UTF-8"%>
<%@ page pageEncoding="UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags" prefix="msg" %>
<%@ page session="false" %>
<html>
<head>
 <title>Home</title>
</head>
<body>
<h1> 
 Hello world!
</h1>
  <msg:message code="Button.text" var="test"/>
  ${test} <br/> ${pageContext.response.locale} <br/> ${controllerMessage}
</body>
</html>
и на последок обязательно нужно следить за локализацией всего web приложения от начала и до конца все файлы, все ресурсы, все запросы, в моем приложении по умолчанию используют кодировку UTF-8:
Первое все символы в запросе и ответе UTF-8
<!-- Creates the Spring Container shared by all Servlets and Filters -->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
Второе все файлы набраны в кодировке UTF-8. Третье в jsp файле установлены заголовки
<%@ page contentType="text/html;charset=UTF-8"%>
<%@ page pageEncoding="UTF-8" %>
...
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<head> 
в четвертых на правильную кодировку настроен contentType класса InternalResourceViewResolver
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <beans:property name="prefix" value="/WEB-INF/views/"/>
        <beans:property name="suffix" value=".jsp"/>
        <beans:property name="contentType" value="text/html;charset=UTF-8"/> 
</beans:bean>
в пятых правильно настроен p:fileEncodings и p:defaultEncoding класса ReloadableResourceBundleMessageSource
<beans:bean  class="org.springframework.context.support.ReloadableResourceBundleMessageSource"
            id="messageSource"
            p:basenames="WEB-INF/i18n/Application"
            p:fallbackToSystemLocale="false"
            p:fileEncodings="UTF8"
            p:defaultEncoding="UTF8"
            />
в шестых использую maven для компиляции приложения, у него настроена кодировка при сборке на UTF-8
          <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.5</source>
                    <target>1.5</target>
                    <encoding>UTF-8</encoding>
                </configuration>
          </plugin>
и в седьмых если использую tomcat то настраиваю Connector параметром URIEncoding="UTF-8"
<Connector port="8080" protocol="HTTP/1.1" 
               connectionTimeout="20000" 
               redirectPort="8443" URIEncoding="UTF-8"/>
если использую подключение к БД настраиваю передачу и хранение данных в нужной кодировке. И так локализация приложения это комплексное действие при котором нужно настраивать всЁ приложение на работу с определенной кодировкой, в моем случае используется - UTF-8.
 source code

10.26.2012

Apache Tika.

Apache Tika - библиотека предназначенная для определения, извлечения данных и метаданных из различных типов файлов в простой текстовый формат plan text, для последующего анализа содержимого файла и принятия решения что же с ним(файлом) далее делать. Очень часто tika используется как парсер текста файла, и извлеченные данные передаются для дальнейшего анализа поисковым движкам которые в свою очередь индексируют файл и его содержимое. Типы файлов которые поддерживает tika можно посмотреть на сайте http://tika.apache.org/1.2/formats.html. И так  давайте перейдем к работе с парсером tika, что для этого нужно: прежде всего файл, пусть для нашего примера мы будем парсить PDF - файл, далле нам нужен сам  парсер будем использовать спецефический заточенный под PDF-файл PDFParser, но если мы не знаем точный тип файла можно использовать и AutoDetectParser который сам принимает решение, какой парсер вызывать. У парсера есть метод public void parse(java.io.InputStream stream, org.xml.sax.ContentHandler handler,  org.apache.tika.metadata.Metadata metadata, org.apache.tika.parser.ParseContext context); который парсит файл, первый параметр принимает InputStream, источник что мы разбираем в нашем случае это InputStream is2 = new FileInputStream(file); второй параметр ContentHandler будет содержать тело документа/текст, третий параметр будет содержать метаданные - кто создал, когда и т.д., четвертый параметр какой xml-парсер будет использоваться. Давайте рассмотрим пример подробно:
package com.vit;

import org.apache.log4j.Logger;
import org.apache.tika.Tika;
import org.apache.tika.exception.TikaException;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.parser.AutoDetectParser;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.parser.pdf.PDFParser;
import org.apache.tika.sax.BodyContentHandler;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class TikaSample {
    private static final String filename = "bs12.pdf";

    public TikaSample() throws IOException, SAXException, TikaException {
        Logger log = Logger.getLogger(this.getClass().getName()); //create Logger
        Metadata metadata = new Metadata(); //create metadata
        Tika tika = new Tika(); //create tika
        InputStream is = getClass().getResourceAsStream(filename);
        File file = new File(filename);
        InputStream is2 = new FileInputStream(file);// create source InputStream
        ParseContext pc = new ParseContext(); //create xml-парсер
        ContentHandler ch = new BodyContentHandler(-1);//сдесь будет содержаться тело документа в виде plan text
        String mimeType = tika.detect(is);//определяем mimeType нашего документа
        metadata.set(Metadata.CONTENT_TYPE, mimeType);//устанавливаем mimeType
        PDFParser parser = new PDFParser(); //create PDFParser
        //AutoDetectParser parser = new AutoDetectParser();//create универсальный парсер который на основе mimeType  принимает решение какой использовать узкоспециализированный парсер
        parser.parse(is2, ch, metadata, pc); //разбор файла на мелкие кусочки
        log.info(ch.toString());//выводим текст документа
        for (String name : metadata.names()) {//выводим метаданные документа
            log.info(name + ": " + metadata.get(name));
        }
        if (is2 != null) is2.close();
        if (is != null) is.close();
    }

    public static void main(String[] args) throws IOException, SAXException, TikaException {
        TikaSample tikaSample = new TikaSample();
    }
}
обратите внимание на -1 при создании  new BodyContentHandler(-1); иначе будет ошибка если большой файл по размеру, это связано с нехваткой памяти куда будет загружается наш документ: Exception in thread "main" org.apache.tika.sax.WriteOutContentHandler$WriteLimitReachedException: Your document contained more than 100000 characters, запускаем нашу программу и получаем такой результат:
...
текст документа 
...
2012-10-26 12:50:22,990 INFO [com.vit.TikaSample] - <meta:creation-date: 2005-04-22T18:10:03Z>
2012-10-26 12:50:22,990 INFO [com.vit.TikaSample] - <dcterms:modified: 2005-04-23T05:28:17Z>
2012-10-26 12:50:22,990 INFO [com.vit.TikaSample] - <meta:save-date: 2005-04-23T05:28:17Z>
2012-10-26 12:50:22,990 INFO [com.vit.TikaSample] - <dcterms:created: 2005-04-22T18:10:03Z>
2012-10-26 12:50:22,990 INFO [com.vit.TikaSample] - <Last-Modified: 2005-04-23T05:28:17Z>
2012-10-26 12:50:22,990 INFO [com.vit.TikaSample] - <date: 2005-04-22T18:10:03Z>
2012-10-26 12:50:22,990 INFO [com.vit.TikaSample] - <modified: 2005-04-23T05:28:17Z>
2012-10-26 12:50:22,990 INFO [com.vit.TikaSample] - <xmpTPg:NPages: 25>
2012-10-26 12:50:22,990 INFO [com.vit.TikaSample] - <Creation-Date: 2005-04-22T18:10:03Z>
2012-10-26 12:50:22,990 INFO [com.vit.TikaSample] - <title: PC World Russia CD, May 2005>
2012-10-26 12:50:22,990 INFO [com.vit.TikaSample] - <created: Fri Apr 22 22:10:03 MSD 2005>
2012-10-26 12:50:22,990 INFO [com.vit.TikaSample] - <producer: Модуль Acrobat Web Capture 6.0>
2012-10-26 12:50:22,990 INFO [com.vit.TikaSample] - <Content-Type: application/pdf>
2012-10-26 12:50:22,990 INFO [com.vit.TikaSample] - <Last-Save-Date: 2005-04-23T05:28:17Z>
и конечно же приведу файл pom.xml с зависимостями:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.vit</groupId>
    <artifactId>spring-jpa-utility</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>
    <name>Spring JPA</name>

    <properties>
        <tika>1.2</tika>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.tika</groupId>
            <artifactId>tika-core</artifactId>
            <version>${tika}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tika</groupId>
            <artifactId>tika-parsers</artifactId>
            <version>${tika}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.pdfbox</groupId>
            <artifactId>pdfbox</artifactId>
            <version>1.6.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.pdfbox</groupId>
            <artifactId>pdfbox-app</artifactId>
            <version>1.6.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.pdfbox</groupId>
            <artifactId>pdfbox</artifactId>
            <version>1.7.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tika</groupId>
            <artifactId>tika-app</artifactId>
            <version>${tika}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.7</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.6.1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.5</source>
                    <target>1.5</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Внутренняя кухня hibernate.

Всем новичкам кажется что ORM Hibernate делает разработку проще и код эфективней, но если не знать, некоторых особенностей, то программирование с использованием ORM Hibernate преврашается в кромешный ад. И так что же нужно знать новичку о Hibernate - первое это способ применения, есть стандартный патерн Session per View, который предполагает последовательность правильных действий: Открыть сессию, начать транзакцию, выполнить действия над "pojo-объектом", построить представление на основе "pojo-объекта", закрыть транзакцию, закрыть сессию:
// create a event...
  Session session = sessionFactory.openSession();
  session.beginTransaction();
                Event event = new Event( "Our very first event!", new Date() );
  session.save( event );
  session.getTransaction().commit();
                System.out.println( "Event create" );
  session.close(); 
после закрытия сессии все данные не должны использоваться больше и участвовать в работе приложения. При этом использовать "pojo-объекты" можно только в одном потоке,  который открыл сессию, иначе в работе Hibernate возможны ошибки. Почему нельзя использовать "pojo-объект" после закрытия сессии это связано с особенностью работы Hibernate со сложными "pojo-объектами" которые имеют связи один-к-одному(One-to-one), один-ко-многим(One-to-many) и т.д. при создании связи по умолчанию Hibernate применяет стратегию ленивой загрузки, т.е. за место реальной связи создается прокси-объект без его реального заполнения данными и после завершения сессии, при запросе к прокси-объекту возникает рантаймовое исключение LazyInitializationException. Если вы все-таки хотите работать с "pojo-объектом" после закрытия сессии нужно отключить стратегию ленивой загрузки и переопределить методы equals() и hashCode(). Поясню все на примере, пусть у нас связь person(one) -> address(one):

@Entity
@Table(name="PERSON")
public class Person implements Serializable {
   
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  @Column(name="personId")
  private int id;

  @OneToOne
  @PrimaryKeyJoinColumn
  private Address address;

  public Address getAddress() {
    return address;
  }
}

@Entity
@Table(name = "ADDRESS")
public class Address {
  @Id
  @Column(name = "personId")
  private int id;
}
....
session.close();
person.getAddress(); --> здесь возникает исключение LazyInitializationException.
я знаю 5 или 6 методов как отключить стратегию ленивой загрузки вот один из них, нужно просто добавить аннотацию для нашего примера @Proxy(lazy=false) и все:
@Entity
@Table(name="PERSON")
@Proxy(lazy=false)
public class Person { 
....
session.close();
person.getAddress();
здесь уже не возникает исключение LazyInitializationException, так как тут уже подгружен реальный объект/коллекция объектов, но нужно быть осторожными с подгружаемыми зависимостями так как это может навредить быстродействию программы и ухудшить её работу, просто нужно найти золотую середину, посредством тестирования кода. Небольшое отступление от темы,  Hibernate нужны интерфейсы для всех сущностей если вы хотите чтобы проксировались подгружаемые объекты через Java Proxy, иначе работа идет через CGLIB, ему не нужны  интерфейсы сущностей и при помощи ASM'а  Hibernate на лету генерирует байт код прокси-объектов, выбор метода проксирования остается за вами,  по умолчанию Hibernate использует CGLIB и интерфейсы ему не нужны. Конечно мы не ищем легких путей и хотим пользоваться "pojo-объектом" и после закрытия сессии изменять их и сохранять сделанные нами изменения с помощью Hibernate, для этого как я упоминал раньше нужно переопределить методы equals() и hashCode(). Вообще я практически всегда эти важные методы наследую и периопределяю, плюс к этому всегда стараюсь наследовать итерфейс Serializable, но пропускаю конструктор "pojo-объекта" по умолчанию, т.к. при создании любого объекта как  new Person() - java автоматически за нас создает и вызывает конструктор по умолчанию. Почему я так настаиваю на переопределении методов equals() и hashCode() я постараюсь объяснить на примере, пусть у нас есть "pojo-объект" без переопределенных методов equals() и hashCode():
public class Entity {
    private int id = 0;
    ....
}
наша программа создает объект Entity и добавляет его в HashSet, далее идут проверки содержит ли HashSet точную копию объекта Entity.
import java.util.HashSet;
import java.util.Set;

public class Main {
    public static void main(String[] argv) {
        Entity slimy = new Entity();
        //1
        Set failSet = new HashSet();
        System.out.println(failSet.contains(slimy)); // false
        //2
        failSet.add(slimy);
        System.out.println(failSet.contains(slimy)); // true

        // 3 ID was assigned by an ORM Hibernate 
        slimy.setId(100);
        System.out.println(failSet.contains(slimy)); // true
    }
}
  1. создаем объект и проверяем существует ли объект slimy в HashSet, его там нет // false
  2. заносим объект slimy в HashSet и проверяем существует  ли объект slimy в HashSet - он там присутствует(содержит точную копию) // true
  3. уподобляемся ORM Hibernate и меняем slimy, производим проверку и опять точная  копия содержится в HashSet // true, - ошибка!!!
переопределим методы equals() и hashCode() класса Entity:
public class Entity {
    private int id = 0;
    ....
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + id;
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Entity other = (Entity) obj;
        if (id != other.id)
            return false;
        return true;
    }
}
заново выполняем программу и на 3-м шаге получаем отрицательный ответ // false объект slimy в HashSet не совпадает с измененным объектом slimy что есть правильно!  Плюс я всегда стараюсь переопределить метод toString() для правильной визуализации  тестовых данных!
Еще новичкам обязательно нужно понять и знать как работает Hibernate cache, без его понимания не стоит и пытаться использовать Hibernate, особенности работы подробно описаны на Хабрахабр'е вот 2 сылки Hibernate cache и Hibernate Cache Практика. Ну вот и все! здесь я дал 30% теории которую вы должны усвоить прежде чем начать работу с ORM Hibernate, 70% придет с практикой!

10.15.2012

Spring MyBatis H2.

В этой статье будет представлен пример интеграции Spring MyBatis + CRUD основных команд MyBatis'a. И так начнем с описания схемы базы данных H2 которая просто встраивается в Spring - schema.sql:
DROP TABLE IF EXISTS PERSON;

CREATE TABLE PERSON (
       ID INT NOT NULL AUTO_INCREMENT
     , FIRST_NAME VARCHAR(20) NOT NULL
     , LAST_NAME VARCHAR(20) NOT NULL
     , BIRTH_DATE DATE
     , UNIQUE UI_PERSON (FIRST_NAME, LAST_NAME)
     , PRIMARY KEY (ID)
);
мы создаем таблицу PERSON где у нас будут храниться данные по человеку его имя, фамилия и дата рождения. Напишем скрип наполняюший эту таблицу произвольными данными - test-data.sql:
insert into person (first_name, last_name, birth_date) values ('Lopanov', 'Vitaly', '1981-08-23');
insert into person (first_name, last_name, birth_date) values ('Kozirev', 'Valera', '1952-01-01');
insert into person (first_name, last_name, birth_date) values ('Aristov', 'Sergey', '1964-10-13');
отобразим нашу табличку доменной моделью реализованной классом Person.java:
package com.vit.domain;

import java.io.Serializable;
import java.util.Date;

public class Person implements Serializable {

    private Long id;
    private String firstName;
    private String lastName;
    private Date birthDate;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public Date getBirthDate() {
        return birthDate;
    }

    public void setBirthDate(Date birthDate) {
        this.birthDate = birthDate;
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                ", birthDate=" + birthDate +
                '}';
    }
}
опишим интерфейс доступа к данным, все операции CRUD:
package com.vit.persistence;

import com.vit.domain.Person;

import java.util.List;

public interface PersonMapper {

    public List findAll();

    public Person findById(Long id);

    public void insert(Person person);

    public void update(Person person);

    public void delete(Long id);

}
с проектируем все SQL-запросы в специальном файле PersonMapper.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.vit.persistence.PersonMapper">

    <resultMap id="personResultMap" type="Person">
        <id property="id" column="ID" />
        <result property="firstName" column="FIRST_NAME" />
        <result property="lastName" column="LAST_NAME" />
        <result property="birthDate" column="BIRTH_DATE" />
    </resultMap>

    <select id="findAll" resultMap="personResultMap">
        SELECT ID, FIRST_NAME, LAST_NAME, BIRTH_DATE
        FROM PERSON
    </select>
    
    <select id="findById" parameterType="long" resultMap="personResultMap">
        SELECT ID,FIRST_NAME,LAST_NAME,BIRTH_DATE
        FROM PERSON
        WHERE ID = #{id}
    </select>
    
    <insert id="insert" parameterType="Person" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO PERSON (FIRST_NAME, LAST_NAME, BIRTH_DATE)
        VALUES (#{firstName}, #{lastName}, #{birthDate})    
    </insert>   
    
    <update id="update" parameterType="Person">
        UPDATE PERSON SET
            FIRST_NAME = #{firstName},
            LAST_NAME = #{lastName},
            BIRTH_DATE = #{birthDate}
        WHERE ID = #{id}   
    </update>       
    
    <delete id="delete" parameterType="long">
        DELETE FROM PERSON WHERE ID = #{id}
    </delete>         
    
</mapper>
Опишем сервисный слой доступа к операциям CRUD c помощью интерфейса PersonService:
package com.vit.service;

import com.vit.domain.Person;

import java.util.List;

public interface PersonService {

    // Find all persons
    public List findAll();

    // Find by ID - person
    public Person findById(Long id);

    // Create a new or save an existing person
    public Person save(Person person);

    // Delete a person
    public void delete(Person person);

}
и реализуем интерфес доступа PersonServiceImpl:
package com.vit.service;

import com.vit.domain.Person;
import com.vit.persistence.PersonMapper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service("personService")
@Repository
@Transactional
public class PersonServiceImpl implements PersonService {
    @Autowired
    private PersonMapper personMapper;

    public List findAll() {
        List persons = personMapper.findAll();
        return persons;
    }

    public Person findById(Long id) {
        Person person = personMapper.findById(id);
        return person;
    }

    public Person save(Person person) {
        if (person.getId() == null) {
            insert(person);
        } else {
            update(person);
        }
        return person;
    }

    private Person insert(Person person) {
        personMapper.insert(person);
        return person;
    }

    private Person update(Person person) {
        personMapper.update(person);
        return person;
    }

    public void delete(Person person) {
        Long personId = person.getId();
        personMapper.delete(personId);
    }
}
осталось самое интересное интегрировать в Spring - MyBatis все это дело описывается парой строчек в конфигурационном файле ApplicationContext.xml:
<?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:context="http://www.springframework.org/schema/context"
    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.1.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
  http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
    <!-- 1 -->
    <jdbc:embedded-database id="dataSource" type="H2">
        <jdbc:script location="classpath:schema.sql" />
        <jdbc:script location="classpath:test-data.sql" />
    </jdbc:embedded-database>
    <!-- 2 -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <tx:annotation-driven />
    <!-- 3 -->
    <context:component-scan base-package="com.vit.service" />

    <!-- 4 Define the SqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="typeAliasesPackage" value="com.vit.domain" />
    </bean>

    <!-- 5 Scan for mappers and let them be autowired -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.vit.persistence" />
    </bean>

</beans>
  1. спринг выполняет скрипты, создает и заполняет данными таблицу Person, и настраивается dataSource H2.
  2. создается менеджер транзакций - transactionManager.
  3. задается путь где Spring ищет наши компоненты.
  4. создается SqlSessionFactory mybatis'a.
  5. объект связывающий PersonMapper.xml с интерфейсом доступа к данным PersonMapper.
и осталось показать как работать со спрингом:
package com.vit;

import com.vit.domain.Person;
import com.vit.service.PersonService;
import org.springframework.context.support.GenericXmlApplicationContext;

import java.util.*;

public class MyBatisSample {

 public static void main(String[] args) {
  GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
  ctx.load("classpath:ApplicationContext.xml");
  ctx.refresh();
  
  PersonService personService = ctx.getBean("personService", PersonService.class);

  List persons;
  
  // Find all persons
  persons = personService.findAll();
  listPersons(persons);

  // Find person by id
  persons = new ArrayList();
  System.out.println("Finding person with id 1");
  Person person = personService.findById(1L);
  persons.add(person);
  listPersons(persons);
  
  // Add new person
  System.out.println("Add new person");
  person = new Person();
  person.setFirstName("Layla");
  person.setLastName("Roberts");
  person.setBirthDate(new Date());
  personService.save(person);
  persons = personService.findAll();
  listPersons(persons);
  
  // Update person
  System.out.println("Update person with id 1");
  person = personService.findById(1L);
  person.setFirstName("Vlad");
  personService.save(person);
  persons = personService.findAll();
  listPersons(persons);

  // Delete person
  System.out.println("Delete person with id 1");
  person = personService.findById(1L);
  personService.delete(person);
  persons = personService.findAll();
  listPersons(persons);
 }
 
 private static void listPersons(List persons) {
  System.out.println("Listing persons without details:");
  for (Person person: persons) {
   System.out.println(person+"\n");
  }
 }
}
и конечно же на последок файл pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.vit</groupId>
    <artifactId>spring-mybatis</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>
    <name>Spring Utility</name>

    <properties>
                <spring.framework.version>3.1.1.RELEASE</spring.framework.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.7</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.framework.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.framework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.framework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.framework.version}</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.14</version>
        </dependency>

        <!-- H2 -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.3.160</version>
        </dependency>

        <!-- MyBatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.1.1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>