上一篇《APEX实战第2篇:构建自己第一个APEX程序》虽然有了程序,但实在是太单薄!

本篇将会介绍一些数据库的基础知识,演示如何通过函数、触发器、存储过程、视图等来完善项目的一些基础功能。

没有编程经验也完全没关系,笔者其实也从来都没做过程序员,但可以借助APEX结合一些数据库基础知识,就能让我们也能轻松构建属于自己的应用程序。

比如这里举例,我想构建一个学习平台,所谓活到老学到老,这里就叫小鲸鱼终身学习平台吧,嗯,让它看起来像个真正的项目,这样介绍起来也会有趣些。

项目名称:小鲸鱼终身学习平台

嗯。。做戏做全套,干脆正式一点,把上篇的名字都改掉,重定义下:

英文:Little Whale Lifelong Learning Platform

Logo:WhaleStudy

  • 1.构建验证用户登录的函数
  • 2.利用触发器自动维护历史记录
  • 3.使用存储过程处理重复数据问题
  • 4.巧用视图构建DIY数据

1.构建验证用户登录的函数

基础功能先搞简单些,我只需要实现不同用户登录系统,能且只能看到自己的学习进度,可以通过登录用户加以判断。

举实际例子吧,我这里先假设已有3个用户:duoduo、manman、test;

  • 多多小朋友 username=duoduo
  • 满满小朋友 username=manman
  • 测试人员兼管理员 username=test

要求用户登录只能看到自己的数据,管理员登录可以有权限看到一些额外的管理菜单和子项。

首先构建一张用户表,就三列,用户、密码、是否管理员:

--新建表 T_USERS
create table T_USERS (
username varchar2(30) primary key,
password varchar2(50) not null,
is_admin number default 0
);

然后创建一个函数,专门用于验证用户登录:

--新建函数 F_LOGIN
create or replace function F_LOGIN(
p_username in varchar2,
p_password in varchar2
) return boolean is
l_cnt number;
l_result boolean;
begin
--判断用户密码
select count(*) into l_cnt from t_users
where username = p_username
and password = p_password;
--判断
IF l_cnt=1 THEN
l_result := true;
--htp.p('Welcome,' || p_username);
ELSE
l_result := false;
--htp.p('Error!!!' || p_username);
END IF;
return l_result;
--异常处理
exception
WHEN OTHERS THEN
RETURN false;
end;
/

手工测试:

--ORACLE 23ai可以不用再写from dual,当然,写也不会报错,还能更好向下兼容,看个人习惯
--正确返回True
select f_login('test','test');
--错误返回False
select f_login('test','123');

在APEX界面,找到你的程序进入到验证方案,比如:Application 102 -> Shared Components -> Authentication Schemes,新建一个账号密码登录的验证方案,编辑时选择 Authentication Function Name 指定为上面创建的函数名。

不过登录测试还存在问题,因为我这里用户表中都是小写的用户名,但是网上搜索发现APEX登录界面会默认自动转为大写,这是因为账号大小写敏感设置问题。

这点参考了网上公开资料,可以这样修改,亲测有效,在登录页面Login部分插入一段PL/SQL Code:

apex_authentication.login(
p_username => :P9999_USERNAME,
p_password => :P9999_PASSWORD,
p_uppercase_username => FALSE);

再次测试使用小写账号登录成功。

OK,登录搞定,也学会了简单函数的使用。

关于只能看到自己的数据?

  • 可以通过配置APEX界面,数据库where条件为:username = :APP_USER,这里的:APP_USER就是当前登录的用户变量。

关于管理员登录可以有权限看到一些额外的管理菜单和子项?

  • 可以通过 Application 102 -> Shared Components -> Authorization Schemes配置一个管理权限,Scheme Type选择Exists SQL Query,SQL Query内容为:select 1 from t_users where username = :APP_USER and IS_ADMIN = 1,然后在导航菜单等地方就可以选择这个管理权限。

2.利用触发器自动维护历史记录

我这里设计一个稍复杂的场景,就是之前的T_CURRENT表,在进行更新修改都没有任何记录,无法分析历史数据,所以现在我就需要搞一张历史表专门用于存储历史信息,这里就通过新建一个触发器来实现这个功能,如下:

create or replace TRIGGER TRI_T_CURRENT
AFTER INSERT OR UPDATE OR DELETE ON "T_CURRENT"
FOR EACH ROW
DECLARE
v_current_date DATE := SYSDATE; -- 当前系统日期
BEGIN
IF INSERTING THEN
-- 插入操作,将插入的数据和当前日期插入到T_HISTORY表
INSERT INTO t_history (type, week, day, content, username, history_date)
VALUES (:new.type, :new.week, :new.day, :new.content, :new.username, v_current_date);
ELSIF UPDATING THEN
-- 更新操作,先尝试更新已有的记录,如果找不到,则插入新记录
-- 我这样设计,是因为想随时添加新的content内容,而不让历史表记录零碎信息
MERGE INTO t_history h
USING (SELECT :new.type AS type, :new.week AS week, :new.day AS day, :new.username AS username FROM dual) src
ON (h.type = src.type AND h.week = src.week AND h.day = src.day AND h.username = src.username)
WHEN MATCHED THEN
UPDATE SET h.content = :new.content, h.history_date = v_current_date
WHEN NOT MATCHED THEN
INSERT (type, week, day, content, username, history_date)
VALUES (:new.type, :new.week, :new.day, :new.content, :new.username, v_current_date);
ELSIF DELETING THEN
-- 删除操作,将被删除的数据和当前日期插入到T_HISTORY表
INSERT INTO t_history (type, week, day, content, username, history_date)
VALUES (:old.type, :old.week, :old.day, :old.content, :old.username, v_current_date);
END IF;
END;
/

其实,我主要用到的场景就是更新,只不过更新的要求稍多一点,因为我不想太多垃圾记录存在,详见上面代码注释部分说明。

3.使用存储过程处理重复数据问题

存储过程,用于做些啥呢,容笔者现编一下应用场景。。干脆就用于删除历史遗留的数据重复问题吧:

--新建存储过程 P_CLEAN_DUP_HISTORY
CREATE OR REPLACE PROCEDURE P_CLEAN_DUP_HISTORY
IS
BEGIN
DELETE FROM t_history h
WHERE h.history_date < (
SELECT MAX(h2.history_date)
FROM t_history h2
WHERE h.type = h2.type
AND h.week = h2.week
AND h.day = h2.day
AND h.username = h2.username
);
COMMIT;
END;
/

注意,我这里定义的重复数据,是根据我这个程序的业务场景来决定,我认为同一用户,在同一天(history_date),同一课程类型(type)、相同分片(week和day都一样),只能有一条,如果存在多条,一定是之前的记录CONTENT内容不完整,可以删除掉这样的垃圾条目,只保留最新的完整记录行。



额,自己说起来都感觉好绕,如果不理解可以多读几遍。。就是类似上面这种重复数据,还不理解也没关系,这里主要就是刷一下存储过程的存在感。

这样执行存储过程:

begin
P_CLEAN_DUP_HISTORY;
end;

之后再去查数据发现已实现这个去重数据的功能。

4.巧用视图构建DIY数据

那就搞一张视图来专门展示CONTENT中的内容吧!

create or replace view V_CONTENT as
select content from T_CURRENT
where content is not null;

嗯,看起来貌似有点没太大必要哈,是因为这个场景太简单了,我就是想展示一下可以这么玩,等以后遇到更复杂的场景就能看出巧用视图的优势了。

注意,细心的小伙伴应该已经发现了我这里的命名规范:

  • 所有表以T_开头
  • 所有函数以F_开头
  • 所有过程以P_开头
  • 所有视图以V_开头
  • 所有触发器以TRI_开头

    ...

职业病又犯了。。之前做DBA时还总是去给开发人员培训,让他们遵守一些开发规范,好利于排查维护。

当然你也可以构建你自己的一套命名规范,只要养成这个好习惯,以后的管理维护工作就会多一缕轻松愉快。

回到正题,是不是做过ORACLE DBA的小伙伴会觉得APEX这玩意儿简直是太好玩了,终于能比较容易的把一些内功给输出到前端了。

所以并不是做广告,笔者真觉得APEX这玩意儿,确实是蛮好玩儿的一个低码平台,请继续保持关注,后续还将有更多内容分享。

APEX实战第3篇:如何完善项目基础功能的更多相关文章

  1. R实战 第三篇:数据处理(基础)

    数据结构用于存储数据,不同的数据结构对应不同的操作方法,对应不同的分析目的,应选择合适的数据结构.在处理数据时,为了便于检查数据对象,可以通过函数attributes(x)来查看数据对象的属性,str ...

  2. Jemter+Badboy实战经验一(Badboy录制及基础功能)

    1. 使用工具: Apache Jemeter:http://jmeter.apache.org/download_jmeter.cgi (免费官网下载地址) BadBoy:   http://www ...

  3. Nancy简单实战之NancyMusicStore(三):完善商品信息与管理

    前言 上一篇,我们做了不少准备,并且还把我们NancyFx音乐商城的首页打造好了.这一篇主要是完善我们在首页的商品浏览问题和添加对商品的管理. 下面开始正题: 商品详情 首先是查看单个商品的详情: 先 ...

  4. Sping Boot入门到实战之入门篇(二):第一个Spring Boot应用

    该篇为Spring Boot入门到实战系列入门篇的第二篇.介绍创建Spring Boot应用的几种方法. Spring Boot应用可以通过如下三种方法创建: 通过 https://start.spr ...

  5. Sping Boot入门到实战之入门篇(四):Spring Boot自动化配置

    该篇为Sping Boot入门到实战系列入门篇的第四篇.介绍Spring Boot自动化配置的基本原理与实现.   Spring Boot之所以受开发者欢迎, 其中最重要的一个因素就是其自动化配置特性 ...

  6. SpringCloud实战 | 第五篇:SpringCloud整合OpenFeign实现微服务之间的调用

    一. 前言 微服务实战系列是基于开源微服务项目 有来商城youlai-mall 版本升级为背景来开展的,本篇则是讲述SpringCloud整合OpenFeign实现微服务之间的相互调用,有兴趣的朋友可 ...

  7. SpringCloud实战 | 第四篇:SpringCloud整合Gateway实现API网关

    一. 前言 微服务实战系列是基于开源微服务项目 有来商城youlai-mall 版本升级为背景来开展的,本篇则是讲述API网关使用Gateway替代Zuul,有兴趣的朋友可以进去给个star,非常感谢 ...

  8. [Spring Cloud实战 | 第六篇:Spring Cloud Gateway+Spring Security OAuth2+JWT实现微服务统一认证授权

    一. 前言 本篇实战案例基于 youlai-mall 项目.项目使用的是当前主流和最新版本的技术和解决方案,自己不会太多华丽的言辞去描述,只希望能勾起大家对编程的一点喜欢.所以有兴趣的朋友可以进入 g ...

  9. Spring Cloud实战 | 最八篇:Spring Cloud +Spring Security OAuth2+ Axios前后端分离模式下无感刷新实现JWT续期

    一. 前言 记得上一篇Spring Cloud的文章关于如何使JWT失效进行了理论结合代码实践的说明,想当然的以为那篇会是基于Spring Cloud统一认证架构系列的最终篇.但关于JWT另外还有一个 ...

  10. Docker实战 | 第四篇:Docker启用TLS加密解决暴露2375端口引发的安全漏洞,被黑掉三台云主机的教训总结

    一. 前言 在之前的文章中 IDEA集成Docker插件实现一键自动打包部署微服务项目,其中开放了服务器2375端口监听,此做法却引发出来一个安全问题,在上篇文章评论也有好心的童鞋提示,但自己心存侥幸 ...

随机推荐

  1. WPF 怎么利用behavior优雅的给一个Datagrid添加一个全选的功能

    前言:我在迁移旧项目代码的时候发现别人写很多界面都涉及到一个DataGrid的全选,但是每个都写的很混乱,现在刚好空闲下来,写一个博客, 给部分可能不太会写这个的同学讲一下,怎么实现全选功能,并且可以 ...

  2. 在shell脚本中为日志添加颜色

    在 Shell 脚本中,可以通过添加 ANSI 转义序列来为日志输出添加颜色.以下是一个完整的 Shell 脚本示例,包含日志颜色定义.日志函数封装以及使用示例: 完整脚本:colored_logs. ...

  3. runoob-PostgreSQL 教程

    https://www.runoob.com/postgresql/postgresql-tutorial.html

  4. 直播预览层(AVCaptureVideoPreviewLayer)底层实现

    分析sampleBuffer(帧数据) 通过设置AVCaptureVideoDataOutput的代理,就能获取捕获到一帧一帧数据 [videoOutput setSampleBufferDelega ...

  5. 学习shamir秘密分享

    介绍 1979年Shamir在下文提出基于拉格朗日插值多项式的\((r,n)\)秘密共享方案(\(0<r \leq n\)).秘密拥有者通过构建一元多项式将秘密分为\(n\)份,接收方收集大于等 ...

  6. 在离线环境使用nuget包

    原来程序集的引用 一个项目所有功能我们不可能都自己写对吧.这个时代 引用一大片的第三方包  项目源文件几百兆 ,有可能第三方包占了总体积99%.有可能我们自己写的代码不过几十行.想想我们原来的 老时代 ...

  7. android studio编译flutter项目

    1创建flutter项目:如下图 2选择 flutter application 3 出现flutter SDK没有发现:但是自己又是安装了的 如果,忘记自己flutter安装在哪里的同学. 可以先找 ...

  8. JavaScript数组(包括上一笔记都是ECMAScript对象),BOM对象,DOM对象,html DOM Enent(事件)

    JavaScript数组(包括上一笔记都是ECMAScript对象),BOM对象,DOM对象,html DOM Enent(事件) 1.Arrary; var ret = new Arrary(1,2 ...

  9. redis启停shell脚本

    启停脚本(redis-5.0.5) 一.编辑脚本 vim /u01/redis/redisServer.sh #!/bin/sh # # Simple Redis init.d script conc ...

  10. Java中的输出格式化

    在Java中,输出格式化是一个非常重要的功能,尤其是在需要精确控制输出格式的场景下. 以下是对代码中输出部分的详细解释: 原代码中的输出: System.out.printf("%.6f\n ...