Spring Boot JPA操作数据(六)

简介

在Spring Boot项目开发中,我们操作数据库的时候一般会用到JPA或者是MyBatis框架,今天所要将的是JPA操作数据库案例。后续会说到MyBatis操作数据。大家在开发的时候,看看自己对哪个比较熟练或者是公司项目的需要来选择,上篇我们已经说了Spring Boot如何连接数据库,我们紧跟着上篇Spring Boot 连接数据库(五)的内容来进行操作。

Spring Boot.png

认识JPA

首先我们在遇到一个新的知识的时候,官方技术文档将是我们最好的帮手,或者是网上查询相关技术支持博文。进入JPA官方文档学习吧。

什么是JPA?

Spring Data JPA, part of the larger Spring Data family , makes it easy to easily implement JPA based repositories. This module deals with enhanced support for JPA based data access layers. It makes it easier to build Spring-powered applications that use data access technologies.
Spring Data JPA aims to significantly improve the implementation of data access layers by reducing the effort to the amount that’s actually needed.

翻译如下:

Spring Data JPA是更大的Spring Data系列的一部分,可以轻松实现基于JPA的存储库。 此模块处理对基于JPA的数据访问层的增强支持。 它使构建使用数据访问技术的Spring驱动应用程序变得更加容易。
Spring Data JPA旨在通过减少实际需要的工作量来显着改善数据访问层的实现

JPA有什么特性?

  • 基于Spring和JPA构建存储库的复杂支持
  • 支持Querydsl术语,从而支持类型安全的JPA查询
  • 透明审核域名类
  • 分页支持,动态查询执行,集成自定义数据访问代码的能力
  • 在引用时可通过@Query带注释的自定义查询
  • 支持基于XML的实体映射
  • 基于JavaConfig的存储库配置,介绍@EnableJpaRepositories

配置JPA要求

在Spring Boot中通过JPA来进行操作数据库是非常简便的,并且我们也可以自定义SQL语句,而在满足我们项目的条件下不需要进行书写过多的SQL语句,这能很大程度解决我们的开发效率。

配置

Spring Boot项目是如何进行配置JPA呢?这里我用的是MevanGradle两种方式进行项目的配置来创建Spring Boot项目,但是在本案例开发提供代码是通过Mavan进行的,后期会补充Gradle的案例代码。

Maven配置

1.添加JPA maven依赖库
pom.xml添加

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--mysql依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.11</version>
        </dependency>
        <!--JPA依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
  • 注:这里因为要操作数据,所以需要添加数据库依赖,如MySQL

2.在application.yml配置文件中进行配置(推荐)

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver  # MySql jdbc Driver
    # 连接数据库
    # eirunye_springboot_notes表示的是你创建的数据库;
    # useSSL:是否使用SSL证书验证;
    # characterEncoding:编码格式;
    # useJDBCCompliantTimezoneShift:是否使用符合JDBC的时区转换;
    # useLegacyDatetimeCode:是否使用旧版日期时间码;
    # serverTimezone:选择服务器时间方式;
    url: jdbc:mysql://127.0.0.1:3306/eirunye_springboot_notes?useSSL=false&requireSSL=false&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
    username: root   #本地设置数据库账号
    password: 123456 #密码
  jpa:
    hibernate:
      ddl-auto: update   
    show-sql: true
server:
  port: 8090 #知道访问端口
  • spring.jpa.hibernate.ddl-auto配置的属性解释:
    • create:每次启动该程序,没有表的会新建表,表内有数据都会清空,结束时,并未清空表
    • create-drop:每次启动、结束程序的时都会清空表,而且启动时会重新创建表,
    • update:每次启动程序时,没有表的会创建表,表不会清空,检查表字段是否相同,不同则更新,
    • validate:每次启动程序时,校验数据与表(字段、类型等)是否相同,不同会报错
    • none:在所有其他情况下,默认为none,这里选择update

application.properties配置文件中进行配置

  • 注:.yml.properties选择其一
server.port=8090
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver  # MySql jdbc Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/eirunye_springboot_notes?useSSL=false&requireSSL=false&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
spring.datasource.data-username=root
spring.datasource.data-password=123456
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

Gradle配置

1.在build.gradle添加依赖

dependencies {
    //注:Android 的Gradle依赖已经改为implementation
    compile("org.springframework.boot:spring-boot-starter-data-jpa")   //添加jpa依赖
    compile ("mysql:mysql-connector-java:5.1.24") //添加MySQL依赖
    compile("com.h2database:h2")
    testCompile("junit:junit")
}

2.在.yml或者.properties配置文件中进行配置和上面的Maven一样。

JPA操作数据

  • :在这里简单的举例说明JPA的使用,在系列文章会更加深入讲关于JPA的使用,在这里的代码创建到是按照Spring Boot 项目如何搭建(四)项目包层级定义来创建的,所以大家不清楚就再去看看,或者下载本例代码查看就明白了。

1.在.bean包下创建实体User.class

  • 注:这里添加注解的位置如果选择在属性上面添加的话就全部都选择在属性上面添加,本例的User.class,如果选择在get方法上面添加,那么全部都在get方法上面添加注解,不然有时可能获取不到数据,或者其他错误。
@Entity
@Table(name = "user")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @Column(name = "username",columnDefinition = "VARCHAR(50) NOT NULL COMMENT '用户名不为空!",unique = true)
    @NotBlank(message = "username can't be null")
    private String username;

    @Column(name = "password",columnDefinition = "VARCHAR(50) NOT NULL COMMENT '密码不为空!")
    @NotBlank(message = "password can't be null")
    private String password;

    @Temporal(TemporalType.DATE)
    @Column(name = "create_time", columnDefinition = "datetime DEFAULT NULL COMMENT '创建时间'")
    private Date createTime; //创建时间

    @Temporal(TemporalType.DATE)
    @Column(name = "update_time", columnDefinition = "datetime DEFAULT NULL COMMENT '更新时间'")
    private Date updateTime;//更新时间
// get/set方法   注:Lombok插件可以直接构建get、set方法
}
  • 实体注解解释(这里讲解的是常用的)
注解源 解释说明
@Entity JPA实体映射到数据库
@Table 表示表,@Table(name = "user")表示该实体生成的表是user
@Table还有几个属性,如catalog:对应数据库的catalogschema:对应数据库的schemaUniqueConstraint定义在@Table@SecondaryTable元数据里,用来指定建表时需要建唯一约束的列等
@Id 表示主键Id
@GeneratedValue @GeneratedValue(strategy = GenerationType.AUTO)表示Id自增
@Column name = "username"表示表的字段名为usernamecolumnDefinition表示字段创建的SQL语句说明,第一次创建时生效
@NotBlank @NotBlank(message = "password can't be null")解释说明
@Temporal @Temporal(TemporalType.DATE)表示时间的定义格式为:yyyy-MM-dd 即:2018-09-01TemporalType.TIME表示为:hh:mm:ss 即 10:12:11TemporalType.TIMESTRAMP表示为:yyyy-mm-dd hh:mm:ss 即:2018-09-01 10:12:11
  • 说明:以上的实体注解解释完成了。当然了,在这里我们使用的JPA的注解只是很少的一部分,接下来的文章会详细说明相关注解的作用和使用。

运行项目生成的表如下

create table user
(
  id    int   not null primary key,
  create_time datetime    null  comment '创建时间',
  password    varchar(50) not null   comment '密码不为空!',
  update_time datetime    null    comment '更新时间',
  username    varchar(50) not null    comment '用户名不为空!',
  constraint UK_sb8bbouer5wak8vyiiy4pf2bx
  unique (username)
)
  engine = MyISAM charset = utf8;

2.在.controller包下创建UserController.class
这里代码的就是测试增删改查,这里的注解之前的文章都解释过了,这里就不解释了,遇到最新的就解释。

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    UserService userService;

    /**
     * 通过id查找user信息
     * @param id 参数id
     * @return 返回Json user
     * @throws Exception
     */
    @GetMapping(value = "/info/{id}")
    public Result<User> getUserDataById(@PathVariable Integer id) throws Exception {

        if (null == id) throw new EirunyeException(ResultEnum.UNKNOWN_ERROR);//这里定义自己的提示错误信息,最好每个都有定义这样比较明确错误!!!

        return userService.getUserDataById(id);
    }
    /**
     * 插入数据
     * @param user 用户信息
     * @return
     * @throws Exception
     */
    @PostMapping(value = "/info/save")
    public Result<User> saveUserData(@Valid User user) throws Exception {
        if (null == user) throw new EirunyeException(ResultEnum.UNKNOWN_ERROR);//这里定义自己的提示错误信息,最好每个都有定义这样比较明确错误!!!

        return userService.saveUserData(user);
    }
    /**
     * 更新user信息
     * @param user 参数
     * @return 返回提示 更新失败或者成功
     * @throws Exception 异常
     */
    @PostMapping(value = "/info/update")
    public Result<String> updateUserInfo(@Valid User user) throws Exception {

        if (null == user) throw new EirunyeException(ResultEnum.UNKNOWN_ERROR);

        return userService.updateUserInfo(user);
    }
    /**
     * 通过ID参数user信息
     * @param id 参数id
     * @return 返回提示 参数成功或者失败
     */
    @GetMapping(value = "info/delete/{id}")
    public Result<String> deleteUserInfo(@PathVariable Integer id) throws Exception {
        if (null == id) throw new EirunyeException(ResultEnum.UNKNOWN_ERROR);

        return userService.deleteUserInfo(id);
    }
}

3.在.service包下创建UserService.class

@Service
public class UserService {

    @Autowired
    UserRepository userRepository;

    /**
     * 插入数据
     * @param user 用户
     * @return Json user
     * @throws Exception 异常抛出
     */
    public Result<User> saveUserData(User user) throws Exception {

        User user1 = userRepository.save(user);
        return ResultUtil.getResult(ResultEnum.SUCCESS, user1);
    }
    /**
     * 根据id来获取user信息
     * @param id 参数id
     * @return Json user
     */
    public Result<User> getUserDataById(Integer id) throws Exception {

        User user = userRepository.findAllById(id);
        if (null == user) {
            throw new EirunyeException(ResultEnum.USERNOTEXIST);//用户不存在
        } else
            return ResultUtil.getResult(ResultEnum.SUCCESS, user);
    }
    /**
     * 更新user信息 更新数据说明:
     * 1.如果通过save(S s)方法的话必须带主键Id,这样通过主键id就能更新数据了,
     * 如果不带参数id则,数据将会自增一条数据(变成插入数据了)
     * 2.自定义SQL语句更新数据(后面会讲到)
     *
     * @param user 参数user
     * @return json String
     */
    public Result<String> updateUserInfo(User user) throws Exception {

        if (null == user.getId()) return ResultUtil.error(-1, "Id不能为空!");
        //更新数据
        User userUpdate = userRepository.save(user);
        if (userUpdate == null) return ResultUtil.error(-1, "数据更新失败,请联系后台!");

        // 一般更新数据步骤:
        // 1.通过ID获取当前用户信息
        // 2.将所需更新的信息进行设置,如用户名、密码等,而创建时间不需要更新
        // 3.如果没有其他特殊字段直接save方法更新(注:这里我就简单直接save了,大家的项目应该是下面这样方式写)
        //User userInfo = userRepository.findAllById(user.getId());
        //userInfo.setId(user.getId());
        //userInfo.setUsername(user.getUsername());
        //userInfo.setPassword(user.getPassword());
        //userInfo.setUpdateTime(new Date());
        //userInfo.setCreateTime(new Date()); //这个不需要设置

        return ResultUtil.getResult(ResultEnum.SUCCESS, "更新数据成功!!!");
    }

    /**
     * @param id 参数id
     * @return json String
     */
    public Result<String> deleteUserInfo(Integer id) throws Exception {

        userRepository.deleteById(id);

        //下面不需要也可以,直接返回return ResultUtil.getResult(ResultEnum.SUCCESS, "删除数据成功!!!");
        User user = userRepository.findAllById(id);
        if (null == user) {
            throw new EirunyeException(ResultEnum.SUCCESS);
        } else return ResultUtil.error(-1, "删除数据失败!!!");
    }
}

4.在.repository创建UserRepository.class

  • 注:这里需要注意的是命名规则 查找find,删除delete等等
public interface UserRepository extends JpaRepository<User, Integer> {

    /**
     * @param id 根据id获取当前user数据
     * @return user
     */
    User findAllById(Integer id);
    /**
     * 通过用户名和密码来获取用户
     * @param username 参数用户名
     * @param password 参数密码
     * @return 返回user
     */
    User findAllByUsernameAndPassword(String username, String password);
    /**
     * 模糊查询 自定义查询语句
     * @param username
     * @return
     */
    @Query(value = "select * from user u where u.username like %:username%",nativeQuery=true)
    List<User> findAllByUsername(@Param("username") String username);

}
  • 注: JPA的自定义语句都是@Query(value="SQL语句"),下面的方法还是要按照JpaRepository命名规则进行,后面的文章将会说明该内容。

测试

1.新建单元测试
src\test\java\com\eirunye\spring_boot_jpa\创建UserTest.class,这里只是简单的进行单元测试,之后文章会讲解到Spring Boot项目如何进行单元测试。
测试代码UserTest.class进行查看,这里只贴一些

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserTest {

    private final static Logger logger = LoggerFactory.getLogger(ExceptionHandle.class);


    @Autowired
    UserService userService;

    @Test
    public void getUserInfo() throws Exception {
        Result<User> userDataById = userService.getUserDataById(1);
        logger.info(userDataById.getData().toString());
        System.out.println(userDataById.getData().toString());
    }

    @Before   //@Before是在这个测试类中点击每个@Test都会执行的注解,在`@Test`之前执行
    public void saveUserInfo() throws Exception {
        User user = new User();
        user.setUsername("Eirunye");
        user.setPassword("123456");
        user.setCreateTime(new Date());//这里只做测试用,一般都是后台处理时间
        user.setUpdateTime(new Date());//这里只做测试用,一般都是后台处理时间
        System.out.println(user.toString());
        Result<User> userResult = userService.saveUserData(user);
        System.out.println(userResult);
    }
//......
}
  • 测试验证
测试成功.png
  • 测试id输入错误时,如:userService.getUserDataById(0);
id输入错误异常.png
  • 查看数据库
查看数据库.png

2.IDEA 请求数据测试

这里只列举一个,大家进测试就好了。

idea请求数据.png

3.Postman请求数据测试

这里只列举一个,大家进测试就好了。

Postman请求数据.png

下载

本篇案例代码下载-码云
本篇案例代码下载-GitHub
Spring Boot系列代码-码云
Spring Boot系列代码-GitHub

总结

1.本篇文章主要讲解的是JPA与Spring Boot整合操作数据,只是简单的说明JPA的增删改查功能,接下来的文章会深入研究JPA的更多内容,如:JPA的多表操作如何使用、JPA分页操作等等。
2.JPA在项目中使用能使我们快速构建项目,不需要书写过多的SQL语句(相信这是大家都非常喜欢的)
3.前文开始的时候说了JPA一些特性。
4.Android平台上的GreenDao的使用方式和JPA是非常相似的

我的博客

大家可以到我的博客https://eirunye.github.io进行浏览相关文章,大家一起相互探讨技术。

推荐

Spring Boot 系列

如果大家想了解更多的Spring Boot相关博文请进入
我的Spring Boot系列博客栈

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

推荐阅读更多精彩内容