07.springBootJpa
1 连接数据库配置示例
1.1 数据库配置文件
spring:
datasource:
url: jdbc:mysql://localhost:3306/zhuche?characterEncoding=utf-8&serverTimezone=GMT%2B8
username: root
password: 12345678
jpa:
hibernate:
ddl-auto: create-drop # <-- 这里是每次启动时都重建数据表,适用于开发阶段,如果是生产环境的话,则写none才合适。
1.2 需要用到的maven
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
1.2 创建模型
package com.zhuche.server.model;
import javax.persistence.*;
import java.util.List;
@Entity // <-- 加入持久化实例注解
@Table(name = "banner") // <-- 指定表名
public class BannerModel {
@Id
private long id; // <-- 字段 id
private String name; // <-- 字段 name
private String description; // <-- 字段 description
private String img; // <-- 字段 img
private String title; // <-- 字段 title
}
然后重启下应用,这在zhuche
数据库中就已生成了一张名为banner
的数据表了。
2 "1对多"示例
现有一个banner
表,而banner
下关联着banner_item
表中的多条记录。这就是典型的1对多
了。以下就是通过模型类的方式把这种关系描述
出来:
package com.zhuche.server.model;
import javax.persistence.*;
import java.util.List;
@Entity
@Table(name = "banner")
public class BannerModel {
@Id
private long id;
private String name;
private String description;
private short type;
private String img;
private String title;
@OneToMany
@JoinColumn(name = "bannerId") // 本模型1对多子模型,且本模型的在子模型的外键名为: bannerId
private List<BannerItemModel> Items;
}
package com.zhuche.server.model;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "banner_item") // 这是子模型
public class BannerItemModel {
@Id
private long id;
private String img;
private String keyword;
private short type;
private String name;
private Long bannerId; // 这是banner模型的外键
}
添加repository
层的文件;
package com.zhuche.server.repository;
import com.zhuche.server.model.BannerModel;
import org.springframework.data.jpa.repository.JpaRepository;
public interface BannerRepository extends JpaRepository<BannerModel, Long> {
BannerModel findOneById(Long id);
BannerModel findOneByName(String name);
}
最后调用它:
package com.zhuche.server.api.v1;
import com.zhuche.server.model.BannerModel;
import com.zhuche.server.service.BannerService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.constraints.NotBlank;
@RestController
@RequestMapping("/banner")
@Validated
public class BannerController {
protected BannerService _bannerService;
public BannerController(BannerService bannerService) {
this._bannerService = bannerService;
}
@GetMapping("/name/{name}")
public BannerModel getByName(@PathVariable @NotBlank String name) {
BannerModel banner = this._bannerService.getByName(name);
return banner; // <-- 这就是了调用后返回的查询结果了
}
}
然后重启下应用,就会生成banner
和banner_item
这2个表了。其整个调用层次为controller
-> service
-> repository
-> model
3 开启sql
控制台输出
model
在对数据库操作的过程,本质上是通过sql
来实现的,在开发中,及时查看sql
能快速定位问题所在.
server:
port: 8081
spring:
datasource:
url: jdbc:mysql://localhost:3306/zhuche?characterEncoding=utf-8&serverTimezone=GMT%2B8
username: root
password: 12345678
jpa:
properties:
hibernate:
show_sql: true # <-- 启用sql输出
format_sql: true # <-- 并格式化
重启下应用,就可以了。
4 "一对多"和"多对一"的双向绑定
一对多
那么反过来也可以多对一
, 用数据表来说,就是banner
是父表,而banner_item
是子表,一个banner
的数据可能对应多条baner_item
的记录,反过来banner_item
也可以对应banner
表的记录,这就是双向。而维持这种关系的外键是存放在baner_item
表中的baner_id
键
示例如下:
4.1 模型层文件示例
package com.zhuche.server.model;
import javax.persistence.*;
import java.util.List;
@Entity
@Table(name = "banner")
public class BannerModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private String description;
private short type;
private String img;
private String title;
@OneToMany(mappedBy = "banner")
private List<BannerItemModel> Items;
}
package com.zhuche.server.model;
import javax.persistence.*;
@Entity
@Table(name = "banner_item")
public class BannerItemModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String img;
private String keyword;
private short type;
private String name;
protected Long bannerId;
@ManyToOne
@JoinColumn(insertable = false, updatable = false, name = "bannerId")
private BannerModel banner;
}
4.2 repository
层文件示例
package com.zhuche.server.repository;
import com.zhuche.server.model.BannerModel;
import org.springframework.data.jpa.repository.JpaRepository;
public interface BannerRepository extends JpaRepository<BannerModel, Long> {
BannerModel findOneById(Long id);
BannerModel findOneByName(String name);
}
4.3 service
服务层示例
package com.zhuche.server.service;
import com.zhuche.server.model.BannerModel;
public interface BannerService {
BannerModel getByName(String name);
}
package com.zhuche.server.service;
import com.zhuche.server.model.BannerModel;
import com.zhuche.server.repository.BannerRepository;
import org.springframework.stereotype.Service;
@Service
public class BannerServiceImpl implements BannerService{
private final BannerRepository _bannerRepository;
public BannerServiceImpl(BannerRepository bannerRepository) {
this._bannerRepository = bannerRepository;
}
@Override
public BannerModel getByName(String name) {
return this._bannerRepository.findOneByName(name);
}
}
4.5 controller
控制层调用示例
package com.zhuche.server.api.v1;
import com.zhuche.server.model.BannerModel;
import com.zhuche.server.service.BannerService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.constraints.NotBlank;
@RestController
@RequestMapping("/banner")
@Validated
public class BannerController {
protected BannerService _bannerService;
public BannerController(BannerService bannerService) {
this._bannerService = bannerService;
}
@GetMapping("/name/{name}")
public BannerModel getByName(@PathVariable @NotBlank String name) {
BannerModel banner = this._bannerService.getByName(name);
return banner;
}
}
5 双向多对多
package com.zhuche.server.model;
import javax.persistence.*;
import java.util.List;
@Entity
@Table(name = "theme")
public class ThemeModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String name;
@ManyToMany
@JoinTable(
name = "theme_spu",
joinColumns = @JoinColumn(name = "theme_id"),
inverseJoinColumns = @JoinColumn(name = "spu_id")
)
private List<SpuModel> spuList;
}
package com.zhuche.server.model;
import javax.persistence.*;
import java.util.List;
@Entity
@Table(name = "spu")
public class SpuModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String subtitle;
@ManyToMany(mappedBy = "spuList")
private List<ThemeModel> themeList;
}
6 禁用物理外键
JPA
默认有物理外键的,不需要来自数据库的物理外键约束的话。可以禁用掉。
6.1 指定单个j实例不加物理外键的注解方式
package com.zhuche.server.model;
import javax.persistence.
@Entity
@Table(name = "banner_item")
public class BannerItemModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String img;
private String keyword;
private short type;
private String name;
protected Long bannerId;
@ManyToOne
@JoinColumn(
foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT), // <-- 加入这行注解
insertable = false, updatable = false, name = "bannerId"
)
private BannerModel banner;
}
7 操作时间相关的字段声明
package com.zhuche.server.model;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import java.time.LocalDateTime;
@Getter
@Setter
@MappedSuperclass // <-- 用于,让继承本抽像类后,本类的字段声明生效
public abstract class BaseEntity {
@Column(columnDefinition = "DATETIME DEFAULT CURRENT_TIMESTAMP")
private LocalDateTime createTime;
@Column( columnDefinition = "datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
private LocalDateTime updateTime;
private LocalDateTime deleteTime;
}