首先·讲解下JAVA的MVC思想
- Model:用于定义数据模型
- Controller:控制层,用来调用逻辑方法
- Service:用于编写逻辑方法
- View:前端展示
结合之前讲到OCP思想,分层可以更好地协同开发。
当下的分层模式主要分为:水平分割和垂直分割
- 水平分割:利用MVC,不同的项目不同的开发者/团队进行开发
- 垂直分割:微服务架构,低层分割。
JAVA作为经典的后端语言,与数据库的交互是必备的掌握技能。常用的模式是:
- JPA:关系型数据库查询。优点:简单易用;缺点:学习成本大;国外常用
- MyBatis: 优点:简单易用;缺点:需要维护和调优
现在我们开始讲解SpringBoot的JPA模式
菜单
1、SpringBoot引入第三方数据库链接依赖
1.1、修改项目下面的pom.xml文件
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.8.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
1.2、reload porject并查看maven依赖加载情况
2、使用JPA和Model模型创建数据表
2.1、编写Service层 ,编写业务层
前面讲到,@Service也可以加入到容器中,因为这个是service层,规范化的话,使用@Service而不使用@Compent,根据依赖注入,在controller处private BannerService bannerservice;前记得添加@Autowired
方法一:简单模式
创建一个BannerService类
在项目创建一个package并命名为service,并创建一个类BannerService
package com.fcors.fcors.service;
import org.springframework.stereotype.Service;
@Service
public class BannerService {
void getByName(String name){
/* 此处开始编写 业务逻辑代码 */
}
}
方法二:OCP模式
根据之前说讲,为了更好的实现OCP,我们的类都要实现接口,然后控制层调用接口,而不是直接调用实例类
在项目创建一个package并命名为service,并创建一个 interface:BannerService
package com.fcors.fcors.service;
import org.springframework.boot.Banner;
public interface BannerService {
Banner getByName(String name);
}
在service中创建一个 BannerServiceImpl 类去实现接口
package com.fcors.fcors.service;
import com.fcors.fcors.model.Banner;
import org.springframework.stereotype.Service;
@Service
public class BannerServiceImpl implements BannerService{
@Override
public Banner getByName(String name) {
return null;
}
}
2.2、编写Model层
Model主要是存储数据库的数据模型。 一个Model类就是一个数据库的一个表
创建一个package,命名为Model, 并在创建一个类BannerModel类,加上 @Entity(实体类)注解
注意,为了接口在controller能输出,需要在此处加入@Getter和@Setter
package com.fcors.fcors.model;
import javax.persistence.*;
@Getter
@Setter
@Entity
/* 实体类名与数据库表名不一致的时候 */
@Table(name="banner")
public class Banner {
/* 定义主键*/
@Id
private long id;
/*定义长度*/
@Column(length=16)
private String name;
/* 定义该成员不在数据库,不与数据库关联 */
@Transient
private String description;
private String img;
private String title;
}
2.3、编写数据库配置
因为数据库配置信息,一般不同环境信息都有所差异,因此我们把信息写在application-dev.yml中
2.3.1Mysql的配置
server:
port:8082
spring:
datasource:
url: jdbc:mysql://localhost:3306/missyou?characterEncoding=utf-8&serverTimezone=GMT%2B8
username: root
password: 123
jpa:
show-sql: true
properties:
hibernate:
format_sql: true
enable_lazy_load_no_trans: true
2.3.2.1SqlServer的配置
server:
port: 8082
spring:
datasource:
driver-class-name: net.sourceforge.jtds.jdbc.Driver
url: jdbc:jtds:sqlserver://ip:1433;databaseName=Databasename
username: sa
password: password
jpa:
show-sql: true
properties:
hibernate:
format_sql: true
enable_lazy_load_no_trans: true
2.3.2.2引入sqlserver的依赖
<!-- JPA依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- sql server-->
<dependency>
<groupId>net.sourceforge.jtds</groupId>
<artifactId>jtds</artifactId>
<version>1.3.1</version>
</dependency>
记得启用该配置文件
2.3.3设置JPA参数,建立与Mysql的链接,修改application.yml
mysql:
port: 3306
ip: 192.168.8.8
hero:
condition: fox222
porject-PathName: com.fcors.fcors
spring:
profiles:
active: dev
jpa:
open-in-view: false
hibernate:
ddl-auto: create-drop
create-drop:每次运行都会删除和重建表
2.4、编写Controller层,控制器层
package com.fcors.fcors.api.sample.v1;
import com.fcors.fcors.service.BannerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotBlank;
import org.springframework.validation.annotation.Validated;
import java.io.IOException;
/** 声明这个类是一个controller */
@RestController
@RequestMapping("/banner/")
@Validated
public class BannerController {
@Autowired
private BannerService bannerService;
@GetMapping("/name/{name}")
public void getByName(@PathVariable @NotBlank String name){
}
}
3、使用JPA和Model模型查询数据库
3.1、修改application.yml的JAP属性
3.2、数据表插入两条数据
3.3、JPA定义接口Repository
3.3.1 创建一个Repository接口
在项目中创建一个package并命名为repository,在下面创建一个接口BannerRepository
实现JpaRepository<@value1,@value2>,两个参数分别是
- @value1:model类名
- @value:model的主键即表的主键
package com.fcors.fcors.repository;
import com.fcors.fcors.model.Banner;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/*JpaRepository<@value1,@value2>
* @value1:model类名
* @value:model的主键即表的主键
* */
@Repository
public interface BannerRepository extends JpaRepository<Banner,Long> {
Banner finOneById(String name);
}
3.4、修改service文件BannerServiceImpl
package com.fcors.fcors.service;
import com.fcors.fcors.model.Banner;
import com.fcors.fcors.repository.BannerRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class BannerServiceImpl implements BannerService {
@Autowired
private BannerRepository bannerRepository;
public Banner getOneByName(String name){
return bannerRepository.findOneByName(name);
}
}
3.5、修改controller
package com.fcors.fcors.api.sample.v1;
import com.fcors.fcors.model.Banner;
import com.fcors.fcors.service.BannerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotBlank;
import java.io.IOException;
/** 声明这个类是一个controller */
//@Controller
//@ResponseBody
//@RestController 等同于同时声明@Controller和@ResponseBody
@RestController
@RequestMapping("/banner/")
@Validated
public class BannerController {
@Autowired
private BannerService bannerService;
@GetMapping("/name/{name}")
public Banner getOneByName(@PathVariable @NotBlank String name){
Banner banner = bannerService.getByName(name);
return banner;
}
}
4、JPA单向一对多模型
单向一对多模型:是指两个表,其中表1的条数据对应表2的数据是多条。我们通过查询表1的时候,把表2的信息也能显示出来。
例如:Banner表,和BannerItem表;一个Banner会对应多个BannerItem;下面就针对这个来实现。
4.1、数据库创建BannerItem表
如果通过sql表达
select * from banner a
left join banneritem b on a.id=b.bannerid
where 1=1
4.2、在model下创建BannerItem的类
package com.fcors.fcors.model;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Getter
@Setter
/* 实体类名与数据库表名不一致的时候 */
@Table(name="banneritem")
public class BannerItem {
/*定义主键*/
@Id
private long id;
private String name;
private String url;
private long bannerid;
private long status;
}
4.3、在model下Banner的类
package com.fcors.fcors.model;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.util.List;
@Entity
@Getter
@Setter
/* 实体类名与数据库表名不一致的时候 */
@Table(name="banner")
public class Banner {
/* 定义主键*/
@Id
private long id;
/*定义长度*/
@Column(length=16)
private String name;
/* 定义该成员不在数据库,不与数据库关联 */
@Transient
private String description;
private String img;
private String title;
/* 设置关联
* banner and banneritem 两个实体类和数据表的关联
* */
@OneToMany(fetch = FetchType.EAGER)
@JoinColumn(name = "bannerid")
private List<BannerItem> Banneritem;
}
4.4、Repository下的BannerRepository接口
package com.fcors.fcors.repository;
import com.fcors.fcors.model.Banner;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/*JpaRepository<@value1,@value2>
* @value1:model类名
* @value:model的主键即表的主键
* */
@Repository
public interface BannerRepository extends JpaRepository<Banner,Long> {
Banner findOneByName(String name);
}
4.5、Service下的BannerService接口和BannerServiceImpl类
4.5.1、BannerService接口
package com.fcors.fcors.service;
import com.fcors.fcors.model.Banner;
public interface BannerService {
Banner getOneByName(String name);
}
4.5.2、BannerServiceImpl类
package com.fcors.fcors.service;
import com.fcors.fcors.model.Banner;
import com.fcors.fcors.repository.BannerRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class BannerServiceImpl implements BannerService {
@Autowired
private BannerRepository bannerRepository;
public Banner getOneByName(String name){
return bannerRepository.findOneByName(name);
}
}
4.6 Controller文件
package com.fcors.fcors.api.sample.v1;
import com.fcors.fcors.model.Banner;
import com.fcors.fcors.service.BannerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotBlank;
import java.io.IOException;
/** 声明这个类是一个controller */
//@Controller
//@ResponseBody
//@RestController 等同于同时声明@Controller和@ResponseBody
@RestController
@RequestMapping("/banner/")
@Validated
public class BannerController {
@Autowired
private BannerService bannerService;
@GetMapping("/name/{name}")
public Banner getOneByName(@PathVariable @NotBlank String name){
Banner banner = bannerService.getOneByName(name);
return banner;
}
}
5、JPA双向一对多模型
为什么会有双向一对多的出现?
例如我们需要查bannerItem是属于哪个banner,需要banner表的某些信息,这就是双向1对多。
注意当双向的时候,@JoinColumn打在多方【多端】处。
5.1、model下的BannerItem类
package com.fcors.fcors.model;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.*;
@Entity
@Getter
@Setter
/* 实体类名与数据库表名不一致的时候 */
@Table(name="banneritem")
public class BannerItem {
/*定义主键*/
@Id
private long id;
private String name;
private String url;
private long bannerid;
private long status;
@ManyToOne(cascade={CascadeType.MERGE,CascadeType.REFRESH},optional=false,fetch=FetchType.LAZY)
@JoinColumn(insertable = false,updatable = false,name="bannerid",referencedColumnName="id", nullable = false)
@JsonBackReference
private Banner banner;
}
5.2、model下的Banner类
package com.fcors.fcors.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.util.List;
@Entity
@Getter
@Setter
/* 实体类名与数据库表名不一致的时候 */
@Table(name="banner")
public class Banner {
/* 定义主键*/
@Id
private long id;
/*定义长度*/
@Column(length=16)
private String name;
/* 定义该成员不在数据库,不与数据库关联 */
@Transient
private String description;
private String img;
private String title;
/* 设置关联
* banner and banneritem 两个实体类和数据表的关联
* */
@OneToMany(mappedBy = "banner",cascade = CascadeType.REMOVE,fetch = FetchType.EAGER)
// @JoinColumn(name = "bannerid")
private List<BannerItem> Banneritem;
}
5.3、BannerItemRepository的类
package com.fcors.fcors.repository;
import com.fcors.fcors.model.Banner;
import com.fcors.fcors.model.BannerItem;
import org.springframework.data.jpa.repository.JpaRepository;
public interface BannerItemRepository extends JpaRepository<BannerItem,Long> {
BannerItem findOneByName(String name);
}
5.4、service下的BannerItem接口
package com.fcors.fcors.service;
import com.fcors.fcors.model.BannerItem;
public interface BannerItemService {
BannerItem getOneByName(String name);
}
5.5、service下BannerItem类
切记如果model一对多是懒加载的话,在service层一定要加上@Transactional
不然会报错:
Unable to evaluate the expression Method threw 'org.hibernate.LazyInitializationException' exception.
package com.fcors.fcors.service;
import com.fcors.fcors.model.BannerItem;
import com.fcors.fcors.repository.BannerItemRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Transactional
@Service
public class BannerItemServiceImpl implements BannerItemService{
@Autowired
private BannerItemRepository bannerItemRepository;
public BannerItem getOneByName(String name){
return bannerItemRepository.findOneByName(name);
}
}
5.6、控制器层Bannercontroller
package com.fcors.fcors.api.sample.v1;
import com.fcors.fcors.model.Banner;
import com.fcors.fcors.model.BannerItem;
import com.fcors.fcors.service.BannerItemService;
import com.fcors.fcors.service.BannerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotBlank;
import java.io.IOException;
/** 声明这个类是一个controller */
//@Controller
//@ResponseBody
//@RestController 等同于同时声明@Controller和@ResponseBody
@RestController
@RequestMapping("/banner/")
@Validated
public class BannerController {
@Autowired
private BannerItemService bannerItemService;
@GetMapping("/bannerItem/name/{name}")
public BannerItem getBannerItemOneByName(@PathVariable @NotBlank String name){
BannerItem bannerItem = bannerItemService.getOneByName(name);
return bannerItem;
}
}
上面的例子还是有个bug,就是访问BannerItem的时候,并获取不了banner的内容。(需后续更改)
6、JPA单向多对多模型
单向的多对多模型,我们只有一方保留导航模式,即@ManyToMany
6.1、创建数据表Spu、Theme、Spu_Theme三个表
6.2、在model下创建Spu的类
package com.fcors.fcors.model;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import java.util.List;
@Entity
@Getter
@Setter
@Table(name="spu")
public class Spu {
@Id
private Long id;
private String title;
private String subtitle;
// @ManyToMany
// private List<Theme> themeList;
}
6.3、在model下创建Theme的类
package com.fcors.fcors.model;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.util.List;
@Entity
@Getter
@Setter
@Table(name="theme")
public class Theme {
@Id
private Long id;
private String title;
private String name;
@ManyToMany
/* joinColumns 第三张表的*/
@JoinTable(name="spu_theme",joinColumns = @JoinColumn(name="themeid"),
inverseJoinColumns = @JoinColumn(name="spuid")
)
private List<Spu> spuList;
}
更多的级联操作可以参考
SprintBoot系列(十五):JPA级联操作 – 星伴同行 (fcors.com)
下面我们将讲解如何通过IDEA逆向生成Model