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分类
- 关系型数据库(API)
- JDBC
- JPA
- MyBatis
- 非关系型数据库
- 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
-
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自动建立数据库表的操作是相当方便的,它将自动创建/更新表。
实体类
@Entity
注解。被注解的实体类将被自动创建数据库表,例如对于TodoItem
类自动创建表todo_item
。@Id
与@GeneratedValue
注解。被注解属性将在自动创建时作为自增id。@NotNull
注解。表示属性非空。@Transient
注解。在实体类中的属性若被@Transient
注解,则自动创建数据库表时不会自动生成该属性。@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时才进行对应的查询。
接口使用与其测试
-
使用接口。声明接口
TodoJPARepository
继承自JpaRepository<TodoItem, Long>
与JpaSpecificationExecutor<TodoItem>
。- 只需要在Service中自动注入
TodoJPARepository
属性,而后就可以通过如下方法对数据库进行操作。- C:
saveAndFlush(todo)
。保存操作。 - R:
getById(id)
。取数据操作。 - U:
save(todo)
。更新操作。 - D:
deleteById(id)
。删除操作。
- C:
- 关于接口在此继承的两个父接口,只是jpa接口其二,jpa接口及其作用列举如下
CrudRepository
。提供CRUD方法。PagingAndSortingRepositoy
。提供分页和排序操作。JpaRepository
。提供CRUD方法的更多特性。JpaSpecificationExecutor
。支持动态查询语句。
- 只需要在Service中自动注入
-
测试相关。
@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)
-
Subject。如
findBy
、existsBy
以及countBy
等,用以区分询问的种类。 -
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()]));
。
关于测试部分,需添加以下注解:
@ParameterizedTest
。注解为参数化测试方法。@CsvSource({",, "})
。注解参数原。
事务
事务的ACID特性
- A: Atomicity。原子性,即全部成功完成或全部不做。
- C:Consistency。一致性,即事务完成前后数据完整性保持一致。
- I:Isolation。独立性,事务之间相互独立,互不影响。
- D:Durability。持久性,事务完成提交后,永久性改变将作用于数据库。
@Transactional注解
@Transactional
注解于方法表示方法为事务。特别的,可以在其中注明参数以达相应目的,如:
- 注明回退或不回退时的异常抛出类型:
@Transactional(rollbackFor = {SQLException.class})
@Transactional(noRollbackFor = {RuntimeException.class})
- 注明事务的传播属性:
@Transactional(propagation = Propagation.REQUIRED)
。支持当前事务,若无事务就新建事务。@Transactional(propagation = Propagation.SUPPORTS)
。支持当前事务,若无事务就以非事务进行。@Transactional(propagation = Propagation.REQUIRES_NEW)
。新建事务,若有当前事务,则将其挂起。