springboot-mybatis-oracle学习笔记
前言
最近公司的项目是基于 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学习笔记的更多相关文章
- Oracle学习笔记三 SQL命令
SQL简介 SQL 支持下列类别的命令: 1.数据定义语言(DDL) 2.数据操纵语言(DML) 3.事务控制语言(TCL) 4.数据控制语言(DCL)
- SpringBoot + Spring Security 学习笔记(五)实现短信验证码+登录功能
在 Spring Security 中基于表单的认证模式,默认就是密码帐号登录认证,那么对于短信验证码+登录的方式,Spring Security 没有现成的接口可以使用,所以需要自己的封装一个类似的 ...
- SpringBoot + Spring Security 学习笔记(三)实现图片验证码认证
整体实现逻辑 前端在登录页面时,自动从后台获取最新的验证码图片 服务器接收获取生成验证码请求,生成验证码和对应的图片,图片响应回前端,验证码保存一份到服务器的 session 中 前端用户登录时携带当 ...
- oracle学习笔记第一天
oracle学习笔记第一天 --oracle学习的第一天 --一.几个基础的关键字 1.select select (挑选) 挑选出显示的--列--(可以多列,用“,”隔开,*表示所有列),为一条 ...
- Oracle学习笔记——点滴汇总
Oracle学习笔记——点滴汇总 http://www.botangdb.com/ Oracle GI = Grid Infrastructure = ASM + Cluster
- Oracle学习笔记之四sp1,Oracle 11g的常用函数
从Oracle学习笔记之四,SQL语言入门中摘出来的,独立成一章节 3.1 字符类函数 ASCII(c)和CHR(i) 分别用于返回一个字符的ASCII码和返回给定ASCII值所对应的字符. C ...
- Oracle学习笔记之四,SQL语言入门
1. SQL语言概述 1.1 SQL语言特点 集合性,SQL可以的高层的数据结构上进行工作,工作时不是单条地处理记录,而对数据进行成组的处理. 统一性,操作任务主要包括:查询数据:插入.修改和删除数据 ...
- Oracle学习笔记—数据字典和常用命令(转载)
转载自: oracle常用数据字典和SQL语句总结 Oracle常用命令大全(很有用,做笔记) 一.Oracle数据字典 数据字典是Oracle存放有关数据库信息的地方,其用途是用来描述数据的.比如一 ...
- MyBatis:学习笔记(4)——动态SQL
MyBatis:学习笔记(4)——动态SQL 如果使用JDBC或者其他框架,很多时候需要你根据需求手动拼装SQL语句,这是一件非常麻烦的事情.MyBatis提供了对SQL语句动态的组装能力,而且他只有 ...
- oracle学习笔记(一)用户管理
--oracle学习第一天 --连接 @后面连接数据库实例,具体连接到那个数据库 conn scott/tiger@MYORA1; --修改密码 passw; --显示用户 show user; -- ...
随机推荐
- LeetCode(239.滑动窗口的最大值
题目: 给定一个数组nums,有一个大小为k的滑动窗口从数组的最左侧移动到最右侧,你只可以看到滑动窗口内的k个数字.滑动窗口每次只向右移动一位. 返回滑动窗口中的最大值. 示例: 输入: nums = ...
- libfastcommon总结(一)加载主机上所有网卡的IPv4的地址
头文件为local_ip_func.h 主要接口 load_local_host_ip_addrs();//加载主机网口所有IPv4地址到列表 print_local_host_ip_addrs ...
- 创建Sphinx + GitHub + ReadtheDocs托管文档
最新博客链接 "Tsanfer's Blog" 创建Sphinx + GitHub + ReadtheDocs托管文档 Readthedocs在线电子书链接
- CVE-20117-111882漏洞复现及利用
背景 工程实践题目: 渗透方向:实验班要求 1.利用已有的漏洞,搭建内网实验环境(WEB漏洞或系统漏洞以近两年内的CVE编号为准,每人一个,先报先得,具体由学习委员负责协调),利用工具进行内网渗透攻击 ...
- 使用 Docker 部署 Spring Boot 项目
Docker 介绍 Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口.它是目前最流行的 Linux 容器解决方案. Docker 将应用程序与该程序的依赖,打包在一个文件里面 ...
- 解决Requires: libc.so.6(GLIBC_2.14)(64bit)错误解决方法
glibc简介: glibc是GNU发布的libc库,即c运行库.glibc是linux系统中最底层的api,几乎其它任何运行库都会依赖于glibc.glibc除了封装linux操作系统所提供的系统服 ...
- Java中内部类和静态内部类的区别
内部类和静态内部类 示例 public class OuterClass { private int numPrivate = 1; public int numPublic = 2; public ...
- Linux下安装MySQL的tar.gz包
以root用户登录待安装的服务器. 上传软件包并解压. 以root用户通过sftp/ftp工具上传“mysql-5.7.24-linux-glibc2.12-x86_64.tar.gz”软件包到“/o ...
- Python之open()函数
Python内置了读写文件的函数open(). # 方法一 # 使用Python内置的open()函数,传入文件名和标示符 f = open('E:/test/driver.py', 'r', enc ...
- leetcode之820. 单词的压缩编码 | python极简实现字典树
题目 给定一个单词列表,我们将这个列表编码成一个索引字符串 S 与一个索引列表 A. 例如,如果这个列表是 ["time", "me", "bell& ...