前言

最近公司的项目是基于 oracle 数据库的,这里记录下 springboot+mybatis+oracle 的踩坑过程。

开发前准备

环境参数

  • 开发工具:IDEA
  • 基础工具:Maven+JDK8
  • 所用技术:SpringBoot+Mybatis+Oracle
  • 数据库:MySQL5.7
  • SpringBoot 版本:2.2.6.RELEASE
  • Mybatis 版本: 2.1.2
  • Oracle: oracle-xe-11g

基于 Docker 搭建 Oracle

docker-compose-oracle.yml

version: '2'
services:
oracle:
image: sath89/oracle-xe-11g
container_name: oracle
ports:
- 1521:1521
- 8082:8080
volumes:
- ./oracle/data:/u01/app/oracle

连接参数

  • Connection Type: Basic
  • Host: localhost
  • Port: 1521
  • Service Name: xe
  • Username: system
  • Password: oracle

创建 表空间

Navicat 创建表空间

脚本创建表空间

-- 表空间 OA
-- 表空间物理文件位置 /u01/app/oracle/oradata/XE/OA
-- 大小20M
-- 每次20M自动增大
-- 最大100M
CREATE TABLESPACE "OA" DATAFILE '/u01/app/oracle/oradata/XE/OA' SIZE 20 M AUTOEXTEND ON NEXT 20 M MAXSIZE 100 M

创建 用户 并绑定 角色

Navicat 创建用户

Navicat 绑定角色

脚本创建用户并绑定角色

CREATE USER "OA" IDENTIFIED BY "123456" DEFAULT TABLESPACE "OA" TEMPORARY TABLESPACE "TEMP";

GRANT "DBA" TO "root";

ALTER USER "root" DEFAULT ROLE "DBA"

设计 Entity

public class Account {

    // 主键
private Integer id;
// 用户名(唯一)
private String realName;
// 工号(递增)
private Integer jobNumber;
// 创建时间
private Date createTime; public Account() {
} public Account(String realName, Integer jobNumber) {
this.realName = realName;
this.jobNumber = jobNumber;
this.createTime = new Date();
} public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getRealName() {
return realName;
} public void setRealName(String realName) {
this.realName = realName;
} public Integer getJobNumber() {
return jobNumber;
} public void setJobNumber(Integer jobNumber) {
this.jobNumber = jobNumber;
} public Date getCreateTime() {
return createTime;
} public void setCreateTime(Date createTime) {
this.createTime = createTime;
} @Override
public String toString() {
return "Account{" +
"id=" + id +
", realName='" + realName + '\'' +
", jobNumber=" + jobNumber +
", createTime=" + createTime +
'}';
}
}

创建数据库

-- ID 主键、自增
-- USER_NAME 唯一索引
-- JOB_NUMBER 递增
CREATE TABLE "OA"."ACCOUNT" (
"ID" NUMBER(18, 0) NOT NULL ,
"REAL_NAME" VARCHAR2(128) NOT NULL ,
"JOB_NUMBER" NUMBER(18,0) DEFAULT 0 NOT NULL ,
"CREATE_TIME" TIMESTAMP(6) NULL ,
PRIMARY KEY ("ID")
); -- 字段说明
COMMENT ON table ACCOUNT IS '账户表';
COMMENT ON COLUMN "OA"."ACCOUNT"."ID" IS '主键';
COMMENT ON COLUMN "OA"."ACCOUNT"."REAL_NAME" IS '用户名';
COMMENT ON COLUMN "OA"."ACCOUNT"."JOB_NUMBER" IS '工号';
COMMENT ON COLUMN "OA"."ACCOUNT"."CREATE_TIME" IS '创建时间'; -- 创建唯一索引
CREATE UNIQUE INDEX INDEX_REAL_NAME on ACCOUNT(REAL_NAME); -- 创建ID递增序列
CREATE SEQUENCE ACCOUNT_ID_SEQ INCREMENT BY 1 START WITH 1 NOMAXVALUE NOCYCLE NOCACHE; -- 创建触发器实现主键自增
CREATE OR REPLACE TRIGGER ACCOUNT_TRG BEFORE INSERT ON ACCOUNT FOR EACH ROW
BEGIN
SELECT ACCOUNT_ID_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL;
END; -- 查看序列
SELECT * FROM user_sequences;
-- 查看触发器
SELECT * FROM user_triggers;

代码实践

配置文件

pom.xml

<?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>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.idea360</groupId>
<artifactId>idc-oracle</artifactId>
<version>0.0.1</version>
<name>idc-oracle</name>
<description>Demo project for Spring Boot</description> <properties>
<java.version>1.8</java.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency> <dependency>
<groupId>com.oracle.ojdbc</groupId>
<artifactId>ojdbc8</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</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>

application.yml

server:
port: 9090
spring:
profiles:
active: local mybatis:
type-aliases-package: cn.idea360.oracle.model
configuration:
map-underscore-to-camel-case: true
default-fetch-size: 100
default-statement-timeout: 30
mapper-locations: mapper/*.xml logging:
level:
root: info

application-local.yml

spring:
datasource:
driver-class-name: oracle.jdbc.driver.OracleDriver
url: jdbc:oracle:thin:@127.0.0.1:1521:XE
username: oa
password: 123456

mybatis-config.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<typeAlias alias="account" type="cn.idea360.oracle.model.Account"/>
</typeAliases>
</configuration>

Java 代码

mapper 包扫描配置

@MapperScan("cn.idea360.oracle.dao")
@SpringBootApplication
public class OracleApp { public static void main(String[] args) {
SpringApplication.run(OracleApp.class, args);
} }

分页

/**
* Mapper进一步实现可以基于拦截器实现
* @param <T>
*/
@Data
public class Page<T> { /**
* 查询数据列表
*/
private List<T> records = Collections.emptyList();
/**
* 总数
*/
private long total = 0;
/**
* 每页显示条数,默认 10
*/
private long size = 10;
/**
* 当前页
*/
private long current = 1;
/**
* KEY/VALUE 条件
*/
private Map<Object, Object> condition; /**
* oracle分页: start
*/
private Integer startIndex = 1;
/**
* oracle分页: end
*/
private Integer endIndex = 10; public Page() {
} public Page(long current, long size) {
this(current, size, 0);
} public Page(long current, long size, long total) {
if (current > 1) {
this.current = current;
}
this.size = size;
this.total = total;
} /**
* 计算当前分页偏移量
*/
public long offset() {
return getCurrent() > 0 ? (getCurrent() - 1) * getSize() : 0;
} /**
* 当前分页总页数
*/
public long getPages() {
if (getSize() == 0) {
return 0L;
}
long pages = getTotal() / getSize();
if (getTotal() % getSize() != 0) {
pages++;
}
return pages;
} /**
* oracle分页开始
* @return
*/
public long getStartIndex() {
return (getCurrent()-1)*size+1;
} /**
* oracle分页结束
* @return
*/
public long getEndIndex() {
return getCurrent()*size;
} }

mapper 定义

public interface AccountMapper {

    /**
* 插入单条数据
* @param account
* @return 返回主键id
*/
int insert(Account account); /**
* 批量插入list
* @param data
*/
void insertBatch(@Param("coll") Collection<Account> data); /**
* 更新数据
* @param account
* @return
*/
int updateIgnoreNullById(@Param("et") Account account); /**
* 删除数据
* @param id
* @return
*/
int removeById(@Param("id") Integer id); /**
* 根据id查询数据
* @param id
* @return
*/
Account selectById(@Param("id") Integer id); /**
* 根据其他字段查询数据
* @param columnMap
* @return
*/
Account selectByMap(@Param("cm") Map<String, Object> columnMap); /**
* 根据id数组批量查询数据
* @param idArray
* @return
*/
List<Account> selectByIds(@Param("coll") Integer[] idArray); /**
* 根据分页参数查询数据
* @param page
* @return
*/
List<Account> selectPage(@Param("page") Page page); /**
* 查询所有
* @return
*/
List<Account> listAll(); }

AccountMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="cn.idea360.oracle.dao.AccountMapper"> <!--resultType是直接表示返回类型的(对应着我们的model对象中的实体),而resultMap则是对外部ResultMap的引用(提前定义了db和model之间的隐射key-value关系)-->
<resultMap id="accountMap" type="account">
<id column="id" property="id" />
<result column="real_name" property="realName"/>
<result column="job_number" property="jobNumber" />
<result column="create_time" property="createTime" />
</resultMap> <!--插入单条数据,自增主键通过序列和触发器实现-->
<insert id="insert" parameterType="account" useGeneratedKeys="true">
insert into account (real_name, job_number, create_time)
values
(#{realName}, #{jobNumber}, #{createTime})
<selectKey keyProperty="id" resultType="java.lang.Integer" order="AFTER">
select ACCOUNT_ID_SEQ.CURRVAL from dual
</selectKey>
</insert> <!--通过list批量插入数据-->
<insert id="insertBatch" parameterType="java.util.List">
insert into account (real_name, job_number, create_time)
<foreach collection="coll" item="item" index="index" separator="UNION ALL">
select
#{item.realName}, #{item.jobNumber}, #{item.createTime}
from dual
</foreach>
</insert> <!--更新数据-->
<update id="updateIgnoreNullById">
update account
<set>
<if test="et.realName != null">
real_name = #{et.realName},
</if>
<if test="et.jobNumber != null">
job_number = #{et.jobNumber},
</if>
</set>
where id = #{et.id}
</update> <!--根据主键id移除数据-->
<delete id="removeById">
delete from account where id = #{id}
</delete> <!--根据主键id查询数据-->
<select id="selectById" parameterType="integer" resultMap="accountMap">
select * from account where id = #{id}
</select> <!--根据其他字段查询数据-->
<select id="selectByMap" resultMap="accountMap">
select * from account
<where>
<if test="cm != null and cm.realName != null">
and real_name = #{cm.realName}
</if>
</where>
</select> <!--根据id数组查询数据-->
<select id="selectByIds" resultMap="accountMap">
select * from account where id in
<foreach collection="coll" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select> <!--根据分页参数查询数据-->
<select id="selectPage" resultMap="accountMap">
SELECT
T2.*
FROM
( SELECT T1.*, ROWNUM RN FROM ( SELECT a.* FROM account a
<where>
<if test="page != null and page.condition != null and page.condition.keyword != null">
AND a.real_name LIKE '%' || #{page.condition.keyword} || '%'
</if>
</where>
ORDER BY id DESC
) T1 ) T2
<where>
<if test="page != null and page.startIndex != null">
and RN <![CDATA[ >= ]]> #{page.startIndex}
</if>
<if test="page != null and page.endIndex != null">
AND RN <![CDATA[ <= ]]> #{page.endIndex}
</if>
</where>
</select>
<select id="listAll" resultType="cn.idea360.oracle.model.Account">
select * from account
</select> </mapper>

单元测试

@SpringBootTest
class AccountMapperTest { @Autowired
private AccountMapper accountMapper; @Test
public void insert() {
Account account = new Account("admin", 1);
accountMapper.insert(account);
System.out.println(account.getId());
} @Test
public void insertBatch() {
List<Account> data = new ArrayList<>();
for (int i=0; i<3; i++) {
Account account = new Account("test" + i, i+2);
data.add(account);
}
accountMapper.insertBatch(data);
} @Test
public void updateIgnoreNullById() {
Account account = new Account("admin0", 1);
account.setId(1);
accountMapper.updateIgnoreNullById(account);
} @Test
public void removeById() {
accountMapper.removeById(4);
} @Test
public void selectById() {
Account account = accountMapper.selectById(4);
System.out.println(account.toString());
} @Test
public void selectByMap() {
Map<String, Object> params = new HashMap<>();
params.put("realName", "admin");
Account account = accountMapper.selectByMap(params);
System.out.println(account);
} @Test
public void selectByIds() {
Integer[] ids = {1, 2};
List<Account> accounts = accountMapper.selectByIds(ids);
System.out.println(accounts);
} @Test
public void selectPage() {
Page<Account> page = new Page<>(1, 10);
HashMap<Object, Object> condition = new HashMap<>();
condition.put("keyword", "ad");
page.setCondition(condition);
List<Account> accounts = accountMapper.selectPage(page);
page.setRecords(accounts);
System.out.println(page);
} @Test
public void listAll() {
List<Account> accounts = accountMapper.listAll();
System.out.println(accounts);
}
}

最后

oracle 在自增主键、分页、模糊查询、批量操作上和 mysql 略有不同,上边的例子里基本都演示到了。初次接触 oracle, 如有错误还请指出, 共同进步。同时,希望大家关注公众号【当我遇上你】, 您的支持就是我最大的动力。

springboot-mybatis-oracle学习笔记的更多相关文章

  1. Oracle学习笔记三 SQL命令

    SQL简介 SQL 支持下列类别的命令: 1.数据定义语言(DDL) 2.数据操纵语言(DML) 3.事务控制语言(TCL) 4.数据控制语言(DCL)  

  2. SpringBoot + Spring Security 学习笔记(五)实现短信验证码+登录功能

    在 Spring Security 中基于表单的认证模式,默认就是密码帐号登录认证,那么对于短信验证码+登录的方式,Spring Security 没有现成的接口可以使用,所以需要自己的封装一个类似的 ...

  3. SpringBoot + Spring Security 学习笔记(三)实现图片验证码认证

    整体实现逻辑 前端在登录页面时,自动从后台获取最新的验证码图片 服务器接收获取生成验证码请求,生成验证码和对应的图片,图片响应回前端,验证码保存一份到服务器的 session 中 前端用户登录时携带当 ...

  4. oracle学习笔记第一天

    oracle学习笔记第一天 --oracle学习的第一天 --一.几个基础的关键字   1.select select (挑选) 挑选出显示的--列--(可以多列,用“,”隔开,*表示所有列),为一条 ...

  5. Oracle学习笔记——点滴汇总

    Oracle学习笔记——点滴汇总 http://www.botangdb.com/ Oracle GI = Grid Infrastructure = ASM + Cluster

  6. Oracle学习笔记之四sp1,Oracle 11g的常用函数

    从Oracle学习笔记之四,SQL语言入门中摘出来的,独立成一章节 3.1 字符类函数 ASCII(c)和CHR(i)    分别用于返回一个字符的ASCII码和返回给定ASCII值所对应的字符. C ...

  7. Oracle学习笔记之四,SQL语言入门

    1. SQL语言概述 1.1 SQL语言特点 集合性,SQL可以的高层的数据结构上进行工作,工作时不是单条地处理记录,而对数据进行成组的处理. 统一性,操作任务主要包括:查询数据:插入.修改和删除数据 ...

  8. Oracle学习笔记—数据字典和常用命令(转载)

    转载自: oracle常用数据字典和SQL语句总结 Oracle常用命令大全(很有用,做笔记) 一.Oracle数据字典 数据字典是Oracle存放有关数据库信息的地方,其用途是用来描述数据的.比如一 ...

  9. MyBatis:学习笔记(4)——动态SQL

    MyBatis:学习笔记(4)——动态SQL 如果使用JDBC或者其他框架,很多时候需要你根据需求手动拼装SQL语句,这是一件非常麻烦的事情.MyBatis提供了对SQL语句动态的组装能力,而且他只有 ...

  10. oracle学习笔记(一)用户管理

    --oracle学习第一天 --连接 @后面连接数据库实例,具体连接到那个数据库 conn scott/tiger@MYORA1; --修改密码 passw; --显示用户 show user; -- ...

随机推荐

  1. 数据结构 5 哈希表/HashMap 、自动扩容、多线程会出现的问题

    上一节,我们已经介绍了最重要的B树以及B+树,使用的情况以及区别的内容.当然,本节课,我们将学习重要的一个数据结构.哈希表 哈希表 哈希也常被称作是散列表,为什么要这么称呼呢,散列.散列.其元素分布较 ...

  2. go学习第四天、条件和循环

    循环 Go语言仅支持循环关键字 for for i := 0; i<5; i++ 示例 while 条件循环 while(n<5) n := 0 for n < 5 { n++ fm ...

  3. Python中使用subprocess模块远程执行命令

    使用subprocess模块执行远程命令 服务端代码 1 import socket 2 import subprocess 3 4 sh_server = socket.socket() 5 sh_ ...

  4. Navicat15最新版本破解 亲测可用!!!

    1.下载Navicat Premium官网https://www.navicat.com.cn/下载最新版本下载安装 2.本人网盘链接:https://pan.baidu.com/s/1ncSaxId ...

  5. ASP.net MVC 构建layui管理后台(整体效果)

    登录页: 首页 模块管理 角色管理,角色分配 用户管理

  6. npm 安装 electron 出现的奇葩错误

    起因 使用yarn安装electron有时稳定,在搭配别的框架时有时又出现无解的错误了,于是打开electron官网,发现: emm,似乎官方对npm情有独钟.于是我遵从官方旨意使用npm安装elec ...

  7. 【Linux】linux系统管理---好用的一些开源工具

    目录 linux系统管理---好用的一些开源工具 htop dstat Glances iftop nethogs iotop linux系统管理---好用的一些开源工具 htop htop是一款运行 ...

  8. 升级cocoapods到指定版本

    把cocoapods升级到1.7.4 sudo gem install -n /usr/local/bin cocoapods -v 1.7.4

  9. 【转】不怕难之BlockingQueue及其实现

    1. 前言 BlockingQueue即阻塞队列,它是基于ReentrantLock,依据它的基本原理,我们可以实现Web中的长连接聊天功能,当然其最常用的还是用于实现生产者与消费者模式,大致如下图所 ...

  10. 回文串的Manacher算法

    Manacher算法较传统算法的优化之处在于它对每个回文中心寻找回文半径的时候并不是都从半径为1开始找的,而是利用前面已经完成的任务,寻找一个初始的开始搜索的半径大小,复杂度是线性的. 参考博客:ht ...