1.首先看一下测试数据库的物理模型
2.测试所需要的Hibernate的jar包
3.数据库的sql
/*==============================================================*/ /* DBMS name: MySQL 5.0 */ /* Created on: 2015/7/3 23:17:57 */ /*==============================================================*/ drop table if exists books; drop table if exists publishers; /*==============================================================*/ /* Table: books */ /*==============================================================*/ create table books ( bookId int not null auto_increment, publisherId int, title varchar(20), author varchar(20), primary key (bookId) ); /*==============================================================*/ /* Table: publishers */ /*==============================================================*/ create table publishers ( publisherId int not null auto_increment, publishName varchar(20), primary key (publisherId) ); alter table books add constraint FK_publisherToBook foreign key (publisherId) references publishers (publisherId) on delete restrict on update restrict;
4.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节点就够了,除非我们中间使用了多个数据库--> <session-factory> <!--用户名 --> <property name="connection.username">root</property> <!--url信息 --> <property name="connection.url">jdbc:mysql://localhost:3306/bookstore</property> <!--数据库方言信息--> <property name="dialect">org.hibernate.dialect.MySQLDialect</property> <!--密码 --> <property name="connection.password">hjzgg5211314</property> <!--数据库驱动信息 --> <property name="connection.driver_class">org.gjt.mm.mysql.Driver</property> <!--指定Hibernate映射文件路径 --> <!-- 字符集乱码问题 --> <property name="connection.useUnicode">true</property> <property name="connection.characterEncoding">UTF-8</property> <!-- 控制台输出sql语句 --> <property name="show_sql">true</property> <property name="format_sql">true</property> <mapping resource="com/entry/books.hbm.xml" /> <mapping resource="com/entry/publishers.hbm.xml" /> </session-factory> </hibernate-configuration>
5.测试的servlet
package com.servlet; import java.io.IOException; import java.io.PrintWriter; import java.util.List; import java.util.Set; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.dao.MyHibernateDao; import com.entry.Books; import com.entry.Publishers; /** * Servlet implementation class HibernateServlet */ @WebServlet("/HibernateServlet") public class HibernateServlet extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#HttpServlet() */ public HibernateServlet() { super(); // TODO Auto-generated constructor stub } private void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setCharacterEncoding("utf-8"); response.setContentType("text/html"); MyHibernateDao dao = new MyHibernateDao(); List<Books> list = dao.getAllBooks(); PrintWriter out = response.getWriter(); for(Books book : list){ out.print("书名:" + book.getTitle() + "---作者:" + book.getAuthor() + "---出版社:" + book.getPublishers().getPublishName()); out.print("<br>"); } out.print("<br>"); out.print("<br>"); out.print("<br>"); out.print("<br>"); List<Publishers> tlist = dao.getAllPublishers(); for(Publishers publishers : tlist){ out.print("出版社: " + publishers.getPublishName()); out.print("<br>"); Set<Books> bookSet = publishers.getBooks(); for(Books books : bookSet){ out.print("       "); out.print(books.getTitle() + " " + books.getAuthor() + "<br>"); } out.print("<br>"); } } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub process(request, response); } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub process(request, response); } }
6.简单的Dao层
package com.dao; import java.util.List; import org.hibernate.Session; import com.entry.Books; import com.entry.Publishers; import com.hibtest1.HibernateSessionFactory; public class MyHibernateDao { public List<Books> getAllBooks(){ Session session = HibernateSessionFactory.getSession(); List<Books> list = null; try{ String hql = "from Books"; list = session.createQuery(hql).list(); } catch (Exception e){ System.out.println("getAllBooks---errors"); e.printStackTrace(); } return list; } public List<Publishers> getAllPublishers(){ Session session = HibernateSessionFactory.getSession(); List<Publishers> list = null; try{ String hql = "from Publishers"; list = session.createQuery(hql).list(); } catch (Exception e){ System.out.println("getAllPublishers---errors"); e.printStackTrace(); } return list; } }
7.实体Publishers和它的映射文件xml
package com.entry; import java.io.Serializable; import java.util.Set; public class Publishers implements Serializable{ private Integer publisherId; private String publishName; public Integer getPublisherId() { return publisherId; } public void setPublisherId(Integer publisherId) { this.publisherId = publisherId; } public String getPublishName() { return publishName; } public void setPublishName(String publishName) { this.publishName = publishName; } private Set<Books> books; public Set<Books> getBooks() { return books; } public void setBooks(Set<Books> books) { this.books = books; } }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.entry"> <class name="Publishers" table="publishers"> <!-- 定义持久化类的表示属性 --> <id name="publisherId" column="publisherId" type="java.lang.Integer"> <!-- 定义主键生成策略 --> <generator class="identity"/> </id> <property name="publishName" column="publishName" type="java.lang.String"/> <!-- 反过来看,出版社和书籍是一对多的关系,这也就意味着每个Publishers对象会引用一组Books对象。 --> <set name="books" lazy="false"> <key column="publisherId"/> <one-to-many class="com.entry.Books"/> </set> </class> </hibernate-mapping>
8.实体Books和它的映射文件xml
package com.entry; import java.io.Serializable; public class Books implements Serializable{ private Integer bookId; private String title; private String author; private Publishers publishers; public Integer getBookId() { return bookId; } public void setBookId(Integer bookId) { this.bookId = bookId; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public Publishers getPublishers() { return publishers; } public void setPublishers(Publishers publishers) { this.publishers = publishers; } }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.entry"> <class name="Books" table="books"> <!-- 定义持久化类的表示属性 --> <id name="bookId" column="bookId" type="java.lang.Integer"> <!-- 定义主键生成策略 --> <generator class="identity"/> </id> <property name="title" column="title" type="java.lang.String"/> <property name="author" column="author" type="java.lang.String"/> <many-to-one name="publishers" column="publisherId" class="com.entry.Publishers" lazy="false"/> <!-- 默认lazy="true", 这样Books对象加载之后就不会立即加载关联的Publishers对象,就会发生异常 --> </class> </hibernate-mapping>
9. HibernateSessionFactory代码
package com.hibtest1; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.cfg.Configuration; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistryBuilder; @SuppressWarnings("deprecation") public class HibernateSessionFactory { /** * Location of hibernate.cfg.xml file. * Location should be on the classpath as Hibernate uses * #resourceAsStream style lookup for its configuration file. * The default classpath location of the hibernate config file is * in the default package. Use #setConfigFile() to update * the location of the configuration file for the current session. */ private static String CONFIG_FIEL_LOACTION = "/hibernate.cfg.xml"; private static String configFile = CONFIG_FIEL_LOACTION; private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>(); private static org.hibernate.SessionFactory sessionFactory; private static Configuration configuration = new Configuration(); private static ServiceRegistry serviceRegistry; /** 在回话工厂类HibernateSessionFactory中, 首先通过一次静态代码块来启动Hibernate,该代码块只在HibernateSessionFactory类被加载时执行一次, 用于建立sessionFactory。即SessionFactory是线程安全的,只能被实例化一次。 在静态代码块中通过创建Configuration对象并调用其configure()方法读取Hibernate配置文件hibernate.cfg.xml信息,从而进行配置信息的管理。然后 创建SessionFactory实例,在Hibernate4.0版本之前,该实例创建工作由Configuration对象的buildSessionFactory()方法来完成。而Hibernater4.0 版本之后,创建SessionFactory实例的方法有所改变,Hibernate 增加了一个注册ServiceRegistryBuilder类。要生成一个注册机对象,然后所有的生成 SessionFactory的对象向注册机注册一下再使用。生成方法还config.buildSessionFactory()方法,只不过加了一个注册机的参数。 */ static { try { configuration.configure(configFile); serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry(); sessionFactory = configuration.buildSessionFactory(serviceRegistry); } catch (Exception e) { System.err.println("%%%% Error Creating SessionFactory %%%%" + e); e.printStackTrace(); } } private HibernateSessionFactory() { } /** * Returns the ThreadLocal Session instance. Lazy initialize * the <code>SessionFactory</code> if needed. * * 由于sessionFactory是线程安全的,因而同一个SessionFactory实例可以被多个线程共享,即多个并发线程可以同时访问一个SesisonFactory并 * 获得Session的实例。但由于Session不是线程安全的,如果多个并发线程同时操作同一个Session对象,就可能出现一个线程在进行数据库操作,而另一个 * 线程将Session对象关闭的情况,从而出现异常。如何才能保证线程安全呢?这就要求SessionFactory能够针对不同的线程创建不同的Session对象,即 * 需要对Session进行有效的管理,Hibernate中使用ThreadLocal对象来维护和管理Session实例。 * * ThreadLocal是线程局部变量,线程局部变量高效的为每一个使用它的线程提供单独的线程局部变量的副本。为了实现为每个线程维护一个变量的副本,ThreadLocal * 类提供了一个Map结构,是key值用来保存线程的ID, value用来保存一个Session实例的副本。这样多线程并发操作时,实在与自己绑定的Session实例副本上进行 * 的,从而避免多个线程横在同一个Session实例上操作是可能导致的数据异常。 * * 在HibernaterSessionFctory类的getSession()方法中,首先调用ThreadLocal类的get()方法获得当前线程的Session对象,然后判断当前线程是否已存在 * Session对象或者对象未打开,在判断SessionFactory对象是否为空,如果SeesionFctory对象不存在,先调用rebuildSessionFactory()方法创建SesionFactory, * 如果SessionFactory对象已经存在,则调用SessionFactory对象的openSession()方法创建Session对象。创建完Session对象后,还需要调用ThreadLocal的set() * 方法为该线程保存Session对象。 * * @return Session * @throws HibernateException */ public static Session getSession() { Session session = (Session) threadLocal.get(); try{ if (session == null || !session.isOpen()) { if (sessionFactory == null) { rebuildSessionFactory(); } session = (sessionFactory != null) ? sessionFactory.openSession() : null; threadLocal.set(session); } } catch (HibernateException e){ System.err.println("%%%% Error Creating getSession %%%%" + e); e.printStackTrace(); } return session; } /** * Rebuild hibernate session factory * */ public static void rebuildSessionFactory() { synchronized(sessionFactory){ try { configuration.configure(configFile); serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry(); sessionFactory = configuration.buildSessionFactory(serviceRegistry); } catch (Exception e) { System.err.println("%%%% Error rebuildSessionFactory %%%%" + e); e.printStackTrace(); } } } /** * Close the single hibernate session instance. * * @throws HibernateException */ public static void closeSession(){ Session session = (Session) threadLocal.get(); threadLocal.set(null); try{ if (session != null && session.isOpen()) { session.close(); } } catch(HibernateException e){ System.err.println("%%%% Error closeSession %%%%" + e); e.printStackTrace(); } } /** * return session factory * */ public static org.hibernate.SessionFactory getSessionFactory() { return sessionFactory; } /** * return hibernate configuration * */ public static Configuration getConfiguration() { return configuration; } public static void setConfigFile(String configFile){ HibernateSessionFactory.configFile = configFile; } }
一.上述代码可以简单的完成数据的查找
二.Hibernate添加数据
方法1.主控方为“一”的一方添加数据,默认, 注意“多”的一方的外键publisherId是允许空值的情况
在MyHibernateDao.java中添加如下的方法,用来进行级联数据的添加
//添加数据 public void save(Publishers publisher){ Session session = HibernateSessionFactory.getSession(); Transaction tran = session.beginTransaction(); session.save(publisher); tran.commit(); session.close(); }
在HibernateServlet.java中添加如下代码,进行数据添加
//添加数据 Books book1 = new Books(); book1.setAuthor("李xx"); book1.setTitle("数学"); Books book2 = new Books(); book2.setAuthor("hxx"); book2.setTitle("语文"); Publishers publisher = new Publishers(); publisher.setPublishName("hjzgg"); publisher.getBooks().add(book1); publisher.getBooks().add(book2); dao.save(publisher); PrintWriter out = response.getWriter(); List<Books> list = dao.getAllBooks(); out.print(list.size() + "<br>"); for(Books book : list){ out.print("书名:" + book.getTitle() + "---作者:" + book.getAuthor() + "---出版社:" + book.getPublishers().getPublishName()); out.print("<br>"); } out.print("<br>"); out.print("<br>"); out.print("<br>"); out.print("<br>");
publishers.hbm.xml代码更改如下:
<set name="books" lazy="false" cascade="save-update"> <key column="publisherId"/> <one-to-many class="com.entry.Books"/> </set>
注意如果 “多”的一方的外键publisherId是不允许空值的情况,会出现如下异常, 那么更改的地方就多了一些
%%%% Error Creating SessionFactory %%%%org.hibernate.MappingException: Repeated column in mapping for entity: com.entry.Books column: publisherId (should be mapped with insert="false" update="false")
org.hibernate.MappingException: Repeated column in mapping for entity: com.entry.Books column: publisherId (should be mapped with insert="false" update="false")
更改如下:
books.hbm.xml文件:
<many-to-one name="publishers" column="publisherId" class="com.entry.Publishers" lazy="false" insert="false" update="false"/>
publishers.hbm.xml文件:
<set name="books" lazy="false" cascade="save-update"> <key column="publisherId" not-null="true"/> <one-to-many class="com.entry.Books"/> </set>
方法2.主控方为“多”的一方添加数据,利用invers属性,将控制权进行反转,这样添加数据相对来说更为高效, 代码更改如下:
首先在HibernateServlet.java中添加如下代码,进行数据添加
//添加数据, 主控方为“多” Books book1 = new Books(); book1.setAuthor("李xx"); book1.setTitle("数学"); Books book2 = new Books(); book2.setAuthor("hxx"); book2.setTitle("语文"); Publishers publisher = new Publishers(); publisher.setPublishName("hjzgg"); book1.setPublishers(publisher); book2.setPublishers(publisher); dao.saveBooks(book1); dao.saveBooks(book2);
然后修改books.hbm.xml文件:
<many-to-one name="publishers" column="publisherId" class="com.entry.Publishers" lazy="false" cascade="save-update"/>
接着修改publishers.hbm.xml文件:
<set name="books" lazy="false" cascade="save-update" inverse="true"> <key column="publisherId" not-null="true"/> <one-to-many class="com.entry.Books"/> </set>
最后在MyHibernateDao.java中添加如下代码:
//添加数据, 主控方为“多” public void saveBooks(Books book){ Session session = HibernateSessionFactory.getSession(); Transaction tran = session.beginTransaction(); session.save(book); tran.commit(); session.close(); }