SpringBoot使用JPA實現增刪查改


運行環境

  • SpringBoot2.3.0
  • JDK1.8
  • IDEA2020.1.2
  • MySQL5.7

依賴及應用程序配置

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
    </exclusions>
</dependency>
  1. 升級到SpringBoot2.2,spring-boot-starter-test默認使用JUnit 5作為單元測試框架 ,寫單元測試時注解@RunWith(Spring.class)升級為@ExtendWith(SpringExtension.class)
  2. 升級到SpringBoot2.3,hibernate-validator從spring-boot-starter-web移除,需要單獨引入
  3. 升級到SpringBoot2.3,MySQL驅動由com.mysql.jdbc.Driver變更為com.mysql.cj.jdbc.Driver;同時,數據源url需要添加serverTimezone=UTC&useSSL=false參數
  4. 升級到SpringBoot2.x,默認不自動注入HiddenHttpMethodFilter ,需要設置spring.mvc.hiddenmethod.filter.enabled=true開啟PUT、DELETE方法支持

應用程序配置如下:

spring.application.name=springbootjpa
management.endpoints.jmx.exposure.include=*
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
# 應用服務 WEB 訪問端口
server.port=8080
# Actuator Web 訪問端口
management.server.port=8081

# mysql setting
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springbootjpa?serverTimezone=UTC&useSSL=false
spring.datasource.username=username
spring.datasource.password=password

# JPA setting
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql=true

# thymeleaf setting
spring.thymeleaf.cache=false

# delete、put方法支持
spring.mvc.hiddenmethod.filter.enabled=true

定義實體

使用@Entity標記實體類

import lombok.Data;

import javax.persistence.*;
import javax.validation.constraints.NotEmpty;
import java.io.Serializable;

@Entity
@Data
public class Article extends BaseEntity implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, unique = true)
    @NotEmpty(message = "標題不能為空")
    private String title;

    @Column(nullable = false)
    private String body;
}

為了自動添加創建日期、修改日期、創建人及修改人,我們把創建、修改信息放到父類中由實體類繼承,並開啟SpringBoot的自動審計功能,將創建/修改信息自動注入

1、定義實體類的父類,@CreatedDate、@LastModifiedDate、@CreatedBy、@LastModifiedBy標注相應字段

import org.hibernate.annotations.Columns;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseEntity {
    @CreatedDate
    private Long createTime;

    @LastModifiedDate
    private Long updateTime;
    
    @Column(name = "create_by")
    @CreatedBy
    private String createBy;

    @Column(name = "lastmodified_by")
    @LastModifiedBy
    private String lastmodifiedBy;
    
    public Long getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Long createTime) {
        this.createTime = createTime;
    }

    public Long getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(Long updateTime) {
        this.updateTime = updateTime;
    }
    
    public String getCreateBy() {
        return createBy;
    }

    public void setCreateBy(String createBy) {
        this.createBy = createBy;
    }

    public String getLastmodifiedBy() {
        return lastmodifiedBy;
    }

    public void setLastmodifiedBy(String lastmodifiedBy) {
        this.lastmodifiedBy = lastmodifiedBy;
    }
}

@MappedSuperclass注解:

  • 作用於實體類的父類上,父類不生成對應的數據庫表
  • 標注@MappedSuperclass的類不能再標注@Entity或@Table注解,也無需實現序列化接口
  • 每個子類(實體類)對應一張數據庫表,數據庫表包含子類屬性和父類屬性
  • 標注@MappedSuperclass的類可以直接標注@EntityListeners實體監聽器

@EntityListeners(AuditingEntityListener.class)注解:

  • 作用范圍僅在標注@MappedSuperclass類的所有繼承類中,並且實體監聽器可以被其子類繼承或重載
  • 開啟JPA的審計功能,需要在SpringBoot的入口類標注@EnableJpaAuditing

創建日期、修改日期有默認方法注入值,但創建人和修改人注入則需要手動實現AuditorAware接口:

@Configuration
public class BaseEntityAuditor implements AuditorAware<String> {
    @Override
    public Optional<String> getCurrentAuditor() {
        return "";
    }
}

DAO層實現

JPA支持通過約定方法名進行數據庫查詢、修改:

import org.springframework.data.jpa.repository.JpaRepository;
import springbootjpa.entity.Article;

public interface ArticleRepository extends JpaRepository<Article, Long> {
    Article findById(long id);
}

通過約定方法名查詢,只需實現JpaRepository接口聲明查詢方法而不需要具體實現

此外,可以在方法上標注@Query實現JPQL或原生SQL查詢

JpaRepository<T, ID>,T表示要操作的實體對象,ID表示主鍵。該接口繼承了分頁排序接口PadingAndSortRepository,通過構建Pageable實現分頁查詢:

@Autowired
private ArticleRepository articleRepository;

@RequestMapping("")
public ModelAndView articlelist(@RequestParam(value = "start", defaultValue = "0") Integer start, @RequestParam(value = "limit", defaultValue = "5") Integer limit) {
        start = start < 0 ? 0 : start;
        Sort sort = Sort.by(Sort.Direction.DESC, "id");
        Pageable pageable = PageRequest.of(start, limit, sort);
        Page<Article> page = articleRepository.findAll(pageable);
        ModelAndView modelAndView = new ModelAndView("article/list");
        modelAndView.addObject("page", page);
        return modelAndView;
}

如果根據某一字段排序,可以用Sort.by方法構建Sort對象;如果根據多個字段排序,首先構建Sort.Order數組List<Sort.Order>,然后再傳入Sort.by方法構建Sort對象。

PageRequest.of方法生成Pageable對象

Contrller控制器

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import springbootjpa.entity.Article;
import springbootjpa.repository.ArticleRepository;

@Controller
@RequestMapping("/article")
public class ArticleController {
    @Autowired
    private ArticleRepository articleRepository;

    @RequestMapping("")
    public ModelAndView articlelist(@RequestParam(value = "start", defaultValue = "0") Integer start,
                                    @RequestParam(value = "limit", defaultValue = "5") Integer limit) {
        start = start < 0 ? 0 : start;
        Sort sort = Sort.by(Sort.Direction.DESC, "id");
        Pageable pageable = PageRequest.of(start, limit, sort);
        Page<Article> page = articleRepository.findAll(pageable);
        ModelAndView modelAndView = new ModelAndView("article/list");
        modelAndView.addObject("page", page);
        return modelAndView;
    }

    @RequestMapping("/add")
    public String addArticle() {
        return "article/add";
    }

    @PostMapping("")
    public String saveArticle(Article article) {
        articleRepository.save(article);
        return "redirect:/article";
    }

    @GetMapping("/{id}")
    public ModelAndView getArticle(@PathVariable("id") Integer id) {
        Article article = articleRepository.findById(id);
        ModelAndView modelAndView = new ModelAndView("article/show");
        modelAndView.addObject("article", article);
        return modelAndView;
    }

    @DeleteMapping("/{id}")
    public String deleteArticle(@PathVariable("id") long id) {
        System.out.println("put 方法");
        articleRepository.deleteById(id);
        return "redirect:/article";
    }

    @GetMapping("edit/{id}")
    public ModelAndView editArticle(@PathVariable("id") Integer id) {
        Article article = articleRepository.findById(id);
        ModelAndView modelAndView = new ModelAndView("article/edit");
        modelAndView.addObject("article", article);
        return modelAndView;
    }

    @PutMapping("/{id}")
    public String editArticleSave(Article article, long id) {
        System.out.println("put 方法");
        article.setId(id);
        articleRepository.save(article);
        return "redirect:/article";
    }
}

因為<form>表單只能發送GETPOST請求,spring3引入一個監聽器HiddenHttpMethodFilter 來將POST請求轉換為PUTPOST請求。

SpringBoot2.x開始默認不自動注入HiddenHttpMethodFilter ,需要設置spring.mvc.hiddenmethod.filter.enabled=true開啟PUT、DELETE方法支持

配置完后,前端頁面需要在表單中加入隱藏域,表明實際請求方法:

<!-- DELETE 請求 -->
<form id="deletePost" method="POST" action="">
    <input type="hidden" name="_method" value="delete">
</form>


<!-- PUT 請求 -->
<form id="putPost" method="POST" action="">
    <input type="hidden" name="_method" value="put">
</form>

其他

th:valueth:field區別:

  • th:value解析成html,表現為:value="${th:value}"
  • th:field解析成html,表現為:name="${th:name}" value="${th:value}"


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM