八、数据库操作
本章会使用Spring-Data-JPA组件,JPA是一种规范,一种标准,不是插件也不是程序,JPA定义了一系列对象持久化的标准,目前使用这一规范的产品有Hibernate、TopLink等。
在pom.xml中添加依赖:
org.springframework.boot
spring-boot-starter-data-jpa
mysql
mysql-connector-java
在appilication.yml配置文件中添加数据库的配置,datasource和jpa都是写在spring下的:
spring:
application:
name: mytest
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/dbgirl?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&characterSetResults=utf8
username: username
password: password
jpa:
hibernate:
ddl-auto: update
show-sql: true
其中jpa.hibernate.ddl-auto的值介绍如下:
ddl-auto:create----每次运行该程序,没有表格会新建表格,表内有数据会清空
ddl-auto:create-drop----每次程序结束的时候会清空表
ddl-auto:update----每次运行程序,没有表格会新建表格,表内有数据不会清空,只会更新
ddl-auto:validate----运行程序会校验数据与数据库的字段类型是否相同,不同会报错
创建一个实体类:
package com.imooc;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
/*
创建Girl实体类,用来存放数据库的字段信息。
*/
//@Entity:@Table(name="") 表明这是一个实体类,一般和jpa配合着使用,如果实体类名称和数据库名称一致,@Table注解可以省略
@Entity
@Table(name = "Girls")
public class Girl {
//@Id注解表示该属性为主键,此注解放在private Integer id;前面,代表id为该表的主键。
//@GeneratedValue(strategy = GenerationType.SEQUENCE,generator = “repair_seq”):表示主键生成策略是sequence(可以为Auto、IDENTITY、native等,Auto表示可在多个数据库间切换),指定sequence的名字是repair_seq。
@Id
@GeneratedValue
private Integer id;
private String cupSize;
private Integer age;
//生成一个构造函数
public Girl(){
}
//以下是getter和setter方法
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getCupSize() {
return cupSize;
}
public void setCupSize(String cupSize) {
this.cupSize = cupSize;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
创建一个接口:
package com.imooc;
import org.springframework.data.jpa.repository.JpaRepository;
/*
创建一个接口,继承JpaRepository类,Girl是创建的实体类,Integer是参数id的类型
SpringBoot会自动将接口类自动注解到Spring容器中,不需要我们做任何配置
*/
public interface GirlRep extends JpaRepository {
}
创建一个GirlController,写一个获取所有girl的api和添加girl的api :
package com.imooc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import javax.persistence.criteria.CriteriaBuilder;
import javax.websocket.server.PathParam;
import java.util.List;
/*
添加、修改数据库信息
*/
//@RestController 从浏览器中请求该接口
@RestController
public class GirlController {
//通过@Autowired把GirlRep接口自动注入到SpringBoot容器中,同时带入Girl实体类
@Autowired
private GirlRep girlRep;
/**
add girl
@param cupSize
@param age
*/
//定义一个post方法,设置两个参数,与数据库中参数值要对应
@RequestMapping(value = "/addgirl",method = RequestMethod.POST)
public Girl addgirl(@RequestParam("cupSize") String cupSize,
@RequestParam("age") Integer age){
//定义一个gir对象
Girl girl=new Girl();
//设置cupsize和age的值
girl.setCupSize(cupSize);
girl.setAge(age);
//调girlRep接口的save方法,往数据库中插入值,并同时返回插入的值信息
return girlRep.save(girl);
}
/**
方法名:getgirlslist
描述:从数据库中获取女生数据,返回类型为List,是返回所有的女生信息
*/
@RequestMapping(value = "/getgirlslist",method = RequestMethod.GET)
public List getgirlslist(){
return girlRep.findAll();
}
/**
* update girls
* @param id
* @param cupSize
* @param age
* @return girl update
*/
@PostMapping(value = "/updategirls/{id}")
public Girl girlupdate(@PathVariable("id") Integer id, //用户输入id后获取该id值
@RequestParam("cupSize") String cupSize,
@RequestParam("age") Integer age){
Girl girl=new Girl();
//更改女生信息,如果不存在就新增一个女生信息
girl.setAge(age);
girl.setCupSize(cupSize);
girl.setId(id);
return girlRep.save(girl);
}
/**
* delete girl by id
* @param id
*/
@GetMapping(value = "/deletegirls/{id}")
public String deletegirls(@PathVariable("id") Integer id){
// girlRep.delete(id);
girlRep.deleteById(id);
return "删除成功";
}
/**
* get one girl by cupsize
* 在GirlRep方法中定制查询方法
* @param cupSize
*/
@GetMapping(value = "/girlfindbycupSize/{cupSize}")
public List girlfindbycupSize(@PathVariable("cupSize") String cupSize){
return girlRep.findBycupSize(cupSize);
}
//也可以用findByAge方法根据age获取女生信息
/**
* get one girl by cupsize
* @param id
*/
// @GetMapping(value = "/girlfindbyid/{id}")
// public Girl girlfindbycupSize(@RequestParam("id") Integer id){
// Girl girl=new Girl();
// girl.setId(id);
// return girlRep.findOne(girl.getId());
// }
}
Ctrl+F5启动服务,启动时遇到如下两个问题:
1、提示驱动
Loading class com.mysql.jdbc.Driver'. This is deprecated. The new driver class is
com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
字面意思 mysqljdbc . driver。这是被弃用的。新的驱动类是“com.mysql.cjdbc.driver”。驱动程序通过SPI自动注册,而手动加载类通常是不必要的。
那么我只需要把 com.mysql.jdbc.Driver 改为com.mysql.cj.jdbc.Driver 即可
2、提示时间有问题
java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time
从错误上看应该是时区的错误,因此只需要设置为你当前系统时区即可
加上serverTimezone=GMT%2B8" 即可 GMT%2B8代表: 东八区
通过postman可以调接口
数据库查看结果:
事务操作:
事务管理
1.什么是事务?事务是作为一个逻辑单元执行的一系列操作。它有4个特性
- 原子性:事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么全部失败。
- 一致性: 一旦事务完成,不管成功还是失败,系统必须确保它所建模的业务处于一致的状态,而不全是部分完成,或者是部分失败,在现实的数据不应有被破坏。
- 隔离性: 可能有许多事务会同时处理相同的数据, 因此每个事务都应该与其他事务隔离开,防止数据被破坏。
- 持久性: 一旦事务完成, 无论发生什么,系统发生错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来, 通常情况下,事务的记过被写到持久化存储器。
2.我们常用的几个事务:
- PROPAGATION_REQUIRED: 如果存在一个事务,则支持当前的事务,如果没有则开启。
- PROPAGATION_SUPPORTS: 如果存在一个事务,就支持当前事务, 如果没有事务,则以非事务执行。
- PROPAGATION_REQUIRES_NEW: 启动一个新的事务,不依赖当前事务,当前事务挂起。
3.我们模拟一个事务的回滚,体现事务的原子性,第一个save操作不会出现问题,第二个save操作会抛出异常。但是不能部分成功,不能部分失败。这二个操作最终会被回滚。
@Service
public class GirlService {
@Autowired
private GirlRepository girlRepository;
//在service层添加@Transactional注解可以控制事务
**@Transactional**
public void insertTwo() {
Girl girlA = new Girl("garrett-test", 18, "Z");
girlRepository.save(girlA);
Girl girlB = new Girl("mayday-test", 21, "BBBBBBBB");
girlRepository.save(girlB);
}
}
@RestController
public class GirlController {
@Autowired
private GirlService girlService;
/**
* Tests transaction
*/
@GetMapping(value = "/transaction")
public void transactionTest() {
girlService.insertTwo();
}
}
启动应用,打开Postman,测试API。很显然,操作发生异常进行回滚,数据库未插入任何数据。
单元测试:
鼠标放到类的方法中,右键,选择goto--test,生成单元测试的类
勾选要生成单元测试的类:
使用mockmvc做单元测试:
package com.imooc.controller;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import static org.junit.Assert.*;
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc //该注解是指使用mockmvc测试
public class GirlControllerTest {
@Autowired
private MockMvc mvc;
@Test
public void getgirlslist() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/getgirlslist")).
andExpect(MockMvcResultMatchers.status().isOk()).
andExpect(MockMvcResultMatchers.content().string("777"));
//andExpect方法中有很多判断的方法
//在命令行中输入:mvn clean package 打包构建
//mvn clean package -Dmaven.test.skip=true 打包时跳过单元测试
}
}
使用断言,判断预期结果与实际结果的值是否相同
package com.imooc;
import com.imooc.domain.Girl;
import com.imooc.service.GirlService;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
/*
测试GirlService类
*/
//@RunWith注解是指测试运行器,告诉程序是什么环境下的测试,目前参数是SpringRunner是指在Spring环境下的测试
//@SpringBootTest 启动整个SpringBoot的工程
@RunWith(SpringRunner.class)
@SpringBootTest
public class GirlServiceTest {
//注入GirlService类,下面会使用该类中的方法
@Autowired
GirlService girlService;
@Test
public void findoneTest(){
Girl girl=girlService.findone(8);
//断言,判断age值是否与预期的值相等
Assert.assertEquals(new Integer(31),girl.getAge());
}
}