第一部分講解了Struts2的建立過程,這部分將在其基礎上完成Hibernate的整合。
Hibernate是一個開放源代碼的對象關係映射框架,它對JDBC進行了非常輕量級的對象封裝,使得Java程序員可以隨心所欲的使用對象編程思維來操縱數據庫。
Hibernate可以應用在任何使用JDBC的場合,既可以在Java的客戶端程序使用,也可以在Servlet/JSP的Web應用中使用。
步驟一:解壓hibernate-release-4.1.1.Final.zip,然後到hibernate-release-4.1.1.Final\lib\required文件夾下將所有的jar包拷貝到項目所在的lib文件夾下,效果圖如圖1所示:
圖1
其中相應的jar包以及作用如下所示:
jar包(.jar) |
作用 |
antlr-2.7.7 |
語言轉換工具,hibernate利用它實現HQL到SQL的轉換 |
dom4j-1.6.1 |
對dom4j的封裝,解析xml文件 |
hibernate-commons-annotations-4.0.1.Final |
常見的反射代碼用於支持註解處理 |
hibernate-core-4.1.1.Final |
Hibernate的核心代碼 |
hibernate-jpa-2.0-api-1.0.1.Final |
用來定義Hibernate持久性 |
javassist-3.15.0-GA |
一個開源的分析、編輯和創建Java字節碼的類庫 |
jboss-logging-3.1.0.GA |
Jboss的日誌框架 |
jboss-transaction-api_1.1_spec-1.0.0.Final |
Jboss事務處理 |
步驟二:拷貝hibernate-release-4.1.1.Final\project\etc文件夾下hibernate.cfg.xml文件到項目src目錄下,然後進行更改,具體的項目結構如圖2所示:
圖2
對配置文件hibernate.cfg.xml進行更改,如下所示:
hibernate.cfg.xml
<!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>
<!-- mysql連接驅動,需要導入mysql驅動jar包 -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- mysql數據庫連接地址 -->
<property name="connection.url">jdbc:mysql://localhost/steven</property>
<!-- 數據庫用戶名 -->
<property name="connection.username">root</property>
<!-- 數據庫密碼 -->
<property name="connection.password">mysqladmin</property>
<!-- 數據庫方言 -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 顯示sql語句 -->
<property name="show_sql">true</property>
<!-- 格式化sql語句 -->
<property name="format_sql">true</property>
<!-- 加載hibernate自動更新數據庫結構 -->
<property name="hbm2ddl.auto">update</property>
<!-- 映射實體類 -->
<mapping class="com.steven.entity.User" />
</session-factory>
</hibernate-configuration>
在這個過程中將用到數據庫,本文采用Mysql5.0進行操作,具體的安裝步驟將不再給出,可以參照http://blog.csdn.net/digyso888/article/details/3552981文章進行配置。
打開命令提示符,連接Mysql,創建數據庫steven,如圖3所示:
圖3
注:本文Hibernate映射採用全註解方式使用,首先,註解方式簡單,靈活,簡潔,其次在開發中在有源代碼的情況下使用較多,建議使用註解方式。
由於在hibernate.cfg.xml配置文件中已經配置映射實體類User.java,下面給出添加註解後的實體類
User.java
package com.steven.entity;
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* 實體類
*
* @author Steven
*
*/
// 標示這個類爲映射實體類
@Entity
// 改變數據庫中映射表的名稱,不加默認表名稱爲類名即User
@Table(name = "t_user")
public class User {
// 用戶Id
private int userId;
// 用戶名
private String userName;
// 用戶年齡
private int age;
// 用戶生日
private Date birthday;
// 用戶是否是會員 true:是 false:不是
private boolean isVip;
public User() {
}
// 有參構造
public User(String userName, int age, Date birthday, boolean isVip) {
this.userName = userName;
this.age = age;
this.birthday = birthday;
this.isVip = isVip;
}
// 標識此字段爲數據庫中的主鍵
@Id
// 標識此字段爲數據庫中成序列增長的列,不同的數據庫,Hibernate會自動匹配
@GeneratedValue
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public boolean getIsVip() {
return isVip;
}
public void setIsVip(boolean isVip) {
this.isVip = isVip;
}
@Override
public String toString() {
return "用戶信息: [年齡是" + this.getAge() + ", 生日是" + this.getBirthday()
+ ", 用戶編號是" + this.getUserId() + ", 用戶名是" + userName
+ isVip(this.getIsVip()) + "]";
}
/**
* 用來進行測試使用
*
* @param isVip
* @return
*/
public String isVip(boolean isVip) {
return ", " + (isVip == true ? "是" : "不是") + "會員";
}
}
然後建立測試類,首先建立一個Source Folder,用來存放所有的測試代碼,點擊項目名稱,右鍵New—>Source Folder,如圖4所示:
圖4
點擊Finish,完成創建,然後,創建包com.steven.test,創建後的目錄結構如圖5所示:
圖5
下面將創建JUnit Test Case,點擊包名com.steven.test,右鍵New->Other,彈出New選項窗口,選擇JUnit Test Case,如圖6所示:
圖6
點擊Next進入下一步,然後如圖7所示進行選擇和填寫,
圖7
點擊Finish,彈出如圖8所示的窗口,點擊OK完成創建,
圖8
此時項目結構圖如圖9所示:
圖9
打開HibernateTest.java,然後更改代碼如下所示:
HibernateTest.java
package com.steven.test;
import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.junit.Test;
/**
* Hibernate測試類
*
* @author Steven
*
*/
public class HibernateTest {
// JUnit註解,可以通過此進行測試
@Test
/**
* 測試1,自動創建數據表,因爲在配置文件Hibernate.cfg.xml中配置了<property name="hbm2ddl.auto">update</property>
*/
public void testSchemaExport() {
new SchemaExport(new Configuration().configure()).create(true, true);
}
}
接着進行測試,選中要測試的方法,右擊Run As->JUnit Test,此時控制檯會發現出現異常信息,
ERROR: HHH000231: Schema export unsuccessful
org.hibernate.HibernateException: Specified JDBC Driver com.mysql.jdbc.Driver class not found
at org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl.configure(DriverManagerConnectionProviderImpl.java:104)
at org.hibernate.service.internal.StandardServiceRegistryImpl.configureService(StandardServiceRegistryImpl.java:75)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:159)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:131)
at org.hibernate.tool.hbm2ddl.ManagedProviderConnectionHelper.prepare(ManagedProviderConnectionHelper.java:55)
at org.hibernate.tool.hbm2ddl.DatabaseExporter.<init>(DatabaseExporter.java:52)
at org.hibernate.tool.hbm2ddl.SchemaExport.execute(SchemaExport.java:368)
at org.hibernate.tool.hbm2ddl.SchemaExport.create(SchemaExport.java:305)
at org.hibernate.tool.hbm2ddl.SchemaExport.create(SchemaExport.java:294)
at com.steven.test.HibernateTest.testSchemaExport(HibernateTest.java:20)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:73)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:46)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:180)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:41)
at org.junit.runners.ParentRunner$1.evaluate(ParentRunner.java:173)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.runners.ParentRunner.run(ParentRunner.java:220)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:46)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:169)
at org.hibernate.internal.util.ReflectHelper.classForName(ReflectHelper.java:192)
at org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl.configure(DriverManagerConnectionProviderImpl.java:101)
... 33 more
2013-12-10 19:26:52 org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: HHH000230: Schema export complete
Specified JDBC Driver com.mysql.jdbc.Driver class not found, 原因是因爲沒有導入Mysql的驅動包,此時需要加入mysql-connector-java-5.1.7-bin.jar(http://pan.baidu.com/s/1efRMV)到lib下,此時再次執行測試方法,會發現控制檯出現以下信息
…………………………………………………………
INFO: HHH000046: Connection properties: {user=root, password=****}
drop table if exists t_user
create table t_user (
userId integer not null auto_increment,
age integer not null,
birthday datetime,
isVip boolean not null,
userName varchar(255),
primary key (userId)
)
2013-12-10 19:37:59 org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop
INFO: HHH000030: Cleaning up connection pool [jdbc:mysql://localhost/steven]
2013-12-10 19:37:59 org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: HHH000230: Schema export complete
在觀察數據庫,會發現數據表t_user已經被創建,如圖10所示:
圖10
數據表t_user已經創建,並且所有的字段,類型,包括主鍵和序列增長都已經被創建,這就是Hibernate帶來的好處,但是很多類型都是Hibernate默認創建的,如果想自己定義也是可以通過註解的方式更改,在此沒有給出,有興趣的可以參照其他博文進行學習。
注:在開發過程中是先寫實體類還是先建表,面向對象標準開發流程推薦第一種方法,但實際開發中大部分是第二種,主要是因爲在表裏設置外鍵比在類裏寫關聯屬性要簡單,使得不熟悉Hibernate的人用第二種也可以輕鬆的建立好mapping,而且更加靈活的創建表結構。
此時就可以測試數據庫的增刪改查的方法了,爲了方便測試,可以爲Hibernate封裝一個Util類(這個類可以在Hibernate的參考文檔中找到),專門用來對Session的獲取(也就相當於JDBC中連接的概念),此時項目結構如圖11所示:
圖11
HibernateUtil.java內容如下:
package com.steven.util;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
/**
* 創建Hibernate工具類
*
* @author Steven
*
*/
public class HibernateUtil {
// Session 工廠
private static SessionFactory factory;
static {
Configuration cfg = new Configuration().configure();
ServiceRegistry sr = new ServiceRegistryBuilder().applySettings(
cfg.getProperties()).buildServiceRegistry();
factory = cfg.buildSessionFactory(sr);
// 已經過時(主要出現在Hibernate3.x版本)
// factory = new
// AnnotationConfiguration().configure().buildSessionFactory();
}
public static SessionFactory getSessionFactory() {
return factory;
}
public static Session getSession() {
return factory.openSession();
}
public static void closeSession(Session session) {
if (session != null) {
if (session.isOpen()) {
session.close();
}
}
}
}
此時進行創建表數據的測試,在HibernateTest.java中添加如下代碼:
@Test
/**
* 測試2,用來測試創建表數據
*/
public void testCreate() {
User u1 = new User("Steven", 1, new Date(), true);
Session session = HibernateUtil.getSession();
session.beginTransaction();
session.save(u1);
session.getTransaction().commit();
HibernateUtil.closeSession(session);
}
然後一樣進行JUnit測試,運行後控制檯輸出信息:
Hibernate:
insert
into
t_user
(age, birthday, isVip, userName)
values
(?, ?, ?, ?)
在查看數據庫,發現增加了一條數據,如圖12所示:
圖12
這樣可以確定Hibernate整合已經沒有問題了,下面就可以進行具體的Hibernate對數據庫增刪改查操作的封裝,因爲在開發過程中會有着很多不同的項目結構層次,但爲了便於學習,一般情況下可以分爲entity(實體類層),dao(數據訪問對象層),service(業務層,主要可以通過Spring進行事物處理(稍後講解)),action(執行層),有時候用到的工具包可以放在util包下,這樣就可以基本架構整個項目的層次,比如在有些時候可以用到dto(數據傳輸對象層),都是很常見的,所以開發過程中需要具體問題具體分析,本文不需要那麼複雜,僅僅在於搭建框架,可以讓初學者迅速掌握搭建框架中的流程以及注意事項就可以了,以後的深入討論,我會有其他的博文講解,下面繼續進行框架的搭建。
具體的架構呈現會在整合Spring時候完全給出,暫時的架構如圖13所示:
圖13
此時具體的類的內容如下:
IBaseDao.java
package com.steven.dao.iface;
import java.util.List;
/**
* 將常用的數據庫增刪改查進行封裝
*
* @author Steven
*
* @param <T>
*/
public interface IBaseDao<T> {
/**
* 保存對象
*
* @param t
* @return
*/
public void insert(T t);
/**
* 刪除對象
*
* @param t
* @return
*/
public void delete(T t);
/**
* 更新對象
*
* @param t
* @return
*/
public void update(T t);
/**
* 根據Id查找單個對象
*
* @param id
* @return
*/
public T getById(int id);
/**
* 查詢對象集合
*
* @return
*/
public List<T> getAll();
}
IUserDao.java
package com.steven.dao.iface;
import com.steven.entity.User;
/**
* 用來繼承接口,如果有個別自己的方法可以在此進行添加
*
* @author Steven
*
*/
public interface IUserDao extends IBaseDao<User> {
}
UserDao.java
package com.steven.dao.impl;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import com.steven.dao.iface.IUserDao;
import com.steven.entity.User;
import com.steven.util.HibernateUtil;
/**
* 主要對方法進行編寫,在這裏主要實現了增加和查詢所有的方法,其他的沒有實現,在整合Spring過程中會進行再次的封裝,到時候會編寫所有的方法
*
* @author Steven
*
*/
public class UserDao implements IUserDao {
public void delete(User t) {
// TODO Auto-generated method stub
}
@SuppressWarnings("unchecked")
public List<User> getAll() {
Session sesson = HibernateUtil.getSession();
// 由於Hibernate4已經沒有了HibernateSupport和HibernateTemplate這兩個輔助類,可以通過以下實現,
Query query = sesson.createQuery("from User");
return (List<User>) query.list();
}
public User getById(int id) {
// TODO Auto-generated method stub
return null;
}
public void insert(User t) {
Session session = HibernateUtil.getSession();
session.beginTransaction();
session.save(t);
session.getTransaction().commit();
HibernateUtil.closeSession(session);
}
public void update(User t) {
// TODO Auto-generated method stub
}
}
此時可以將UserAction.java更改成如下代碼:
UserAction.java
package com.steven.action;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.apache.struts2.interceptor.RequestAware;
import com.opensymphony.xwork2.ActionSupport;
import com.steven.dao.impl.UserDao;
import com.steven.entity.User;
/**
* 通過繼承ActionSupport獲得Struts2的已有功能,
* 通過實現RequestAware獲取request(這種方式常用,還有其他三種方式,這裏不再贅述)
* 四種獲取request方式請參照http://blog.csdn.net/ms2146/article/details/5406004
*
* @author Steven
*
*/
public class UserAction extends ActionSupport implements RequestAware {
private static final long serialVersionUID = 1L;
private Map<String, Object> request;
private UserDao userDao = new UserDao();
@Override
public String execute() throws Exception {
userDao.insert(new User("Lily", 1, new Date(), false));
userDao.insert(new User("Tom", 1, new Date(), false));
// 創建一個用戶集合
List<User> userList = userDao.getAll();
// 創建用戶並添加到用戶集合中
// userList.add(new User("Steven", 1, new Date(), true));
// userList.add(new User("Lily", 1, new Date(), false));
// userList.add(new User("Tom", 1, new Date(), false));
// 將用戶存儲到ActionContext中(可以通過ognl表達式獲取)
request.put("userList", userList);
return SUCCESS;
}
public void setRequest(Map<String, Object> request) {
this.request = request;
}
}
此時在啓動Tomcat,在瀏覽器輸入http://localhost:8080/SSH,如圖14所示:
圖14
運行結果如圖15所示:
圖15
此時控制檯輸出信息如下所示:
…………………………………………
Hibernate:
insert
into
t_user
(age, birthday, isVip, userName)
values
(?, ?, ?, ?)
Hibernate:
insert
into
t_user
(age, birthday, isVip, userName)
values
(?, ?, ?, ?)
Hibernate:
select
user0_.userId as userId0_,
user0_.age as age0_,
user0_.birthday as birthday0_,
user0_.isVip as isVip0_,
user0_.userName as userName0_
from
t_user user0_
因此可以看出所有的操作都是通過Hibernate操作數據庫的,然後通過Struts將獲得的用戶集合顯示到頁面中,所以在這個過程中,Hibernate起到的作用僅僅是對數據庫的操作,而且Hibernate可以不要依靠Tomcat運行就可以使用,而Struts2主要負責接收處理數據,進行操作後然後返回到頁面中呈現,典型的MVC,而且Struts依靠Tomcat的運行。
如圖16所示,可以查看到數據庫中的數據:
圖16
注:在此,Struts2和Hibernate的簡單整合已經實現,下面將進行Spring的整合,而Spring又是SSH中用到的最多而且最重要的框架之一,所以在Spring整合中我將重點講解器IOC,AOP所代表的含義,具體的會通過例子講解,當然到時候層次架構會發生改變,而且Dao實現會完成企業級應用的封裝,到時候會改變Dao的實現方式,在此一樣恭祝大家學習愉快。