一,hibernate與jpa的關系
首先明確一點jpa是什么?以前我就搞不清楚jpa和hibernate的關系。
1,JPA(Java Persistence API)是Sun官方提出的Java持久化規范。它為Java開發人員提供了一種對象/關系映射工具來管理Java應用中的關系數據。而Hibernate是它的一種實現。除了Hibernate,還有EclipseLink(曾經的toplink),OpenJPA等可供選擇,所以使用Jpa的一個好處是,可以更換實現而不必改動太多代碼。
2,Hibernate作為JPA的一種實現,jpa的注解已經是hibernate的核心,hibernate只提供了一些補充,而不是兩套注解。hibernate對jpa的支持夠足量,在使用hibernate注解建議使用jpa。
(上面兩句話是磚家的回答,http://zhidao.baidu.com/link?url=Crp8dwDmn1BrSTabvPu409AmbJhfQ91mASpvWHvhXkfImPA4LZ1PZvp5iTqzQ3x3RzkT4CMfh6n8Yl8pAgb_WK)
由此可見,是hibernate實現了jpa。而hibernate實現jpa的結果是怎樣呢?
我的理解是有兩點:1充分體現orm思想。2jpa的注解非常方便。
總結下,JPA和Hibernate之間的關系。可以簡單的理解為JPA是標准接口,Hibernate是實現。那么Hibernate是如何實現與 JPA 的這種關系的呢。
Hibernate主要是通過三個組件來實現的,及hibernate-annotation、hibernate-entitymanager和hibernate-core。
由此又可見,hibernate還可以不去實現jpa。那是怎樣的情景呢?如果你單獨學習hibernate便會體驗到了。
在hibernate中,通常配置對象關系映射關系有兩種,一種是基於xml的方式,另一種是基於annotation的注解方式。
前者不贅述,而后者基於annotation的注解方式與jpa聯系緊密,因為它包含jpa的標准,並添加少量hibernate獨有的。
而在jpa出現之前,hibernate配置對象關聯映射只有基於xml的方式。
我是最不喜歡寫那種任何實體-數據庫映射信息都寫在配置文件xml里面的那種項目。所以使用注解是我心目中先進有力的方式。
所以,我選擇spring+jpa!
二,Spring MVC + jpa框架搭建思路分析
spring mvc也就是一個三層架構而已,最底下是:持久層。中間是:控制層。上層是:頁面。
為啥要分三層呢?因為最上層不需要與最底層聯通。按照軟件開發術語叫解耦。
所以,搭建spring mvc就是先搞,上層和中間,再搞中間和下層。
三,上層和中間
配置文件spring-mvc.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:mvc="http://www.springframework.org/schema/mvc" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation= "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd"> <!-- 這個標簽注冊了Spring MVC分發請求到控制器所必須的DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter實例 --> <mvc:annotation-driven/> <context:component-scan base-package="com.controller"/> <!-- 定義視圖解析器viewResolver --> <bean id = "viewResolver" class = "org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name = "prefix" value = "/WEB-INF/jsp/"/> <property name = "suffix" value = ".jsp"/> </bean> </beans>
這個文件其實就是定義了對前端請求的處理,以及處理結果怎樣給到前端。也就是處理,上層-中間層間的關系。
具體講就是,spring先攔截了前端的請求,這個攔截配置在web.xml里面。不贅述。
前端的請求要找到對應的controller類。這時spring要通過注解。<context:component-scan base-package="com.controller"/>這一句,表明要自動掃描,com.controller包。
package com.controller; import java.util.List; import javax.annotation.Resource; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import com.entity.User; import com.service.UserService; @Controller @RequestMapping("/path01/path02") public class HelloController { @Resource(name = "userServiceImpl") private UserService userService; @RequestMapping("/hello.form") public String execute(Model model) throws Exception{ List<User> users = userService.findAllUser(); model.addAttribute("users",users); return "hello"; } }
有了@Controller這個注解就會被掃描到。
<!-- 定義視圖解析器viewResolver -->
<bean id = "viewResolver"
class = "org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name = "prefix" value = "/WEB-INF/jsp/"/>
<property name = "suffix" value = ".jsp"/>
</bean>
這句定義怎樣將處理結果返回。返回到/WEB-INF/jsp/下面的jsp頁面。
<%@page pageEncoding="utf-8" contentType="text/html;charset=utf-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <!DOCTYPE html> <html> <head> <title>Spring Hello World!</title> </head> <body> hello Spring MVC,日向 2016-03 <c:forEach items="${users}" var="user"> id為${user.id}||用戶名為${user.username} </c:forEach> </body> </html>
關鍵是,<mvc:annotation-driven/>這一句。
<mvc:annotation-driven /> 是一種簡寫形式,完全可以手動配置替代這種簡寫形式,簡寫形式可以讓初學都快速應用默認配置方案。
<mvc:annotation-driven /> 會自動注冊DefaultAnnotationHandlerMapping與AnnotationMethodHandlerAdapter 兩個bean,是spring MVC為@Controllers分發請求所必須的。
(from:http://kingliu.iteye.com/blog/1972973)
所以<mvc:annotation-driven />的本質其實是DefaultAnnotationHandlerMapping與AnnotationMethodHandlerAdapter 兩個bean。
網上可以借鑒的回答:
http://kingliu.iteye.com/blog/1972973
http://zhidao.baidu.com/link?url=CvJjOeWrEtf8UXaiowxsDk2rDYHPm7h7GjKMb25Fdu6fWGEsoB3II4gORHOL6U_M3V7JLVfYfQVmDZhjzyIj82O8Vopjghl7Nv_-h9noYoS
四,中間和下層
spring-common.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd"> <!-- 配置數據源 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" > <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost/db_shop"></property> <property name="username" value="root"></property> <property name="password" value="1234"></property> </bean> <!-- LocalContainerEntityManagerFactoryBean spring配置jpa的三種方式之一,適用於所有環境的FactoryBean ,能全面控制EntityManagerFactory配置 --> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource"/> <!-- 設定為自動掃描,spring新特性,有了packagesToScan,我們不再需要自己動手去實現實體類的掃描了 --> <property name="packagesToScan" value="com.entity"/> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="showSql" value="true"/> <!-- generateDdl= true表示自動生成DDL--> <property name="generateDdl" value="true" /> <property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect"/> </bean> </property> </bean> <!-- 定義事務管理器 --> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <!-- 注解掃描 --> <context:component-scan base-package="com" /> <!--對@Transactional這個注解進行的驅動,這是基於注解的方式使用事務配置聲明,這樣在具體應用中可以指定對哪些方法使用事務。 --> <tx:annotation-driven transaction-manager="transactionManager"/> </beans>
dataSource作用:
public interface DataSource
該工廠用於提供到此 DataSource 對象表示的物理數據源的連接。作為 DriverManager 設施的替代項,DataSource 對象是獲取連接的首選方法。實現 DataSource 接口的對象通常在基於 JavaTM Naming and Directory Interface (JNDI) API 的命名服務中注冊。 DataSource 接口由驅動程序供應商實現。共有三種類型的實現: 基本實現 - 生成標准 Connection 對象
連接池實現 - 生成自動參與連接池的 Connection 對象。此實現與中間層連接池管理器一起使用。
分布式事務實現 - 生成一個 Connection 對象,該對象可用於分布式事務,並且幾乎始終參與連接池。此實現與中間層事務管理器一起使用,並且幾乎始終與連接池管理器一起使用。
DataSource 對象的屬性在需要時可以修改。例如,如果將數據源移動到另一個服務器,則可更改與服務器相關的屬性。其優點是,因為可以更改數據源的屬性,所以任何訪問該數據源的代碼都無需更改。 通過 DataSource 對象訪問的驅動程序不會向 DriverManager 注冊。通過查找操作檢索 DataSource 對象,然后使用該對象創建 Connection 對象。使用基本的實現,通過 DataSource 對象獲取的連接與通過 DriverManager 設施獲取的連接相同。
entityManagerFactory:
配置entityManagerFactory說明這是配置的jpa。因為我記得hibernate是配置sessionFactory的。
entityManagerFactory,顧名思義,就是managerFactory的工廠。我們使用的類是LocalContainerEntityManagerFactoryBean。這表明是本地容器托管的,也就決定了是使用注解@PersistenceContext來獲取EntityManager對象的。
entityManagerFactory下面的一些具體配置就不詳解了,代碼中大致注釋了。
transactionManager:
設置jpa事務管理器。事務就是對一系列的數據庫操作進行統一的提交或回滾操作。這樣可以防止在一些意外(例如說突然斷電)的情況下出現亂數據,防止數據庫數據出現問題。
五,其它一些代碼
User:
package com.entity; import javax.persistence.Entity; import javax.persistence.Id; @Entity public class User { private int id; private String username; @Id public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } }
UserDao:
package com.dao; import java.util.List; import com.entity.User; public interface UserDao { public List<User> findAllUsers(); }
UserDaoImpl:
package com.daoimpl; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.FlushModeType; import javax.persistence.PersistenceContext; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import com.dao.UserDao; import com.entity.User; @Transactional @Repository("userDaoImpl") public class UserDaoImpl implements UserDao{ @PersistenceContext protected EntityManager entityManager; @Override public List<User> findAllUsers() { CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); CriteriaQuery<User> criteriaQuery = criteriaBuilder.createQuery(User.class); Root<User> root = criteriaQuery.from(User.class); criteriaQuery.select(root); Predicate restrictions = criteriaBuilder.conjunction(); criteriaQuery.where(restrictions); return entityManager.createQuery(criteriaQuery).setFlushMode(FlushModeType.COMMIT).getResultList(); } }
這段代碼是實際的持久化操作的代碼。通過@PersistenceContext注解獲取,EntityManager,進行持久化操作。@Transactional表示將事務處理托付給spring。
這段代碼實現的功能就是從數據庫中查詢所有的User。
UserService:
package com.service; import java.util.List; import com.entity.User; public interface UserService { public List<User> findAllUser(); }
UserServiceImpl:
package com.serviceimpl; import java.util.List; import javax.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.dao.UserDao; import com.entity.User; import com.service.UserService; @Transactional @Service("userServiceImpl") public class UserServiceImpl implements UserService{ @Resource(name = "userDaoImpl") private UserDao userDao; @Override public List<User> findAllUser() { return userDao.findAllUsers(); } }
web.xml:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <display-name></display-name> <!-- 加載所有的配置文件 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:/spring-*.xml</param-value> </context-param> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:/spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>*.form</url-pattern> </servlet-mapping> <!-- 配置Spring監聽 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
六,運行
簡簡單單,毫無一點裝飾的一個網頁,但在服務器里進行了一系列的代碼邏輯……
總體上,這個效果就是:用戶輸入這個地址,找到我們的服務器地址,localhost8080,我們的項目,RiXiangShop,然后通過spring解析路徑,通過@Controller標簽找到對應的controller,通過標簽注入找到對應的service,再找到對應的dao,然后進行數據庫持久化操作--查詢所有用戶,以List的數據結構返回,返回到service,返回到controller,response給前端jsp頁面,jsp通過jstl將查詢到的用戶的id和名字打印到屏幕上。
所以說,這個框架是個雛形,你加入更多的邏輯,它就可以演化為一個電子商務網站,一個博客。
你加入更多更多邏輯,它也可以演化為阿爾法狗。
我希望將之演化為一個博客,再實踐開發中搞更多技術。
因為這是第一個我完全獨立自主搭建,並理解的框架。
我將用兩個月時間開發。到六月,期待開發成功。
之后希望可以搞個自己的域名,建一個自己的個人網站。
然后,在維護開發中繼續搞更多技術。