Day 13 - Spring Boot & JPA

JPA,全名为Java Persistence API,是Sun 官方提出的Java 持久化规范,它为Java 开发人员提供了一种物件/关联映射工具来管理Java 应用中的关系实体物件,它的出现主要是为了简化现有的持久化开发工作和整合ORM 技术

Spring Data JPA 是Spring 在ORM 框架、JPA 规范的基础上封装了一套JPA 应用框架,可使开发者用极简的程序码即可实现对资料库的访问和操作

需要注意的是Spring Data JPA 新增与更新操作都是使用save() 方法进行,JPA 会透过主键去查询是否存在,不存在就INSERT,存在就是UPDATE,其中,JPA 只能判断INSERT 或是UPDATE并不能判断出是否更新部分栏位所以没有被赋值的栏位都会被覆盖为NULL

实作

新增依赖

<!-- JPA -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

设定JPA 基本配置

  • spring.jpa.hibernate.ddl-auto :
    • create : 每次载入Hibernate 时,会删除上次生成的资料表,再根据Model 类别重新生成资料表。

    • craete-drop : 每次载入Hibernate 时都根据Model 类别生成资料表,但sessionFactory 一关闭就自动删除资料表。

    • update : 第一次载入Hibernate 时根据Model 类别生成资料表,之後载入Hibernate 时根据Model 类别更新资料表,即使资料表结构改变,但不会删除以前的栏位。
      要注意的是,当部署到服务器後,不会马上建立资料表,要等第一次应用时才会建立

    • validate : 每次载入Hibernate 时验证建立资料表,只会和资料库中的资料表进行比较,不会建立新的资料表,但会插入新值。

    • none : 无动作。

  • spring.jpa.show-sql : 是否在Console 显示执行的SQL 语句。
# JPA 设定
spring.jpa.hibernate.ddl-auto=update

# 显示SQL 语句
spring.jpa.show-sql=true

启用JPA 自动赋值

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@SpringBootApplication
@EnableJpaAuditing
public class SpringBootDemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringBootDemoApplication.class, args);
	}

}

调整实体类

  • @Entity : 宣告为实体类。
  • @Table : 对应资料表名称。
  • @Id : 设定为PRIMARY KEY。
  • @GeneratedValue : 设定该Column 生成方式。
    • GenerationType.AUTO : 自动产生。
    • GenerationType.IDENTITY : 资料库维护。
  • @Column : 对应资料表栏位名称。
package com.example.demo.entity;

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

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
@Entity
@Table(name = "member_account")
public class MemberAccount extends Base {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "ID")
	private String id;

	@Column(name = "USERNAME", unique = true)
	private String username;

	@Column(name = "PASSWORD")
	private String password;

	@Column(name = "SALT")
	private String salt;

}
  • @MappedSuperclass : 宣告为实体类的父类别,它并不是完整的实体类,所以不会映射到资料表,但它的属性会映射到它的子类别资料表中,可用於设定相同栏位。
  • @EntityListeners(AuditingEntityListener.class) : 监听器,自动赋值创建时间、修改时间
  • @CreateBy : 设定为建立者,当实体被INSERT 时会预设值。
  • @CreatedDate : 设定为建立时间,当实体被INSERT 时会预设值。
  • @Temporal(TemporalType.TIMESTAMP) : 宣告该Column 储存到资料表时的时间精度,该注解仅适用於被宣告为java.util.DATE 或java.util.Calendar 的属性。
    • TemporalType.DATE : 日期。
    • TemporalType.TIME : 时间。
    • TemporalType.TIMESTAMP : 日期和时间。
  • @LastModifiedBy : 设定为修改者,当实体被UPDATE 时会预设值。
  • @LastModifiedDate : 设定为修改时间,当实体被UPDATE 时会预设值。
package com.example.demo.entity;

import java.util.Date;

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

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 lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class Base {

	@CreatedBy
	@Column(name = "CREATE_BY")
	private String create_by;

	@CreatedDate
	@Temporal(TemporalType.TIMESTAMP)
	@Column(name = "CREATE_TIME")
	private Date create_time;

	@LastModifiedBy
	@Column(name = "UPDATE_BY")
	private String update_by;

	@LastModifiedDate
	@Temporal(TemporalType.TIMESTAMP)
	@Column(name = "UPDATE_TIME")
	private Date update_time;

}

建立持久层接口

package com.example.demo.repository;

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

import com.example.demo.entity.MemberAccount;

public interface MemberAccountRepository extends JpaRepository<MemberAccount, Long> {

	public MemberAccount findByUsername(String username);

}

测试功能

package com.example.demo.repository;

import java.util.UUID;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import com.example.demo.entity.MemberAccount;

@SpringBootTest
public class MemberAccountRepositoryTest {

	@Autowired
	private MemberAccountRepository memberAccountRepository;

	@Test
	public void insert() {
		MemberAccount memberAccount = new MemberAccount();
		memberAccount.setUsername("[email protected]");
		memberAccount.setPassword("password");

		String salt = UUID.randomUUID().toString().toUpperCase().replaceAll("-", "");
		memberAccount.setSalt(salt);
		memberAccount.setCreate_by(memberAccount.getUsername());
		memberAccount.setUpdate_by(memberAccount.getUsername());

		memberAccountRepository.save(memberAccount);
		System.out.println(memberAccount.getId());
	}

	@Test
	public void findByUsername() {
		String username = "[email protected]";
		MemberAccount memberAccount = memberAccountRepository.findByUsername(username);
		if(memberAccount != null) System.out.println(memberAccount.toString());
	}

	@Test
	public void update() {
		String username = "[email protected]";
		MemberAccount memberAccount = memberAccountRepository.findByUsername(username);
		memberAccount.setPassword("123456");

		memberAccountRepository.save(memberAccount);
	}

}

Github

使用JPA 建立持久层及其测试方法

参考网站

Java持久化API
Spring Data JPA
Spring Data JPA : Auditing with @CreatedBy, @CreatedDate, @LastModifiedBy and @LastModifiedDate


<<:  用React刻自己的投资Dashboard Day13 - 制作分页(Pagination)功能

>>:  [DAY14]地图与组图(1)

Day 8 超多的范例?怎麽办呢?

该文章同步发布於:我的部落格 昨天我们做了一个关於汉堡种类的测试,但真正的测试怎麽可能这麽少呢! ...

D17 下载功能改进

我将models内原本FileField的upload_to参数取消让他储存到预设的位置 这样在存入...

[Day 28] 永和美食纪录-翻转屋 锅烧意面

前言 铁人赛已经进入最後的倒数阶段,看着版上有许多钻研着专业领域的前辈们陆陆续续地完成挑战,笔者真的...

Day 17 - SwiftUI开发学习1(按钮)

我们统整一下前16天的内容,我们花了很多的时间学习swift语言的基本,学完语言之後我们要开始进入到...

[Day22] React x TS 中的 Event Handler

今天来分享一个小东西,记得以前一开始用 React 搭配 TypeScript 开发专案的时候,在...