JavaEE学习笔记5——JDBC&JPA

JavaEE学习笔记5


Note for JavaEE-Day5

开发必备,数据库操作来啦~

maven多模块

在一个maven项目中可以存在许多子模块,其各有自己的pom.xml文件,其共享父模块的pom.xml中的属性与依赖配置。

父模块

  • 父模块不需要编译为jar包,设置为pom
<packaging>pom</packaging>
  • 父模块中声明子模块
<modules>
	<module>springboot-jdbc</module>
	<module>springboot-jpa</module>
	<module>springboot-jpa-hello</module>
	<module>springboot-transaction</module>
</modules>
  • 父模块中声明的属性(<properties>)、依赖(<dependencies>)与插件(<plugins>)均将由子模块共享,即在子模块中不必重复声明。

子模块

  • 子模块中需要声明父模块
<parent>
    <artifactId>springboot-data</artifactId>
    <groupId>edu.whu</groupId>
    <version>1.0.0</version>
</parent>
  • 子模块中后续再声明自己的依赖等配置

JDBC

SpringData分类

  1. 关系型数据库(API)
    • JDBC
    • JPA
    • MyBatis
  2. 非关系型数据库
    • CouchDB
    • MongoDB
    • Cassandra
    • Neo4j
    • Solr
    • Redis

JDBC配置

  • 配置pom.xml
<!-- jdbc依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<!-- mysql数据库依赖 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
  • 配置application.yaml。即在application.yaml中配置数据库源、用户等。
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/todos?characterEncoding=utf8
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456
  1. idea相关配置。

    • 在database栏(ideaUI中最右侧栏目)中添加数据库(这里为mysql),配置对应用户名、密码及数据库即可。
    • File->Project Structure->Project Settings->Modules->Dependencies->+JARs or Directories添加于MySQL官网下载的jar包mysql-connector

使用JDBC模板执行SQL

  • 首先自动注入jdbc模板
@Autowired
JdbcTemplate jdbcTemplate;
  • 而后通过jdbc模板执行相关SQL指令,如

    • jdbcTemplate.query (sql+" where COMPLETE = ? ", todoItemRowMapper, complete)
    • jdbcTemplate.update("insert into todo(ID, NAME, COMPLETE) values(?,?,?)",todo.getId(), todo.getName(),todo.isComplete())

注意这里?表示参数,在sql语句后将按顺序给出。

使用RowMapper处理询问结果

RowMapper是内置接口,被@FunctionalInterface注解,参数为sql查询结果与行数。在上节中提到的todoItemRowMapper定义如下

RowMapper<TodoItem> todoItemRowMapper = (resultSet, i) -> {
    TodoItem todo = new TodoItem();
    todo.setName(resultSet.getString("NAME"));
    todo.setId(resultSet.getLong("ID"));
    todo.setComplete(resultSet.getBoolean("COMPLETE"));
    return todo;
};

即使用lambda表达式进行赋值,作用为将sql结果作为TodoItem输出。

JPA基本介绍

ORM

ORM,Object Relational Mapping,即对象关系映射。

  • ORM作用为将对象映射为关系模型
  • 使用ORM可以使程序以面向对象的方法处理数据库
  • JPA与MyBatis都是ORM框架

JPA配置

与JDBC同理,分为pom配置、yaml配置与idea配置,其中idea配置相同。

  • pom.xml配置。即引入jpa依赖。
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
  • application.yaml配置。即在jdbc的基础上添加jpa相关配置。
spring:
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: update
  datasource:
    url: jdbc:mysql://localhost:3306/todos?useSSL=false&autoReconnect=true&characterEncoding=utf8
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456

这里特别指出yaml配置中的ddl-auto选项,其全称为spring.jpa.hibernate.ddl-auto,有以下4个可选参数:

  • create:每次加载hibernate时都会删除上一次的生成的表,然后根据你的model类再重新来生成新表,哪怕两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因。
  • create-drop:每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。
  • update:最常用的属性,第一次加载hibernate时根据model类会自动建立起表的结构(前提是先建立好数据库),以后加载hibernate时根据model类自动更新表结构,即使表结构改变了但表中的行仍然存在不会删除以前的行。要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等应用第一次运行起来后才会。
  • validate:每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。

由此可见如update自动建立数据库表的操作是相当方便的,它将自动创建/更新表。

实体类

  1. @Entity注解。被注解的实体类将被自动创建数据库表,例如对于TodoItem类自动创建表todo_item
  2. @Id@GeneratedValue注解。被注解属性将在自动创建时作为自增id。
  3. @NotNull注解。表示属性非空。
  4. @Transient注解。在实体类中的属性若被@Transient注解,则自动创建数据库表时不会自动生成该属性。
  5. @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.REFRESH)。注解表的一对多关系用以自动创建关联表(即生成新的表关联二者),被注解的即为关联表的实体类list。例如此处为User到Role的一对多关系。
    • cascade = CascadeType.有如下示例:
      • REMOVE。删除user将删除与之相关联的role
      • PERSIST。创建user将创建与之相关联的role。
      • REFRESH。从数据库加载user会同时加载与之对应的role。
    • 另外,关于fetch = FetchType.也有如下示例:
      • EAGER。查询user时立即查询对应的role。
      • Lazy。直到后面需要读取role时才进行对应的查询。

接口使用与其测试

  1. 使用接口。声明接口TodoJPARepository继承自JpaRepository<TodoItem, Long>JpaSpecificationExecutor<TodoItem>

    • 只需要在Service中自动注入TodoJPARepository属性,而后就可以通过如下方法对数据库进行操作。
      • C: saveAndFlush(todo)。保存操作。
      • R: getById(id)。取数据操作。
      • U: save(todo)。更新操作。
      • D: deleteById(id)。删除操作。
    • 关于接口在此继承的两个父接口,只是jpa接口其二,jpa接口及其作用列举如下
      • CrudRepository。提供CRUD方法。
      • PagingAndSortingRepositoy。提供分页和排序操作。
      • JpaRepository。提供CRUD方法的更多特性。
      • JpaSpecificationExecutor。支持动态查询语句。
  2. 测试相关。

    • @SpringBootTest注解。启动整个spring应用以测试,同时支持在test相关类中进行属性注入。
    • @Transactional注解。注意在测试中注解于测试类或测试方法时,其含义并非传统事务,而是对于每项Test,完成后无论结果如何操作都将回滚至初始状态,数据库也会还原。

JPA工作原理

如上节中的TodoJPARepository,SpringData的JPA框架会检查该接口,然后根据接口使用动态代理创建对象。

使用JPA的询问

询问创建

这部分还是声明新接口继承自前面的接口,如JpaRepository,在新接口中声明查询形式如下

Q u e r y = S u b j e c t + P r e d i c a t e (1) Query = Subject + Predicate \tag{1} Query=Subject+Predicate(1)

  1. Subject。如findByexistsBy以及countBy等,用以区分询问的种类。

  2. Predicate。包含查询的属性条件,可以为多个之间使用And/Or连接,属性后的查询条件如StartingWith, EndingWith, Containing, IgnoreCase

JPQL与NativeQuery

JPA中无论使用JPQL还是SQL都比较简单,即在接口对应方法的声明前使用@Query("select ... from ... where ...= ?1")注解query语句即可,这里?1为方法中的参数。默认以JPQL执行,使用SQL需要添加参数,形式为@Query(value = "select ... from ... where name like ?1",nativeQuery = true),即使用NativeQuery。

关于查询语句形式,JPQL与SQL非常相似,形式如

select 实体别名.属性名, 实体别名.属性名 from 实体名 as 实体别名 where 实体别名.实体属性 op 比较值

例如,以下两句JPQL与SQL查询对应注解作用相同。

@Query("select u from User u where name= ?1")
@Query(value = "select * from user where name like ?1",nativeQuery = true)

分页与排序

分页查询将返回查询结果和页信息,需要首先在接口查询声明时添加pageable参数,代表查询的分页与排序条件,形式如下:

Page<User> findByNameContaining(String name, Pageable pageable);

其中pageable的定义如下:

//对应查询按每页大小为3分页的第一页
Pageable pageable = PageRequest.of(0,3);
//可添加查询排序如下
pageable = PageRequest.of(0,3,Sort.by("age"));//升序
pageable = PageRequest.of(0,3,Sort.by(Sort.Direction.DESC,"age"));//降序

使用Specification查询

使用方法为调用接口的findAll方法,内部对应root、query、criteriaBuilder三个参数使用lambda表达式引出查询条件以及分页条件。例子如下:

PageRequest pageable = PageRequest.of(0, 3, Sort.by(Sort.Direction.DESC, "age"));
List<User> result2 = userRepsitory.findAll( (root, query, cb) ->
               cb.and(cb.like(root.get("name"), "%黑马%"),cb.ge(root.get("age"),14))
               ,pageable);

动态查询

以参数形式引入查询条件,在前面规范查询的lambda表达式部分使用List<Predicate> pList=new ArrayList<>();作为条件队列,而后根据参数加入条件,形如pList.add(cb.like(root.get("name"), "%"+name+"%")),最后再将条件转化为谓词数组return cb.and(pList.toArray(new Predicate[pList.size()]));

关于测试部分,需添加以下注解:

  1. @ParameterizedTest。注解为参数化测试方法。
  2. @CsvSource({",, "})。注解参数原。

事务

事务的ACID特性

  1. A: Atomicity。原子性,即全部成功完成或全部不做。
  2. C:Consistency。一致性,即事务完成前后数据完整性保持一致。
  3. I:Isolation。独立性,事务之间相互独立,互不影响。
  4. D:Durability。持久性,事务完成提交后,永久性改变将作用于数据库。

@Transactional注解

@Transactional注解于方法表示方法为事务。特别的,可以在其中注明参数以达相应目的,如:

  1. 注明回退或不回退时的异常抛出类型:
    • @Transactional(rollbackFor = {SQLException.class})
    • @Transactional(noRollbackFor = {RuntimeException.class})
  2. 注明事务的传播属性:
    • @Transactional(propagation = Propagation.REQUIRED)。支持当前事务,若无事务就新建事务。
    • @Transactional(propagation = Propagation.SUPPORTS)。支持当前事务,若无事务就以非事务进行。
    • @Transactional(propagation = Propagation.REQUIRES_NEW)。新建事务,若有当前事务,则将其挂起。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hiroxzwang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值