天天看點

Spring Data JPA 1.10.1 詳解二之快速Demo

一、maven配置檔案加入依賴

Spring Data JPA 依賴,最新穩定的版本為1.10.1.RELEASE,這裡需要說明下的是,其依然依賴hibernate JPA相關JAR,hibernate-core之類的是不需要的。hibernate是JPA規範的一種實作,是以需要加入其依賴。ehcache是hibernate二級緩存的配置,不是必須的。

<dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-jpa</artifactId>
        <version>1.10.1.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-ehcache</artifactId>
    <version>5.1.0.Final</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
	<artifactId>hibernate-validator</artifactId>
	<version>5.2.4.Final</version>
</dependency>
<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-entitymanager</artifactId>
	<version>5.1.0.Final</version>
</dependency>      

二、聲明持久層接口

這裡給大家說說幾個常見的很重要的核心類檔案,源碼下面也貼上了。

Spring Data JPA 1.10.1 詳解二之快速Demo

大家可以看到這個類的繼承結構,這裡值得說的是,Spring Data JPA的持久層的借口命名是有一套規範的,後面你加入的自定義方法滿足其規範之後,你的實作類都是可以不需要寫的,而且事務管理也幫你做好了。是不是覺得很神奇呢!

JpaRepository繼承了PagingAndSortingRepository、QueryByExampleExecutor,PagingAndSortingRepository又繼承了CrudRepository,CrudRepository 大家通過看名字也應該是幹啥的,其就是CRUD的,PagingAndSortingRepository裡面加了分頁的相關方法。

CrudRepository.java

package org.springframework.data.repository;

import java.io.Serializable;

@NoRepositoryBean
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {

	<S extends T> S save(S entity);

	<S extends T> Iterable<S> save(Iterable<S> entities);

	T findOne(ID id);

	boolean exists(ID id);

	Iterable<T> findAll();

	Iterable<T> findAll(Iterable<ID> ids);

	long count();

	void delete(ID id);

	void delete(T entity);

	void delete(Iterable<? extends T> entities);

	void deleteAll();
}

Repository.java 是一個空的接口

package org.springframework.data.repository;

import java.io.Serializable;

public interface Repository<T, ID extends Serializable> {

}

大夥可以看看其源碼,源碼裡面的注釋我就不貼出來了,大家看方法名應該就知道其具體的意思了。
package org.springframework.data.jpa.repository;

import java.io.Serializable;
import java.util.List;

import javax.persistence.EntityManager;

import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.QueryByExampleExecutor;

@NoRepositoryBean
public interface JpaRepository<T, ID extends Serializable>
		extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {

	List<T> findAll();

	List<T> findAll(Sort sort);

	List<T> findAll(Iterable<ID> ids);

	<S extends T> List<S> save(Iterable<S> entities);

	void flush();

	<S extends T> S saveAndFlush(S entity);

	void deleteInBatch(Iterable<T> entities);

	void deleteAllInBatch();

	T getOne(ID id);

	@Override
	<S extends T> List<S> findAll(Example<S> example);

	@Override
	<S extends T> List<S> findAll(Example<S> example, Sort sort);

}      

大家用maven的時候,可以将源碼順便下下來,myeclipse裡面設定一下即可,如圖所示:

Spring Data JPA 1.10.1 詳解二之快速Demo

我這裡的接口如下:

package com.example.dao;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;

import com.example.entity.UserInfo;

public interface UserDaoSpringJpa extends JpaRepository<UserInfo,String>{

	public List<UserInfo> findByUsername(String username);//注意這個命名,Spring會自動根據這個username去資料庫裡面取值,可以看到後面生成的sql語句,where條件裡面就加了 where user_name =?,這就是Spring Data JPA查詢命名的友善之處,隻要按規則命名,他會采取一定的政策,通過解析方法名稱建立查詢,來生成sql語句。
}      

JpaRepository<UserInfo,String> 這裡是泛型接口,UserInfo是我們的實體類,String是主鍵類型,也稱為id,大家以前寫hibernatebasedao的時候可能是見過了的。這叫 泛型限定式依賴注入,也是Spring4的一大特性。之前有篇博文也提到了 http://enetq.blog.51cto.com/479739/1783339

三、在接口中聲明自定義業務方法

Spring Data JPA将會根據指定的政策(後續會有文章說明,本此隻是簡單的介紹下,能跑起來。)為該方法生成實作代碼,使用者不需要實作該接口,這樣就隻需要寫接口就行了。

四、Spring配置檔案相關配置

配置檔案中加入 jpa:repositories,啟用掃描并自動建立代理 Spring 初始化容器時将會掃描 base-package 指定的包目錄及其子目錄,為繼承 Repository 或其子接口的接口建立代理對象,并将代理對象注冊為 Spring Bean,業務層便可以通過 Spring autowired來直接使用該對象。

此需要jpa命名空間:

xmlns:jpa="

xsi:schemaLocation="
       http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
       "      

具體的配置參加如下:各參數的含義配置檔案裡面已經給出了詳細的解釋,同時給出了相關配置類的源碼。

Spring Data JPA 1.10.1 詳解二之快速Demo

一些配置的屬性,大家在源碼裡面也是可以找到的。

Spring Data JPA 1.10.1 詳解二之快速Demo
 		 
<!-- 啟用掃描并自動建立代理 Spring 初始化容器時将會掃描 base-package 指定的包目錄及其子目錄,
	為繼承 Repository 或其子接口的接口建立代理對象,并将代理對象注冊為 Spring Bean,
	業務層便可以通過 Spring autowired來直接使用該對象。 -->
	<jpa:repositories base-package="com.example.service"
		 entity-manager-factory-ref="entityManagerFactory" 
 		 transaction-manager-ref="transactionManager"/> 
 
 <!-- 通過entityManagerFactory的createEntityManager()方法擷取EntityManager -->
    <bean id="entityManagerFactory"
          class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
          <!-- 指定資料源 -->
        <property name="dataSource" ref="dataSource"/>
        <property name="packagesToScan" value="com.example"/>
        <!-- 用于指定持久化實作廠商類 -->
        <property name="persistenceProvider">
            <bean class="org.hibernate.jpa.HibernatePersistenceProvider"/><!-- 替換 org.hibernate.ejb.HibernatePersistence -->
        </property>
        <!-- jpaVendorAdapter :用于設定實作廠商JPA實作的特定屬性,
        	目前Spring提供了HibernateJpaVendorAdapter、OpenJpaVendorAdapter、EclipseLinkJpaVendorAdapter、
      		 三個實作。 TopLinkJpaVendorAdapter在Spring4裡面已經删除了,
      		 目前支援的資料庫如下: DB2, DERBY, H2, HSQL, INFORMIX, MYSQL, ORACLE, POSTGRESQL, SQL_SERVER, SYBASE,
      		 這個類型在Spring提供的枚舉類型 Database 裡面有詳細定義-->
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="generateDdl" value="false"/>
                <property name="database" value="MYSQL"/>
                <property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
                <property name="showSql" value="true"/>
            </bean>
        </property>
        <!--    jpaDialect:用于指定一些進階特性,如事務管理,擷取具有事務功能的連接配接對象等,
		        目前Spring提供HibernateJpaDialect、OpenJpaDialect 、EclipseLinkJpaDialect -->
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
        </property>
        <!-- 用于指定JPA屬性,跟之前hibernateProperties是一緻的 -->
        <property name="jpaPropertyMap">
            <map>
        
                <entry key="hibernate.dialect" value="${hibernate.dialect}"/> 
				<entry key="hibernate.show_sql" value="${hibernate.show_sql}"/>
				<entry key="hibernate.format_sql" value="false"/>
				<entry key="hibernate.query.substitutions" value="${hibernate.query.substitutions}"/>
				<entry key="hibernate.default_batch_fetch_size" value="${hibernate.default_batch_fetch_size}"/>
				<entry key="hibernate.max_fetch_depth" value="${hibernate.max_fetch_depth}"/>
				<entry key="hibernate.generate_statistics" value="${hibernate.generate_statistics}"/>
				<entry key="hibernate.bytecode.use_reflection_optimizer" value="${hibernate.bytecode.use_reflection_optimizer}"/>

				<!-- 緩存Cache配置 -->
				<entry  key="hibernate.cache.provider_class" value="${hibernate.cache.provider_class}"/>
				<entry key="hibernate.cache.use_second_level_cache" value="${hibernate.cache.use_second_level_cache}"/>
				<entry key="hibernate.cache.use_query_cache" value="${hibernate.cache.use_query_cache}"/>
				<entry key="hibernate.cache.region.factory_class" value="${hibernate.cache.region.factory_class}"/>
				<entry key="net.sf.ehcache.configurationResourceName" value="${net.sf.ehcache.configurationResourceName}"/>
				<entry key="hibernate.cache.use_structured_entries" value="${hibernate.cache.use_structured_entries}"/>
                
            </map>
        </property>
    </bean>
    
    <!--指定事務管理器-->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>
 		      

jpaVendorAdapter 相關參數的設定AbstractJpaVendorAdapter 類裡面有詳細說明:如下圖所示:

Spring Data JPA 1.10.1 詳解二之快速Demo
Spring Data JPA 1.10.1 詳解二之快速Demo
package com.example.action;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;

import com.example.entity.UserInfo;
import com.example.service.impl.UserInfoService;
import com.google.gson.Gson;

@Controller
public class UserInfoAction {
	 
	@Autowired
	private UserInfoService userInfoService;
	 
	private Logger log = Logger.getLogger(this.getClass());

	@RequestMapping("findByUserName.do")
	public void findByUserName(HttpServletRequest request,
			HttpServletResponse response, ModelMap map) throws IOException{
		List<UserInfo> listUserInfo = userInfoService.findByUsername("wj");
		
		for(UserInfo userInfo:listUserInfo){
			log.info(userInfo.getUsername()+":"+userInfo.getEmail());
		}
		
		Gson gson = new Gson();
		String jsonStr = gson.toJson(listUserInfo);
		response.getWriter().println(jsonStr);
	}
	
	@RequestMapping("saveUser.do")
	public void saveUser(HttpServletRequest request,
			HttpServletResponse response, ModelMap map) throws IOException{
		List<UserInfo> listUserInfo = new ArrayList<UserInfo>();
		
		for(int i=0;i<10;i++){
			UserInfo userInfo = new UserInfo();
			
			userInfo.setId(UUID.randomUUID().toString());
			userInfo.setUsername(Math.random()+"wj"+i);
			userInfo.setPassword(Math.random()+"ss"+i);
			listUserInfo.add(userInfo);
		}
		
		listUserInfo = userInfoService.save(listUserInfo);//可以直接儲存一個list集合,當然這裡隻是測試,實際的批量新增,批量更新的時候,是需要到一定的條數之後,flush一下的,比如20條,這根hibernate是類似的。
		
		userInfoService.delete("21");//删除
		
		UserInfo userInfo = userInfoService.findOne("2eab2884-e0e9-419c-8871-1d198b399813");
		userInfo.setUsername("zhangsan");
		userInfoService.saveAndFlush(userInfo);//更新
		Gson gson = new Gson();
		String jsonStr = gson.toJson(listUserInfo);
		response.getWriter().println(jsonStr);
	}
}      
Hibernate: select userinfo0_.id as id1_4_, userinfo0_.birthday as birthday2_4_, userinfo0_.contact as contact3_4_, userinfo0_.create_time as create_t4_4_, userinfo0_.create_user as create_u5_4_, userinfo0_.delete_flag as delete_f6_4_, userinfo0_.email as email7_4_, userinfo0_.last_login_ip as last_log8_4_, userinfo0_.last_login_time as last_log9_4_, userinfo0_.last_logout_time as last_lo10_4_, userinfo0_.modify_time as modify_11_4_, userinfo0_.online_state as online_12_4_, userinfo0_.password as passwor13_4_, userinfo0_.register_time as registe14_4_, userinfo0_.sex as sex15_4_, userinfo0_.user_state as user_st16_4_, userinfo0_.username as usernam17_4_ from rbac.user_info userinfo0_ where userinfo0_.username=?

Hibernate: insert into rbac.user_info (birthday, contact, create_time, create_user, delete_flag, email, last_login_ip, last_login_time, last_logout_time, modify_time, online_state, password, register_time, sex, user_state, username, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

insert 語句列印了10條,


delete from rbac.user_info where id=?

Hibernate: update rbac.user_info set birthday=?, contact=?, create_time=?, create_user=?, delete_flag=?, email=?, last_login_ip=?, last_login_time=?, last_logout_time=?, modify_time=?, online_state=?, password=?, register_time=?, sex=?, user_state=?, username=? where id=?