Spring orm +Spring data jpa 入門

系統架構

使用 Spring orm 開發 JPA 數據持久層,用 Spring data jpa 簡化 DAO實現,真的不用寫 SQL 了

先導包:pom

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.cheery.recruitment</groupId>
    <artifactId>Recruitment-Webservice</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>Recruitment-Webservice</name>
    <description>Recruitment-Webservice</description>

    <dependencies>
        <!-- spring-context:  spring 框架核心jars -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!-- spring-orm: 包含 orm 開發的jars,事務也會有 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!-- spring-data-jpa -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>2.0.2.RELEASE</version>
        </dependency>
        <!-- hibernate-core -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>5.2.12.Final</version>
        </dependency>
        <!-- mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.45</version>
        </dependency>
        <!-- commons-dbcp2 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-dbcp2</artifactId>
            <version>2.1.1</version>
        </dependency>

    </dependencies>

</project>

架構一覽:

Spring 核心,依賴注入,並用容器管理 JPA EntityManager …
JPA , 持久化API ,實現選擇 Hibernate
MySQL ,數據庫
DBCP, 連接池
Spring data jpa, 簡化 DAO 實現

完整示例一個

招聘信息,公司發佈招聘信息,查詢相關信息。

1、 ORM 設計

很簡單,兩個 Entity ,在 domain 包下面:

Company: 公司類,name 唯一、非空 ,city非空(方便測試)

package com.cheery.recruitment.domain;

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

@Entity
@Table(name="company")
public class Company {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private int id;

    @Column(nullable=false,unique=true)
    private String name; // 名稱:阿里巴巴
    @Column(nullable=false)
    private String city; // 城市: 杭州
    private String address; // 公司地址
    private String website;// 公司網站
    private String industry;// 行業
    private String description;// 簡介

    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 getWebsite() {
        return website;
    }

    public void setWebsite(String website) {
        this.website = website;
    }

    public String getIndustry() {
        return industry;
    }

    public void setIndustry(String industry) {
        this.industry = industry;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }
}

Recruitment :招聘信息類,外鍵關聯 company_id(one-to-one), job+company 構成唯一性約束,城市和工資非空(方便測試)

package com.cheery.recruitment.domain;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

@Entity
@Table(name = "recruitment", uniqueConstraints = { @UniqueConstraint(columnNames = { "job", "company_id" }) })
public class Recruitment {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @Column(nullable = false)
    private String job; // 崗位:Java高級工程師
    @OneToOne
    @JoinColumn(name = "company_id", nullable = false)
    private Company company; // 公司:阿里巴巴
    private String city;// 工作地點:杭州
    private int salary;// 薪酬,月薪:20000
    private String education;// 學歷要求:小學
    private short experience;// 工作經驗:5, 0(應屆生)
    private Date publishTime;// 發佈時間:2017-12-25
    private Date deadline;// 有效期至:201s7-12-16, null(長期有效)
    private String industry; // 行業:IT
    private String responsibility; // 崗位職責:1.2.3...
    private String requirement;// 崗位要求:1.2.3...
    private String welfare;// 福利:五險一金,100個月年終獎...

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }

    public Company getCompany() {
        return company;
    }

    public void setCompany(Company company) {
        this.company = company;
    }

    public String getEducation() {
        return education;
    }

    public void setEducation(String education) {
        this.education = education;
    }

    public short getExperience() {
        return experience;
    }

    public void setExperience(short experience) {
        this.experience = experience;
    }

    public Date getPublishTime() {
        return publishTime;
    }

    public void setPublishTime(Date publishTime) {
        this.publishTime = publishTime;
    }

    public Date getDeadline() {
        return deadline;
    }

    public void setDeadline(Date deadline) {
        this.deadline = deadline;
    }

    public String getIndustry() {
        return industry;
    }

    public void setIndustry(String industry) {
        this.industry = industry;
    }

    public String getResponsibility() {
        return responsibility;
    }

    public void setResponsibility(String responsibility) {
        this.responsibility = responsibility;
    }

    public String getRequirement() {
        return requirement;
    }

    public void setRequirement(String requirement) {
        this.requirement = requirement;
    }

    public String getWelfare() {
        return welfare;
    }

    public void setWelfare(String welfare) {
        this.welfare = welfare;
    }

}

2. DAO 實現

使用 spring data jpa 實現 dao 不要太簡單
核心原理:繼承 JpaRepository 的接口後,框架會自動生成對應的實現類,並實現了一系列的方法,CRUD 根本沒問題。要啓用這個功能,必須進行配置,註解實現是這樣的:

@EnableJpaRepositories(basePackages="com.cheery.recruitment.repository")
package com.cheery.recruitment.repository;

import java.util.List;

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

import com.cheery.recruitment.domain.Company;

public interface CompanyRepo extends JpaRepository<Company, Integer>{   

    //注意哦,這個方法 spring data jpa 也會動態生成實現的哦!
    public List<Company> readByCity(String city);   
    public Company findCompanyByName(String name);

}
package com.cheery.recruitment.repository;

import java.util.List;

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

import com.cheery.recruitment.domain.Recruitment;

public interface RecruitmentRepo extends JpaRepository<Recruitment, Long> {

    // 指定城市工資超過多少的指定工作, 這個方法也會動態實現的哦!
    public List<Recruitment> readByJobLikeAndCityAndSalaryGreaterThanEqual(String job,String city,int salary);

}

說明:

  1. 上面兩個接口都實現了 JpaRepository , spring data jpa 會在 Spring application context 創建之後創建實現類,默認情況下,實現類名=接口類名+Impl 。也就是說,會創建兩個實現類, CompanyRepoImpl 和 RecruitmentRepoImpl 。
  2. JpaRepository 接口及其父接口定義的方法,實現類都會自動實現,同時,Spring data jpa 還支持定製的查詢方法,如 “readByJobLikeAndCityAndSalaryGreaterThanEqual”,這個方法符合,這些方法稱爲 “Repository methods”。
    他們的格式是:查詢動詞+查詢對象(可省略)+By+查詢條件
    這裏 read 是查詢動詞,與 get和find作用相同,還有一個查詢動詞 count 。這個方法省略了對象聲明 “Recruitments”,所以 readRecruitments… 效果一樣。
    “JobLikeAndCityAndSalaryGreaterThanEqual”是查詢條件,多個條件用 And 或 Or 組合,條件可以是 SQL支持的多種之一,這裏用到的是,Like (job 模糊查詢), =(city =‘杭州’),>=(salary >=30000)
  3. 如果 Repository methods ,還不能滿足需求,可以再定義一個DAO 接口,如 CompanyCustRepo , 在其中聲明定製的方法。然後讓實現類實現該接口,如 CompanyRepoImpl , 你只要在該類中實現 CompanyCustRepo 的方法,CompanyRepo 中的方法自動合併到這個類實現中,前提是,這個類名只能是CompanyRepo +Impl 不能隨便改了。

JPA 配置

由於該 demo 用 java app 測試,所以配置相對簡單,使用java 註解的配置方式

RepoConfig:Dao 配置

package com.cheery.recruitment.configure;

import java.util.HashMap;
import java.util.Map;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

import org.apache.commons.dbcp2.BasicDataSource;
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@ComponentScan(basePackages="com.cheery.recruitment.repository")
@EnableJpaRepositories(basePackages="com.cheery.recruitment.repository")
@EnableTransactionManagement
public class RepoConfig {

    @Bean(destroyMethod = "close")
    public DataSource dataSource() {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql:///recruitment");
        dataSource.setUsername("XXX");
        dataSource.setPassword("XXX");
        return dataSource;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
        factoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
        factoryBean.setDataSource(dataSource());
        factoryBean.setPackagesToScan("com.cheery.recruitment.domain");
        factoryBean.setJpaPropertyMap(jpaProperties());
        return factoryBean;
    }

    @Bean
    @Autowired
    public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory);
        return transactionManager;
    }

    private Map<String, ?> jpaProperties() {
        Map<String, String> jpaPropertiesMap = new HashMap<String, String>();
        jpaPropertiesMap.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
        jpaPropertiesMap.put("hibernate.hbm2ddl.auto", "update");
        return jpaPropertiesMap;
    }

}

@Configuration : 聲明這是配置類
@ComponentScan(basePackages=”com.cheery.recruitment.repository”) : 啓用基於註解的 bean 定義
@EnableJpaRepositories(basePackages=”com.cheery.recruitment.repository”) :啓用 Spring data jpa JpaRepositories 功能
@EnableTransactionManagement:啓用事務管理

ServiceConfig : Service 層配置

package com.cheery.recruitment.configure;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@ComponentScan(basePackages="com.cheery.recruitment.service")
@EnableTransactionManagement
public class ServiceConfig {    

}

4.Service 層

package com.cheery.recruitment.service;


import java.util.List;

import com.cheery.recruitment.domain.Company;

public interface CompanyService {

    public boolean saveCompany(Company company);
    public Company getCompanyByName(String name);
    public List<Company> findAllCompanies();
    public List<Company> findCompaniesByCity(String city);  

}
package com.cheery.recruitment.service;

import java.util.List;

import javax.transaction.Transactional;

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

import com.cheery.recruitment.domain.Company;
import com.cheery.recruitment.repository.CompanyRepo;

@Service
@Transactional
public class CompanyServiceImpl implements CompanyService {

    @Autowired
    private CompanyRepo companyDao;

    @Override
    public boolean saveCompany(Company company) {
        if (companyDao.save(company) == null) {
            return false;
        } else {
            return true;
        }
    }

    @Override
    public List<Company> findAllCompanies() {
        return companyDao.findAll();
    }

    @Override
    public List<Company> findCompaniesByCity(String city) {
        return companyDao.readByCity(city);
    }

    @Override
    public Company getCompanyByName(String name) {
        return companyDao.findCompanyByName(name);
    }

}
package com.cheery.recruitment.service;

import java.util.List;

import com.cheery.recruitment.domain.Recruitment;

public interface RecruitmentService {

    public boolean saveRecruitment(Recruitment recruitment);
    public List<Recruitment> findAllRecruitments();
    public List<Recruitment> findRecruitmentsByCriteria(String job,String city,int salary);

}
package com.cheery.recruitment.service;

import java.util.List;

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

import com.cheery.recruitment.domain.Recruitment;
import com.cheery.recruitment.repository.RecruitmentRepo;

@Service
public class RecruitmentServiceImpl implements RecruitmentService {

    @Autowired
    private RecruitmentRepo recruitmentDao;

    @Override
    public boolean saveRecruitment(Recruitment recruitment) {
        if (recruitmentDao.save(recruitment) == null) {
            return false;
        } else {
            return true;
        }
    }

    @Override
    public List<Recruitment> findAllRecruitments() {
        return this.recruitmentDao.findAll();
    }

    @Override
    public List<Recruitment> findRecruitmentsByCriteria(String job, String city, int salary) {
        String jobCriteria="%"+job+"%";
        return this.recruitmentDao.readByJobLikeAndCityAndSalaryGreaterThanEqual(jobCriteria, city, salary);
    }

}

5. 應用層

package com.cheery.recruitment;

import java.util.List;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.cheery.recruitment.configure.RepoConfig;
import com.cheery.recruitment.configure.ServiceConfig;
import com.cheery.recruitment.domain.Company;
import com.cheery.recruitment.domain.Recruitment;
import com.cheery.recruitment.service.CompanyService;
import com.cheery.recruitment.service.RecruitmentService;

public class Main {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(RepoConfig.class,
                ServiceConfig.class);

        CompanyService companyService = (CompanyService) applicationContext.getBean("companyServiceImpl");
        RecruitmentService recruitService=(RecruitmentService) applicationContext.getBean("recruitmentServiceImpl");

        //加幾個公司試試
        initCompanyInfo(companyService);

        System.out.println("系統當前的公司列表:");
        printCompanies(companyService.findAllCompanies());

        System.out.println("系統當前的杭州公司列表:");
        printCompanies(companyService.findCompaniesByCity("杭州"));

        //加幾條招聘信息試試
        initReruitmentInfo(companyService, recruitService);

        //查找杭州工資超過30000的Java相關工作
        printRecruitments(recruitService.findRecruitmentsByCriteria("Java", "杭州", 30000));

    }

    private static void initCompanyInfo(CompanyService companyService) {
        // 新增三個公司
        Company alibaba = new Company();
        alibaba.setName("阿里巴巴");
        alibaba.setCity("杭州");
        Company neteasy = new Company();
        neteasy.setCity("杭州");
        neteasy.setName("網易");
        Company tencent = new Company();
        tencent.setCity("深圳");
        tencent.setName("騰訊");

        companyService.saveCompany(alibaba);
        companyService.saveCompany(neteasy);
        companyService.saveCompany(tencent);
    }

    private static void initReruitmentInfo(CompanyService companyService, RecruitmentService recruitService) {
        // 阿里巴巴發佈兩條招聘信息
        Recruitment ali_recruit1 = new Recruitment();
        ali_recruit1.setJob("Java初級工程師");
        ali_recruit1.setCity("杭州");
        ali_recruit1.setCompany(companyService.getCompanyByName("阿里巴巴"));
        ali_recruit1.setSalary(20000);
        recruitService.saveRecruitment(ali_recruit1);

        Recruitment ali_recruit2 = new Recruitment();
        ali_recruit2.setJob("Java高級工程師");
        ali_recruit2.setCity("杭州");
        ali_recruit2.setCompany(companyService.getCompanyByName("阿里巴巴"));
        ali_recruit2.setSalary(40000);
        recruitService.saveRecruitment(ali_recruit2);


        // 網易發佈兩條招聘信息
        Recruitment neteasy_recruit1 = new Recruitment();
        neteasy_recruit1.setJob("Java高級工程師");
        neteasy_recruit1.setCity("杭州");
        neteasy_recruit1.setCompany(companyService.getCompanyByName("網易"));
        neteasy_recruit1.setSalary(30000);
        recruitService.saveRecruitment(neteasy_recruit1);

        Recruitment neteasy_recruit2 = new Recruitment();
        neteasy_recruit2.setJob("Python高級工程師");
        neteasy_recruit2.setCity("杭州");
        neteasy_recruit2.setCompany(companyService.getCompanyByName("網易"));
        neteasy_recruit2.setSalary(35000);
        recruitService.saveRecruitment(neteasy_recruit2);

        // 騰訊發佈兩條招聘信息
        Recruitment tencent_recruit1 = new Recruitment();
        tencent_recruit1.setJob("Java開發工程師");
        tencent_recruit1.setCity("深圳");
        tencent_recruit1.setCompany(companyService.getCompanyByName("騰訊"));
        tencent_recruit1.setSalary(20000);
        recruitService.saveRecruitment(tencent_recruit1);

        Recruitment tencent_recruit2 = new Recruitment();
        tencent_recruit2.setJob("Java高級工程師");
        tencent_recruit2.setCity("深圳");
        tencent_recruit2.setCompany(companyService.getCompanyByName("騰訊"));
        tencent_recruit2.setSalary(40000);
        recruitService.saveRecruitment(tencent_recruit2);
    }

    private static void printCompanies(List<Company> companies) {
        for (Company c : companies) {
            System.out.println(c.getName());
        }
    }

    private static void printRecruitments(List<Recruitment> recruitments) {
        for (Recruitment r : recruitments) {
            String name=r.getJob()+"("+r.getCompany().getName()+")"; //招聘信息名稱:job(companyName)
            String salary=r.getSalary()+"元/月";
            System.out.println(name+"--"+r.getCity()+"--"+salary);
        }
    }
}

總結

在以業務爲中心的應用中,使用 orm 框架還是很有優勢的,而使用 orm框架,則使用JPA設計不依賴與特定提供商的代碼很有必要,這樣能輕易在供應商之間切換。就 JPA 實現來說,Hibernate 是不錯的選擇,也是 Spring orm 默認的選擇。

使用 spring data jpa 可以輕鬆實現 dao ,再也不用什麼泛型啊,反射啊,功能很強大!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章