1. 程式人生 > >Spring Data JPA入門

Spring Data JPA入門

pack 列表 1.0 stat 刪除 erp builder epo del

1. Spring Data JPA是什麽

它是Spring基於ORM框架、JPA規範封裝的一套JPA應用框架,可使開發者用極簡的代碼即可實現對數據的訪問和操作。它提供了包括增刪改查等在內的常用功能,且易於擴展。學習並使用Spring Data JPA可以極大提高開發效率。

2. Spring Data JPA 有什麽

主要看看Spring Data JPA 提供的編程接口

  • Repository:最頂層的接口,是一個空接口,目的是為了統一所有的Repository的類型,且能讓組件掃描時自動識別。
  • CrudRepository: Repository的子接口,提供CRUD的功能。
  • PagingAndSortingRepository: CrudRepository的子接口, 添加分頁排序。
  • JpaRepository: PagingAndSortingRepository的子接口,增加批量操作等。
  • JpaSpecificationExecutor: 用來做復雜查詢的接口。

接口繼承關系圖

    技術分享圖片

3. 利用Spring Data JPA建立簡單的用戶管理項目

3.1 搭建項目

3.1.1 數據庫建表

    技術分享圖片

3.1.2 用戶管理工程包結構

  • com.zang.usermanage.repository (存放自定義的數據操作接口)
  • com.zang.usermanage.model(存放實體模型)
  • com.zang.usermanage.service(存放服務層的接口和實現)
  • com.zang.usermanage.controller (如果是MVC項目可建立此包,存放控制器)
  • com.zang.usermanage.exception(存放異常類)
  • com.zang.usermanage.test(存放測試類)

    技術分享圖片

3.1.3 所有需要的jar包

  • Spring 基礎jar包(版本:4.1.5)
  • Spring Data Commons jar包(版本:1.9.2)
  • Spring Data JPA jar包(版本:1.7.2)
  • Hibernate 相關 jar包(版本:4.2.0)
  • MySQL數據庫連接包 (版本:5.1.21)
  • 日誌相關jar包
  • 其它jar包

3.1.4 配置與編碼

創建配置

配置文件一:applicationContext.xml(Spring上下文)

<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:jdbc="http://www.springframework.org/schema/jdbc"    
    xmlns:jee="http://www.springframework.org/schema/jee"   
    xmlns:tx="http://www.springframework.org/schema/tx"  
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"  
    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/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd  
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd  
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd  
        http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"  
    default-lazy-init="true">

    <!--第一步-->
    <!--定義服務層代碼存放的包掃描路徑-->
    <context:component-scan base-package="com.zang.usermanage.service" />

    <!--第二步-->
    <!--定義實體的工廠bean-->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="userPU" />
        <property name="persistenceXmlLocation" value="classpath:persistence.xml"></property>
    </bean>

    <!--第三步-->
    <!--定義事務管理器-->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>

    <!--第四步-->
    <!--定義repository接口的存放目錄-->
    <!--定義接口實現的後綴,通常用Impl-->
    <!--定義實體工廠的引用-->
    <!--定義事務管理器的引用-->
       <jpa:repositories base-package="com.zang.usermanage.repository"
                         repository-impl-postfix="Impl" 
                         entity-manager-factory-ref="entityManagerFactory" 
                         transaction-manager-ref="transactionManager"/>


    <!--第五步-->
    <!--聲明采用註解的方式申明事務-->
    <tx:annotation-driven transaction-manager="transactionManager"/>

</beans>

配置文件二:persistence.xml(管理持久化)

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
    <persistence-unit name="userPU" transaction-type="RESOURCE_LOCAL">
        <!--jpa的提供者-->
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <properties>
            <!--聲明數據庫連接的驅動-->
            <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
            <!--jdbc數據庫的連接地址-->
            <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/user_manage"/>
            <property name="hibernate.connection.username" value="root"/>
            <property name="hibernate.connection.password" value="123"/>
            <!--配置方言-->
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
            <!--激活查詢日誌功能-->
            <property name="hibernate.show_sql" value="true"/>
            <!--優雅地輸出Sql-->
            <property name="hibernate.format_sql" value="true"/>
            <!--添加一條解釋型標註-->
            <property name="hibernate.use_sql_comments" value="false"/>
            <!--配置如何根據java模型生成數據庫表結構,常用update,validate-->
            <property name="hibernate.hbm2ddl.auto" value="update"/>
        </properties>
    </persistence-unit>
</persistence>

創建類文件並編碼

User.java(用戶實體類),UserRepository.java(用戶的數據接口,繼承JpaRepository接口), UserService.java(用戶服務接口),UserServiceImpl.java(接口實現), UserNotFound.java(異常類,在查詢實體未找到時拋出)

類之間關系

    技術分享圖片

User.java(用戶實體類)

package com.zang.usermanage.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

/*
實體中常用的註解:
@Entity :聲明這個類是一個實體類
@Table:指定映射到數據庫的表格
@Id :映射到數據庫表的主鍵屬性,一個實體只能有一個屬性被映射為主鍵
@GeneratedValue:主鍵的生成策略
*/
@Entity
@Table(name="user")
public class User {

    @Id
    @GeneratedValue
    private Integer id;
    private String name;
    private String address;
    private String phone;
    
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
    
}

UserRepository.java(用戶的數據接口)

package com.zang.usermanage.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.zang.usermanage.model.User;

public interface UserRepository extends JpaRepository<User,Integer> {

}

3.2 實現簡單的增、刪、改、查

JpaRepository接口方法:

  • delete刪除或批量刪除
  • findAll查找所有
  • findOne查找單個
  • save保存單個或批量保存
  • saveAndFlush保存並刷新到數據庫

接口源碼如下:(實現類為org.springframework.data.jpa.repository.support.SimpleJpaRepository.class)

package org.springframework.data.jpa.repository;

import java.io.Serializable;
import java.util.List;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.PagingAndSortingRepository;

@NoRepositoryBean
public abstract interface JpaRepository<T, ID extends Serializable>
  extends PagingAndSortingRepository<T, ID>
{
  public abstract List<T> findAll();
  
  public abstract List<T> findAll(Sort paramSort);
  
  public abstract List<T> findAll(Iterable<ID> paramIterable);
  
  public abstract <S extends T> List<S> save(Iterable<S> paramIterable);
  
  public abstract void flush();
  
  public abstract <S extends T> S saveAndFlush(S paramS);
  
  public abstract void deleteInBatch(Iterable<T> paramIterable);
  
  public abstract void deleteAllInBatch();
  
  public abstract T getOne(ID paramID);
}

3.2.1 編碼實現

UserService.java(用戶服務接口)

package com.zang.usermanage.service;

import java.util.List;import com.zang.usermanage.exception.UserNotFound;
import com.zang.usermanage.model.User;

public interface UserService {
    //添加用戶
    public void addUser(User user);
    //修改用戶
    public User updateUser(User user) throws UserNotFound;
    //刪除用戶,根據用戶編號刪除
    public User deleteUser(int id) throws UserNotFound;
    //查詢單個用戶
    public User getUser(int id);
    //查詢所有用戶
    public List<User> getUsers(); 
}

UserServiceImpl.java(接口實現)

package com.zang.usermanage.service;


import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;

import com.zang.usermanage.exception.UserNotFound;
import com.zang.usermanage.model.User;
import com.zang.usermanage.repository.UserRepository;


@Service("userService")
public class UserServiceImpl implements UserService {
    
    //自動裝配
    @Autowired
    private UserRepository userRepository;

    //增加用戶調用save方法
    @Override
    public void addUser(User user) {
        userRepository.save(user);
    }

    //更新用戶先進行非空判斷,再對新用戶各個屬性進行非空判斷並賦值,最後調用save方法
    //自定義異常類 UserNotFound
    @Override
    public User updateUser(User user) throws UserNotFound {  
        User userUpdate = userRepository.findOne(user.getId());
        if (userUpdate==null)
            throw new UserNotFound();
        if (user.getName()!=null)
            userUpdate.setName(user.getName());
        if (user.getAddress()!=null)
            userUpdate.setAddress(user.getAddress());
        if (user.getPhone()!=null)
            userUpdate.setPhone(user.getPhone());
        userRepository.save(userUpdate);
        return userUpdate;
    }

    //刪除用戶先進行非空判斷,最後調用delete方法
    @Override
    public User deleteUser(int id) throws UserNotFound {
        User userDelete = userRepository.findOne(id);
        if (userDelete==null)
            throw new UserNotFound();
        userRepository.delete(userDelete);
        return userDelete;
    }

    //查詢單個,調用findOne方法
    @Override
    public User getUser(int id) {

        return userRepository.findOne(id);
    }

    //查詢所有,調用findAll方法
    @Override
    public List<User> getUsers() {

        return userRepository.findAll();
    }

}

3.2.2 測試

Client.java(測試類)

package com.zang.usermanage.test;

import java.util.List;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.domain.Page;

import com.zang.usermanage.exception.UserNotFound;
import com.zang.usermanage.model.User;
import com.zang.usermanage.service.UserService;


public class Client {
    
    public static void printUser(User user){
        StringBuilder sb = new StringBuilder();
        sb.append("id="+user.getId());
        sb.append("name="+user.getName());
        sb.append("address="+user.getAddress());
        sb.append("phone="+user.getPhone());
        System.out.println(sb.toString());
    }
    
    public static void testList(UserService userService){
        List<User> users = userService.getUsers();
        if (users!=null){
            for (int i=0; i<users.size();i++){
                printUser(users.get(i)); 
            }
        }
    }
    public static void testUpate(UserService userService) throws UserNotFound{
        User user = new User();
        user.setId(1);
        user.setAddress("北京路121號");
        userService.updateUser(user);
        testList(userService);   
    }
    
    public static void testAdd(UserService userService){
        User user = new User();
        user.setAddress("天河路120號");
        user.setName("小徐");
        user.setPhone("130000000");
        userService.addUser(user);
        testList(userService);
    }    
    
    public static void testDelete(UserService userService) throws UserNotFound{
        userService.deleteUser(7);
        testList(userService);
    }
    
    public static void main(String[] arg) throws UserNotFound{

        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) ac.getBean("userService");
        //testList(userService);
        testUpate(userService);
        //testAdd(userService);
        //testDelete(userService);
    }
}

3.3 實現復雜查詢

3.3.1 基於方法名解析的概念

JpaRepository支持接口規範方法名查詢。意思是如果在接口中定義的查詢方法符合它的命名規則,就可以不用寫實現

例如:findByName這個方法表示從數據庫中查詢Name這個屬性等於XXX的所有記錄,類似於SQL語句:select * from xxTable where name=xxx這種形式

這段話有兩個重點:

  • 方法名需要在接口中設定
  • 必須符合一定的命名規範

3.3.2 方法名構造方法

find+全局修飾+By+實體的屬性名稱+限定詞+連接詞+ ...(其它實體屬性)+OrderBy+排序屬性+排序方向 例如:findDistinctByFirstNameIgnoreCaseAndLastNameOrderByAgeDesc(String firstName,String lastName){......}

其中:Distinct是全局修飾(非必須),FirstName和LastName是實體的屬性名,And是連接詞,IgnoreCase是限定詞,Age是排序屬性,Desc是排序方向,限定詞和連接詞統稱為“關鍵詞”。

3.3.3 目前支持的關鍵詞

常用詞如下:

  • 全局修飾:Distinct,Top,First
  • 關鍵詞:IsNull,IsNotNull,Like,NotLike,Containing,In,NotIn,IgnoreCase,Between,Equals,LessThan,GreaterThan,After,Before...
  • 排序方向:Asc,Desc
  • 連接詞:And,Or

更多關鍵詞請查看官方在線文檔: https://docs.spring.io/spring-data/jpa/docs/2.0.4.RELEASE/reference/html/

3.3.4 嵌套實體方法命名規則

構詞法:主實體中子實體的名稱+ _ +子實體的屬性名稱

例如:List<Person> findByAddress_ZipCode(ZipCode zipCode) 表示查詢所有 Address(地址)的zipCode(郵編)為指定值的所有Person(人員)

3.3.5 代碼展示條件查詢、分頁、排序

UserRepository接口定義查詢方法

package com.zang.usermanage.repository;

import java.util.List;

import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;

import com.zang.usermanage.model.User;

public interface UserRepository extends JpaRepository<User,Integer> {
        
    //查詢需求: 從數據庫中查詢電話號碼(phone)以指定字符串開始(例如:136)的,並且地址(address)中包含指定字符串(例如:路)的記錄   提取前兩條,降序排列
    //select * from user where phone like ‘136%‘ and address like ‘%路%‘ order by phone desc limit 0,2
    List<User> findTop2ByPhoneStartingWithAndAddressContainingOrderByPhoneDesc(String phone,String address);
    List<User> findTop2ByPhoneStartingWithAndAddressContaining(String phone,String address,Sort sort);
    
    //分頁要用到Pageable接口
    Page<User> findByPhoneStartingWithAndAddressContaining(String phone,String address,Pageable pageable);
    
}

UserService.java

package com.zang.usermanage.service;

import java.util.List;

import org.springframework.data.domain.Page;

import com.zang.usermanage.exception.UserNotFound;
import com.zang.usermanage.model.User;

public interface UserService {
        
    //不分頁帶條件查詢
    public List<User> getUsersByConditionNoPage(String phone,String address);
    
    //帶分頁條件查詢(需要得到用戶列表並且得到分頁信息)
    public Page<User> getUsersByConditionWithPage(String phone,String address,Integer page,Integer pageSize);
    //帶分頁條件查詢(得到用戶列表)
    //public List<User> getUsersByCondition(String phone,String address,Integer page,Integer pageSize);
}

UserServiceImpl.java

package com.zang.usermanage.service;


import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.stereotype.Service;

import com.zang.usermanage.exception.UserNotFound;
import com.zang.usermanage.model.User;
import com.zang.usermanage.repository.UserRepository;


@Service("userService")
public class UserServiceImpl implements UserService {
    
    //自動裝配
    @Autowired
    private UserRepository userRepository;

    @Override
    public Page<User> getUsersByConditionWithPage(String phone,String address,Integer page,Integer pageSize) {
        
        
        //不排序
        Page<User> userPage = userRepository.findByPhoneStartingWithAndAddressContaining(phone,address,new PageRequest(page, pageSize));
        
        //排序(三種方法)
        
        //第一種排序方式
        //Page<User> userPage = userRepository.findByPhoneStartingWithAndAddressContaining(phone,address,new PageRequest(page, pageSize,new Sort(Direction.ASC,"name","phone")));

        //第二種排序方式
        //Order order = new Order(Direction.ASC,"phone");
        //Page<User> userPage = userRepository.findByPhoneStartingWithAndAddressContaining(phone,address,new PageRequest(page, pageSize,new Sort(order));        

        //第三種排序方式
        //List<Order> orders = new ArrayList<Order>();
        //orders.add(new Order(Direction.DESC,"name"));
        //orders.add(new Order(Direction.ASC,"phone"));
        //Page<User> userPage = userRepository.findByPhoneStartingWithAndAddressContaining(phone,address,new PageRequest(page, pageSize,new Sort(orders));

        return userPage;
        
    }

    /**
     * 不分頁
     */
    @Override
    public List<User> getUsersByConditionNoPage(String phone,String address) {
        
        return userRepository.findTop2ByPhoneStartingWithAndAddressContainingOrderByPhoneDesc(phone, address);

        //return userRepository.findTop2ByPhoneStartingWithAndAddressContaining(phone, address, new Sort(Direction.ASC,"phone"));

        //Order order = new Order(Direction.ASC,"phone");
        //return userRepository.findTop2ByPhoneStartingWithAndAddressContaining(phone, address, new Sort(order));

        //List<Order> orders = new ArrayList<Order>();
        //orders.add(new Order(Direction.DESC,"name"));
        //orders.add(new Order(Direction.ASC,"phone"));        
        //return userRepository.findTop2ByPhoneStartingWithAndAddressContaining(phone, address, new Sort(orders);
        
    }


}

Client.java(測試類)

package com.zang.usermanage.test;

import java.util.List;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.domain.Page;

import com.zang.usermanage.exception.UserNotFound;
import com.zang.usermanage.model.User;
import com.zang.usermanage.service.UserService;


public class Client {
    
    public static void printUser(User user){
        StringBuilder sb = new StringBuilder();
        sb.append("id="+user.getId());
        sb.append("name="+user.getName());
        sb.append("address="+user.getAddress());
        sb.append("phone="+user.getPhone());
        System.out.println(sb.toString());
        
    }
    
    /**
     * 打印分頁信息
     * @param pageInfo
     */
    public static <T> void printPageInfo(Page<T> pageInfo){
        StringBuilder sb = new StringBuilder();
        sb.append("當前是第幾頁="+pageInfo.getNumber()).append("\n");
        sb.append("當前頁查得的記錄數="+pageInfo.getNumberOfElements()).append("\n");
        sb.append("每頁需要查詢的條數="+pageInfo.getSize()).append("\n");
        sb.append("總共符合條件的記錄數="+pageInfo.getTotalElements()).append("\n");
        sb.append("總共的頁數是="+pageInfo.getTotalPages()).append("\n");
        System.out.println(sb.toString());
    }
    
    /**
     * 不分頁查詢
     * @param userService
     */
    public static void testListByConditonNoPage(UserService userService){
        
        List<User> users = userService.getUsersByConditionNoPage("136","路");
        
        if (users!=null){
            for (int i=0; i<users.size();i++){
                printUser(users.get(i));  
            }            
        }
    }    
    
    /**
     * 分頁查詢
     * @param userService
     */
    public static void testListByConditonWithPage(UserService userService){
        
        //傳入條件和分頁信息
        Page<User> userPage = userService.getUsersByConditionWithPage("136","路",0,2);
        //打印分頁信息
        printPageInfo(userPage);
        
        List<User> users = userPage.getContent();
        
        if (users!=null){
            for (int i=0; i<users.size();i++){
                printUser(users.get(i));            
            }        
        } 
    }
    

    public static void main(String[] arg) throws UserNotFound{

        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) ac.getBean("userService");

        //testListByConditonNoPage(userService);
        testListByConditonWithPage(userService);        
    }
}

效果:

技術分享圖片

Spring Data JPA入門