背景概述

背景

数据库可谓计算机专业必修的一门课程,为了达到实践效果,推出这么一门实践设计的课程项目,涉及到前端、后端、数据库,以及它们之间的交互技术难题。

这些大多都需要项目成员自行调研,因此本项目是有一定工程量和难度的。值得庆幸的是,我的队友们都非常负责认真,也有很多大佬进行各种技术难题的突破,项目开展至今,是较为顺利的。

如今开发的第一大阶段即将过去,我试图整理一下本项目从开始到现在的各种技术成果,仅供参考。

由于本人进行后端开发,因此后端的技术问题覆盖可能更广一些。

技术分析

首先得有一台服务器,腾讯云或阿里云都可以,我们组采用腾讯云的一个2核2G的linux云服务器,使用新人优惠购买一年有效期可以将价格控制在100以内。

需要注意的是,linux的操作系统也存在不同的发行版,如Centos、Ubuntu、AliCloud等等。不同的类别会内置一些不同的安装工具包,如yum、apt、wget等等。

由于腾和阿里打折优惠的力度可能不同或时间上错开,可以博观而约取,选个最便宜的。

数据库部署

数据库版本采用Oracle12c,使用Docker部署

  1. 首先在服务器上安装好docker,oracle的环境配置极为复杂(本人在自己电脑上配了12c后卸载再装19c,可能注册表误删,导致整个系统重装了),使用docker容器是绝对的省力~,安装好了可以使用docker -v查看版本。

  2. 使用docker拉取oracle镜像

    • 搜索oracle镜像
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    # docker search oracle
    NAME DESCRIPTION STARS OFFICIAL AUTOMATED
    oraclelinux Official Docker builds of Oracle Linux. 903 [OK]
    oracleinanutshell/oracle-xe-11g 226
    wnameless/oracle-xe-11g-r2 Oracle Express Edition 11g Release 2 on Ubun… 84
    gvenzl/oracle-xe Oracle Database XE (21c, 18c, 11g) for every… 80
    truevoly/oracle-12c Copy of sath89/oracle-12c image (https://git… 45
    quillbuilduser/oracle-18-xe Oracle 18c XE Image for Quill Testing Purpos… 27
    oracledb19c/oracle.19.3.0-ee 13
    iamseth/oracledb_exporter A Prometheus exporter for Oracle modeled aft… 5
    oraclecoherence/coherence-ce Coherence Community Edition 4
    18fgsa/oracle-client Hosted version of the Oracle Container Image… 2
    kasmweb/oracle-8-desktop Oracle Linux 8 desktop for Kasm Workspaces 1
    dokken/oraclelinux-8 0
    bitnami/oraclelinux-extras Oracle Linux base images 0 [OK]
    dokken/oraclelinux-6 Oracle Linux 6 image for kitchen-dokken 0
    oracledemo1/hello-world Test docker build from github 0 [OK]
    kasmweb/oracle-7-desktop Oracle Linux 7 desktop for Kasm Workspaces 0
    oraclejmx/docker_jmx 0
    dokken/oraclelinux-7 Oracle Linux 7 image for kitchen-dokken 0
    kasmweb/core-oracle-7 Oracle Linux 7 base image for Kasm Workspace… 0
    kasmweb/core-oracle-8 Oracle Linux 8 base image for Kasm Workspace… 0
    bitnami/oraclelinux-extras-base 0
    oraclejp/weather-collector 0
    oraclejp/paasdocs-preview 0
    bitnami/oraclelinux-base-buildpack Oracle Linux bitnami-base-buildpack 0
    bitnami/oraclelinux-runtimes Oracle Linux runtime-optimized images 0 [OK]
    • 根据学长的经验我们选择了truevoly/oracle-12c镜像
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # docker pull truevoly/oracle-12c
    Using default tag: latest
    latest: Pulling from truevoly/oracle-12c
    863735b9fd15: Pull complete
    4fbaa2f403df: Pull complete
    44be94a95984: Pull complete
    a3ed95caeb02: Pull complete
    b8bc6e8767ee: Pull complete
    c918da326197: Pull complete
    448e1619a038: Pull complete
    faadd00cf98e: Downloading [========> ] 463MB/2.768GB
    197958b982b1: Download complete
    1d3ee67a57bb: Download complete
    • 由于服务器的带宽不同,下载速度可能也会不同,下载完毕后查看本地的镜像
    1
    2
    3
    # docker images
    REPOSITORY TAG IMAGE ID CREATED SIZE
    truevoly/oracle-12c latest 21789d4d876f 3 years ago 5.7GB
    • 运行镜像(关于docker的run命令的进一步解释)
    1
    2
    # docker run -d -p 1521:1521 --name oracle12c 21789d4d876f // 
    b5f0b283e506e0296a0987f92380a63ee970fe8417066b8ef00ed347ca7b2611

    oracle12c是取名,21789d4d876f是镜像的id,-d表示后台运行;-p表示端口映射格式为“主机端口:容器端口”,oracle的端口一般为1521,因此容器端口为1521,将主机端口设置为1521后,可以直接通过访问主机的1521端口来访问到容器内的1521端口;该命令返回运行容器的id

    • 查看运行中的容器
    1
    # docker ps
    • 进入容器内部,然后使用sqlplus命令进入数据库,超管账号(username=system,password=oracle,这个应该会根据不同的版本有所不同)
    1
    2
    # docker exec -it 容器id bash
    # sqlplus
    • 由于超管账号下有许多系统表,因此不能直接用来存放我们的表格。我们使用超管创建一个新的账号,并给予权限,用于存放数据库表格。
    1
    2
    # create user TEST_ACCOUNT identified by TEST_PASSWORD;
    # grant CONNECT, RESOURCE, DBA TO TEST_ACCOUNT;
  3. 使用图形化界面连接数据库

    关于数据库的图形化软件有很多,如sqldeveloper等,我选择了JetBrains的DataGrip,可以从官网下载,也可以使用scoophomebrew包管理工具直接下载(正版需在JetBrains官网上进行学生认证,要上传学生证)

    1
    2
    brew install datagrip // macos
    scoop install datagrip // windows

    安装完毕后,用图形化工具连接数据库,输入账号密码后点击“测试连接”,若测试成功则成功连接上

    image-20220725212454973

    有了图形化界面后,便可以将已经建好的表手动导入数据库之中,这将是一个繁琐的工作……

Back-End

本项目后端开发由我和szb同学共同完成,使用ASP.NET WebApi框架,版本5.0(目前已停止维护,.net6.0已发布)

这是我们的github账号链接:Gxyrious, cerebellumking

这是项目仓库:Back-End

ASP.NET项目的创建与初始化

  • 在Visual Studio中新建.net项目,如图所示
  • 这是项目初始化后的目录文件:

    截屏2022-07-25 21.55.12

    其中,Controllers文件夹放的是控制器,用于写具体的api函数;

    Program.cs和Startup.cs中存放了是项目的配置和启动操作,在Startup.cs中已经配置了swagger的api界面,直接点击运行就可以看到WeatherForecastController.cs中写的Get方法的api函数,其请求参数为空,响应参数为一个Array,如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    [HttpGet]
    public IEnumerable<WeatherForecast> Get()
    {
    var rng = new Random();
    return Enumerable.Range(1, 5).Select(index => new WeatherForecast
    {
    Date = DateTime.Now.AddDays(index),
    TemperatureC = rng.Next(-20, 55),
    Summary = Summaries[rng.Next(Summaries.Length)]
    })
    .ToArray();
    }

    而WeatherForecast.cs则是作为一个Model,定义了WeatherForecast类,和Controller同属于一个namespace之下,因此可以直接使用该Model

  • 使用ORM(对象关系映射)将数据库中的表格映射为项目中的类

    首先下载一些程序包:首先点击,工具-NuGet包管理器-管理解决方案的NuGet程序包中,

    image-20220726173637946

    下载如下几个工具包,如下图所示,由于使用的.net版本为5.0,故我们选择的包都是5.0的最后一个版本

    • Microsoft.EntityFrameworkCore.Design
    • Microsoft.EntityFrameworkCore.Tools
    • Oracle.EntityFrameworkCore
    image-20220726174251260

    我们使用Microsoft.EntityFrameworkCore中的DbContext类。当我们在某个控制器的api接口中想对数据库进行增删改查时,可以直接创建一个ModelContext实例,通过该实例的一系列成员函数来与数据库会话,从而封装了sql查询语句。

    首先进入“工具-NuGet包管理器-程序包管理器控制台”中

    image-20220726230618113

    然后在命令行中输入如下命令(将*替换为自己的服务器ip和数据库账号密码)

    1
    Scaffold-DbContext "Data Source=**.**.**.**/xe;Password=******;User ID=***;" Oracle.EntityFrameworkCore -OutputDir Models -context ModelContext -Force

    这时候我们就会发现在目录中多了一个Models文件夹,其中将该db用户下的所有表格都生成了一个.cs文件,以及还有一个ModelContext.cs文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public partial class ModelContext : DbContext
    {
    public ModelContext()
    {
    }

    public ModelContext(DbContextOptions<ModelContext> options)
    : base(options)
    {
    }

    // 此处省略很多行......
    }

    这时我们就已经成功将数据库映射为.Net中的类和对象了。

  • 创建Controller

    • 右击Controller文件,添加控制器,以最简单的LoginController为例
    image-20220726233738680

    在原来的基础上添加如下内容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    using ASP.NET.Core.Test.Models; // 使用Models命名空间

    namespace ASP.NET.Core.Test.Controllers
    {
    [ApiController]
    [Route("[controller]")]
    public class LoginController : ControllerBase
    {
    private readonly ModelContext myContext; // 创建一个模型上下文对象

    // 构造函数,在每次调用该Controller中的api函数时,都会创建一个context对象用于操作数据库
    public LoginController(ModelContext modelContext)
    {
    myContext = modelContext;
    }

    // 在此处敲api函数
    }
    }

    当然,为了让api调用时能够传入一个ModelContext对象,我们需要在startup.cs的ConfigureServices函数中增加一行代码services.AddScoped<ModelContext>()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    using ASP.NET.Core.Test.Models; // 使用Models

    public void ConfigureServices(IServiceCollection services)
    {
    services.AddControllers();
    services.AddScoped<ModelContext>(); // 这行代码让每次调用api时都自动传入一个context
    services.AddSwaggerGen(c =>
    {
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "ASP.NET.Core.Test", Version = "v1" });
    });
    }

    接下来在LoginController.cs中添加登录api函数,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    [HttpPost]
    public string login(string user_phone,string password)
    {
    Message message = new();
    try
    {
    var user_info = myContext.Users
    .Where(user => user.UserPhone == user_phone && user.UserPassword == password)
    .Select(user => new
    {
    user.UserId,
    user.UserName,
    }).First();
    message.Add("userid", user_info.UserId);
    message.Add("username", user_info.UserName);
    message.Add("errorCode", 200);
    }
    catch(Exception error)
    {
    Console.WriteLine(error.ToString());
    }

    return message.ReturnJson();
    }

未完待续……