以JPA方式操作Oracle-XE-11g
一、数据库工作准备
- 在上一篇中,已经成功启动 Docker,并且启动 Oracle-xe-11g 作为一个容器运行,同时也在 Navicat 成功连接上 Oracle 数据库。
- 为了 Java 程序能够正确交互数据库,需要在数据库中做一些准备,主要有两个工作:准备 Hibernate 需要的一个序列和主键自增所需的触发器以及相关的用户。
1、创建用户和用户赋权
- 新建一个用户,命名为 boot,密码也设置为 boot
create user boot identified by boot;
- 创建用户后,得赋予创建会话等权限,不然无法登录
grant create session,resource to boot;
2、准备序列
- 使用 Spring Boot 以 JPA 方式操作数据库,往往会结合 Hibernate 进行,而在这里使用 Hibernate 自动将实体类映射为数据表格时,会用到一个序列hibernate_sequence,因此先要把这个序列准备好。
- 在 Navicat 软件中,展开 Oracle 服务器节点,选中目标数据库,右键新建查询,在查询脚本编辑界面中,编辑如下 sql 语句:
create sequence hibernate_sequence increment by 1 start with 0 nomaxvalue nocache
3、实现主键自增
- 在一个数据表中,通常会选择一个 ID 列作为主键,并且让该字段是自增的,Oracle 数据库不像 MySQL 那样有支持标识符自增的功能,需要通过编写前触发器来实现。
- sql 语句如下:
create or replace trigger ID_autoincrement before insert on PERSON for each row when(new.id is null) begin select hibernate_sequence.nextval into :new.id from dual; end;
- 这里的 PERSON 就是目标数据表。
二、项目代码编辑
- 接下来开始编辑项目的代码。
1、创建项目和准备依赖
- 首先,在 IDEA 中使用项目创建向导新建一个 Spring Boot 项目,用 Spring Initializer 并勾选 web 和 JPA 依赖。
- 项目信息如下:
<groupId>com.pyc</groupId> <artifactId>springjpa</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>springjpa</name>
- 这里要操作的数据库是 Oracle 数据库,因此需要 Oracle 数据库的驱动包:ojdbc6.jar,但是这个包在 Maven 中央库中没有,因此我们需要手动下载后进行打包到本地 Maven 库;打包的命令如下(在 CMD 窗口)
mvn install:install-file -DgroupId=com.oracle "-DartifactId=ojdbc6" "-Dversion=11.2.0.2.0" "Dfile=ojdbc6.jar 下载存放的路径"
- 打包成功后,在项目的 POM 文件中加入 ojdbc6.jar 的坐标:
<!-- Oracle 数据库依赖--> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc6</artifactId> <version>11.2.0.2.0</version> </dependency>
- 同时增加 Google guava 依赖,该依赖可以提供大量的 Java 工具类。
<!-- Google guava 依赖--> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>18.0</version> </dependency>
- 整个 POM 文件的内容如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.0.M1</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.pyc</groupId> <artifactId>springjpa</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>springjpa</name> <description>Demo project for Spring Boot</description> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <dependencies> <!-- Oracle 数据库依赖--> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc6</artifactId> <version>11.2.0.2.0</version> </dependency> <!-- Google guava 依赖--> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>18.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
2、准备一个初始数据表的 SQL 文件
- 在 src/main/resources 目录下,新建一个 data.sql 文件,编辑如下代码:
insert into person(id,name,age,address) values(hibernate_sequence.nextval,'pyc',22,'汕尾'); insert into person(id,name,age,address) values(hibernate_sequence.nextval,'xx',21,'天津'); insert into person(id,name,age,address) values(hibernate_squence.nextval,'yy',20,'上海'); insert into person(id,name,age,address) values(hibernate_sequence.nextval,'zz',24,'大理'); insert into person(id,name,age,address) values(hibernate_sequence.nextval,'aa',25,'北京'); insert into person(id,name,age,address) values(hibernate_sequence.nextval,'bb',26,'南昌');
- 该文件在项目第一次运行成功后请删除或者更改文件名后缀
3、编辑 Properties 文件
- 编辑 application/properties 文件配置数据源、JPA 和 log 的相关属性。
# 配置数据源 spring.datasource.driver-class-name=oracle.jdbc.OracleDriver spring.datasource.url=jdbc\:oracle\:thin\:@localhost\:1521\:XE spring.datasource.username=boot spring.datasource.password=boot # 利用 Hibernate 自动维护数据库表结构的功能 spring.jpa.hibernate.ddl-auto=update # 设置 Hibernate 操作数据库的时候在控制台显示真实的 spl 语句 spring.jpa.show-sql=true # 让控制器输出 json 字符串格式更美观 spring.jackson.serialization.indent-output=true logging.file=log.log logging.level.org.springframework.web=DEBUG
4、实体类
- 创建一个类名为 Person 的实体类,代码如下:
package com.pyc.springjpa.domain; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.NamedQuery; @Entity //指明这是一个和数据库表映射的实体类 @NamedQuery(name = "Person.withNameAndAddressNamedQuery", query = "select p from Person p where p.name=?1 and p.address=?2") public class Person { @Id // 指明这个属性映射为数据库的主键 @GeneratedValue //指明主键的生成方式为自增 private Long id; private String name; private Integer age; private String address; public Person() { super(); } public Person(Long id, String name, Integer age, String address){ super(); this.address=address; this.name=name; this.age=age; this.id=id; } public void setId(Long id) { this.id = id; } public void setName(String name) { this.name = name; } public void setAge(Integer age) { this.age = age; } public void setAddress(String address) { this.address = address; } public Long getId() { return id; } public String getName() { return name; } public Integer getAge() { return age; } public String getAddress() { return address; } }
- Hibernate 会自动根据属性名生成数据表的字段名。
5、接口编写
- 创建一个接口类,编写用于访问数据库数据的接口方法,代码如下:
package com.pyc.springjpa.dao; import com.pyc.springjpa.domain.Person; import org.springframework.data.repository.query.Param; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import java.util.List; public interface PersonRepository extends JpaRepository<Person,Long> { // 根据地址查询,返回值为列表 List<Person> findByAddress(String address); // 根据姓名和地址查询,返回值为单个对象 Person findByNameAndAddress(String name, String address); // 使用 @Query 查询,参数按照名称绑定 @Query("select p from Person p where p.name= :name and p.address= :address") Person withNameAndAddressQuery(@Param("name")String name,@Param("address")String address); //使用 @NamedQuery 查询,在实体类中已注解 Person withNameAndAddressNamedQuery(String name, String address); }
6、Controller
- 编写一个控制器,用于控制前台对接口的调用逻辑,代码如下:
package com.pyc.springjpa.web; import com.pyc.springjpa.dao.PersonRepository; import com.pyc.springjpa.domain.Person; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController public class DataController { // Spring Data JPA 已自动注册bean,因此可以自动注入 @Autowired PersonRepository personRepository; /* * 保存 * save 支持批量保存:<S extends T> Iterable<S> save(Iterable<S> entities); * 删除 * 支持使用 id 删除对象、批量删除以及删除全部 * void delete(ID id) * void delete(T entity) * void delete(Iterable<? extends T>entities); * void deleteAll(); */ @RequestMapping("/save") public Person save(String name,String address,Integer age){ Person p; p = personRepository.save(new Person(null, name,age,address)); return p; } /* * 测试 findByAddress */ @RequestMapping("/q1") public List<Person> q1(String address){ List<Person> people; people = personRepository.findByAddress(address); return people; } // 测试 findByNameAndAddress @RequestMapping("/q2") public Person q2(String name, String address){ Person person; person = personRepository.findByNameAndAddress(name,address); return person; } //测试 withNameAndAddressQuery @RequestMapping("/q3") public Person q3(String name, String address){ Person p; p = personRepository.withNameAndAddressQuery(name, address); return p; } //测试 withNameAndAddressNamedQuery @RequestMapping("/q4") public Person q4(String name, String address){ Person p; p = personRepository.withNameAndAddressNamedQuery(name, address); return p; } //排序 @RequestMapping("/sort") public List<Person> sort(){ List<Person> people; people = personRepository.findAll(new Sort(Sort.Direction.ASC, "age")); return people; } // 测试分页 @RequestMapping("/page") public Page<Person> page(){ Page<Person> pagePeople; pagePeople = personRepository.findAll(new PageRequest(1,2)); return pagePeople; } }
三、项目运行测试
- 在入口类中启动运行,控制台若无错误,到 Navicat 中展开 boot 数据库,可以看到生成了一个名为 PERSON 的数据表
- 在浏览器中访问 localhost:8080/save?name=csdn&address=杭州&age=20,后台返回 JSON 格式的数据,浏览器页面如下:
- 回到 Navicat,打开 PERSON 数据表,数据如下:
- 浏览器访问 localhost:8080/q1?address=上海
- 浏览器访问 localhost:8080/q2?address=上海&name=pyc
- localhost:8080/q3?address=汕尾&namepyc
- localhost:8080/q4?address=大理&name=zz
- localhost:8080/sort
- localhost:8080/page