
Java Tutorial 第四堂(3)Hibernate 與 JPA
Java Tutorial 第四堂(2)使用 spring 相依注入 << 前情 現在來想想一個需求,如果設計變更,要為每個影片的導演增加更多資訊,因而 public class Dvd { private String title; private Integer year; private Integer duration; private Director director; ... }
public class Director { private String name; ... } 那麼你的 Java 的世界中對這類物件關聯對應(Object-Relational Mapping, ORM)當然不缺解決方案,最有名的方案之一就是 Hibernate,2001年末 Hibernate 第一個版本發表,2003 年 6 月 8 日 Hibernate 2 發表,並於年末獲得 Jolt 2004 大獎,由於 Hibernate 廣為流行,設計方式後續影響了 EJB3 中 JPA(Java Persistence API) 規格之製定。 使用 Hibernate 這類的 ORM 方案,基本上需要宣告物件與關聯式資料庫的對應關係,後續操作就是從物件的觀點來進行操作,Hibernate 會自動為你產生對應的 SQL 語句。 hibernate.cfg.xml對應關係宣告的第一步,就是宣告資料庫組態資訊,這是在 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.connection.driver_class">org.hsqldb.jdbc.JDBCDriver</property> <property name="hibernate.connection.url">jdbc:hsqldb:file:src/main/resources/db/dvd_library</property> <property name="hibernate.connection.username">codedata</property> <property name="hibernate.connection.password">123456</property> <property name="hibernate.dialect">org.hibernate.dialect.HSQLDialect</property> <property name="hibernate.hbm2ddl.auto">create</property> <property name="show_sql">true</property> <property name="format_sql">true</property> <mapping class="tw.codedata.Dvd" /> <mapping class="tw.codedata.Director" /> </session-factory> </hibernate-configuration>
Entity 宣告在 hibernate.cfg.xml 中可看到 package tw.codedata; import javax.persistence.*; @Entity @Table(name="dvds") public class Dvd { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String title; private Integer year; private Integer duration; @ManyToOne(cascade=CascadeType.ALL) @JoinColumn(name="director_id") private Director director; ... } 作為 Entity 的類別,必須使用 一個導演可能主導多個影片,因此 類似地, package tw.codedata; import javax.persistence.*; @Entity @Table(name="directors") public class Director { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; ... } 那麼那些沒有標註的欄位呢?沒有標註就會採預設值,例如 注意到 SessionFactory你可以使用 package tw.codedata; import org.hibernate.cfg.Configuration; import org.hibernate.SessionFactory; import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; public class HibernateUtil { private static final SessionFactory sessionFactory; private static final StandardServiceRegistry serviceRegistry; static { try { Configuration configuration = new Configuration(); configuration.configure(); serviceRegistry = new StandardServiceRegistryBuilder() .applySettings(configuration.getProperties()).build(); sessionFactory = configuration.buildSessionFactory(serviceRegistry); } catch (Throwable ex) { throw new ExceptionInInitializerError(ex); } } public static SessionFactory getSessionFactory() { return sessionFactory; } public static void closeAllResources() { sessionFactory.close(); StandardServiceRegistryBuilder.destroy(serviceRegistry); } } 老實說,這個過程是蠻醜陋的,因而,在你將來有時間深入 Hibernate 之前,故且當這是個魔法好了 … 儲存與查詢有了 Session session = sessionFactory.openSession(); session.beginTransaction(); // 開啟交易 session.save(dvd); // 儲存 Dvd 實體 session.getTransaction().commit(); // 確認變更 session.close(); // 關閉此次操作過程 查詢的話,有幾種方式,像是搭配 HQL(Hibernate Query Language): Session session = sessionFactory.openSession(); session.beginTransaction(); List dvds = session.createQuery("from Dvd").list(); session.getTransaction().commit(); session.close(); 注意! 練習 13:使用 Hibernate 在 Lab 檔案的 exercises/exercise13 中有個 Hibernate 目錄,這是改寫自 Java Tutorial 第三堂(2)使用 spring-jdbc 存取資料庫 的範例,其中 build.gradle 已經幫你寫好了: apply plugin: 'java' apply plugin:'application' mainClassName = 'tw.codedata.Main' repositories { mavenCentral() } dependencies { compile 'org.hsqldb:hsqldb:2.3.1' compile group: 'com.google.guava', name: 'guava', version: '15.0' compile 'org.hibernate:hibernate-core:4.3.0.Final' } hibernate.cfg.xml、 package tw.codedata; import com.google.common.base.Optional; import java.util.List; import org.hibernate.*; public class DirectorDaoHibernateImpl implements DirectorDao { private SessionFactory sessionFactory; public DirectorDaoHibernateImpl(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } @Override public void saveDirector(Director director) { Session session = sessionFactory.openSession(); session.beginTransaction(); session.save(director); session.getTransaction().commit(); session.close(); } @Override public Optional maybeFromName(String name) { Session session = sessionFactory.openSession(); session.beginTransaction(); List directors = session .createQuery("from Director as d where d.name = :name") .setString("name", name).list(); session.getTransaction().commit(); session.close(); return directors.isEmpty() ? Optional.absent() : Optional.of(directors.get(0)); } } 這個 DAO 負責 package tw.codedata; import java.util.List; import org.hibernate.Session; import org.hibernate.SessionFactory; public class DvdDaoHibernateImpl implements DvdDao { private SessionFactory sessionFactory; public DvdDaoHibernateImpl(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } @Override public void saveDvd(Dvd dvd) { Session session = sessionFactory.openSession(); session.beginTransaction(); session.save(dvd); session.getTransaction().commit(); session.close(); } @Override public List allDvds() { Session session = sessionFactory.openSession(); session.beginTransaction(); List dvds = session.createQuery("from Dvd").list(); session.getTransaction().commit(); session.close(); return dvds; } } 觀察一下 package tw.codedata; import org.hibernate.*; public class Main { public static void main(String[] args) { SessionFactory factory = HibernateUtil.getSessionFactory(); DirectorDao directorDao = new DirectorDaoHibernateImpl(factory); DvdDao dvdDao = new DvdDaoHibernateImpl(factory); Director director = new Director("XD"); directorDao.saveDirector(director); dvdDao.saveDvd(new Dvd("XD", 112, 1, director)); for(Dvd dvd : dvdDao.allDvds()) { System.out.println(dvd); } HibernateUtil.closeAllResources(); } } 接著執行 |