SpringBoot学习(四)

八、数据库操作

本章会使用Spring-Data-JPA组件,JPA是一种规范,一种标准,不是插件也不是程序,JPA定义了一系列对象持久化的标准,目前使用这一规范的产品有Hibernate、TopLink等。

在pom.xml中添加依赖:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

在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<Girl,Integer> {
}

创建一个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<Girl> 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<Girl> 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 iscom.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
从错误上看应该是时区的错误,因此只需要设置为你当前系统时区即可

image.png

加上serverTimezone=GMT%2B8" 即可 GMT%2B8代表: 东八区

通过postman可以调接口


image.png

数据库查看结果:


image.png

事务操作:
事务管理
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,生成单元测试的类


image.png
image.png

勾选要生成单元测试的类:


image.png
image.png

使用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());
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,651评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,468评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,931评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,218评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,234评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,198评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,084评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,926评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,341评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,563评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,731评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,430评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,036评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,676评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,829评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,743评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,629评论 2 354

推荐阅读更多精彩内容