文章目录

  • 1.mysql安装:存储:集合(内存:临时),IO流(硬盘:持久化)
    • 1.1 服务端:双击mysql-installer-community-5.6.22.0.msi
    • 1.2 客户端:命令行输入mysql -u...实际是如下安装路径的bin文件夹的mysql.exe
  • 2.DDL/DML:char不用动态变化,效率高
  • 3.DQL:select(相当于System.out)第一个但在倒数第二执行
  • 4.约束和自增长:字段约束(字段的赋值规范)/主键约束(唯一非空且一张表只有一个主键)
  • 5.单表缺点和外键约束:多从外 引出
  • 6.三种多表关系:中间表两外键:一对一(简历),一对多(员工,部门),多对多(学生,课表)
  • 7.多表查询:表..join表on
    • 7.1 外连接:左外=左表+内连接
  • 8.子查询:优先于主查询产生结果
  • 9.事务:一组操作,要么同时成功,要么同时失败
  • 10.四个接口:左java.sql包下的DCSR,右com.mysql.jdbc.Driver
  • 11.释放资源:finally,工具类封装
  • 12.JDBC事务操作:conn.setAutoCommit(false)
  • 13.登陆案例预编译改造:PreparedStatement,setString,executeQuery
  • 14.c3p0连接池:jdbc2.0才引进连接池,不是线程池(连接池的技术标准就是DataSource替代DriverManager)
  • 15.druid连接池:自动ds.set
  • 16.execute/update方法:template =
  • 17.queryForXX/query方法:Map.Entry < String, Object > ,Map < String, Object > 两个数据类型
  • 18.redis安装:加载.conf
  • 19.操作:set/get,push/pop,add/rem
  • 20.持久化策略:存文件,.aof和.rdb文件删了,数据库里数据全没了
  • 21.Jedis:java程序连接redis,拿到jedis
  • 22.案例_好友列表:json = om.
    • 22.1 前端:index.html
    • 22.2 web:FriendServlet .java
    • 22.3 service:FriendService.java
    • 22.4 dao:FriendDao.java
    • 22.5 bean:Friend.java
  • 23.总结:存储过程(PL/SQL代码集,像没有返回值的自定义函数)和函数需要用户显示调用才执行,而触发器是由一个事件来触发运行,当某个事件发生时会自动地隐式运行,不能被显示的调用。sql多用group by
    • 23.1 数据库优化:项目管理PMP证书,数据库DBA/OCM证书
    • 23.2 数据库备份和恢复:冷热备份
  • 24.生成数据:crontab
  • 25.ftp:ftp是tcp/ip协议族中一员,分客户端和服务端
    • 25.1 安装:linux操作系统的用户也是ftp的用户,可以配置专用的ftp用户,专用的ftp用户只能用于ftp,不能登录操作系统
    • 25.2 ftp命令:get,put
      • 登录ftp服务器:linux命令行输入:ftp ip地址,然后输入用户名和密码(adduser ,passwd ),必须先开启ftp服务
      • 查看/下载/上传文件:ftp的传输模式分ASCII码和二进制两种,二进制可传输任何文件,而ASCII码方式只能传输.txt、.html,实际开发中,不管什么文件都用二进制传输文件省事
    • 25.3 ftp应用:OpenForRename,Fgets
      • 数据完整性:在一文件夹里 ls | wc 统计数据行数
      • C语言实现ftp下载文件:.a就是静态库相当.c/.cpp一个别名,编译时不让人看见,makefile中libftp.a就是ftplib.c。
  • 26.oracle安装:swap,dd
    • 26.1 创建swap交换区:grep MemTotal /proc/meminfo (安装Oracle物理内存要求1024MB以上),grep SwapTotal /proc/meminfo
    • 26.2 安装依赖包及改系统核心参数:关闭一些系统对数据库的限制,pam.d,inittab
    • 26.3 创建oracle用户和组:.bash_profile。groupadd dba:增加dba组,相同等级的用户放一起就是一个组,只要对组设权限就行。groups 用户名:查看用户所在组。userdel(-r)用户名:删除用户或删除home文件夹里用户名文件夹
    • 26.4 数据库的启动和关闭:oracle用户登录,lsnrctl start/stop/status 启动/关闭网络监听服务,dbstart/dbshut 启动/关闭数据库
    • 26.5 sqlplus命令行登录数据库:用oracle用户登录,执行sqlplus scott/tiger,以scott用户的身份登录
    • 26.6 plsql客户端登录数据库:远程连接需打开监听lsnrctl start
  • 27.C语言操作oracle:trim,rc,库/头文件
    • 27.1 makefile:-Wno-unused-variable变量未使用关闭警告(如int cc;下面未使用cc变量)
    • 27.2 createtable.cpp:setenv/putenv设置环境变量,char补空格,varchar不补空格。确定长度用char快,varchar2(4000)用到几个字符自动分配几个而不是一下给4000字符
    • 27.3 inserttable.cpp:truncate删除表中全部数据,不产生事务
    • 27.4 selecttable.cpp:stmt.m_cda.rpc变量保存了SQL执行后影响的记录数
    • 27.5 updatetable.cpp:连接数据库,返回值0成功,其它失败,失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中
    • 27.6 deletetable.cpp:删除商品表中数据
    • 27.7 execplsql.cpp:执行一个PL/SQL过程
  • 28.虚表/日期/序列:ntp&hwclock
  • 29.索引/视图/链路/同义词:create index/view/link/synonym,distinct
  • 30.系统高可用性:12个1T组合,rac
  • 31.txt/xml文件入表:结构体内容
  • 32.站点参数建表入表/PowerDesigner/主外键:多表查询
  • 33.数据交换:数据导出为文件,数据文件推送
  • 34.非结构化数据存储:blob,pzhrain24file
  • 35.磁盘/cpu信息收集:内存信息free -m,和top命令查看的内存是一样的,也在系统文件/proc/meminfo

1.mysql安装:存储:集合(内存:临时),IO流(硬盘:持久化)

1.1 服务端:双击mysql-installer-community-5.6.22.0.msi

mysql安装包:链接:https://pan.baidu.com/s/18Ctus6BLVrECZP0W-QKtfw 提取码:94s9。




上面next,execute安装后,下面开始配置。



1.2 客户端:命令行输入mysql -u…实际是如下安装路径的bin文件夹的mysql.exe

命令行java -version要配环境变量,看mysql有没有运行,只能从任务管理器看有没有mysql的进程。

win下cmd输入:完整版没有括号:mysql -h(host) 127.0.0.1 -P(port) 3306 -u(username) root -p(password)。简略版:mysql -u root -p。

可视化工具navicat链接:https://pan.baidu.com/s/11qAEA7yEGSIz6J9GIC_S6A 提取码:1w9n。navicat免安装链接:https://pan.baidu.com/s/1zanhYyyNsUXajI-czfW7KA 提取码:om5g。

2.DDL/DML:char不用动态变化,效率高

如下中间蓝框里一个数据库相当于一个excel表,紫色框里一个table相当于excel表里的一个sheet。use数据库相当于双击打开excel表格,select查看函数返回值,desc全称description,like复制表结构,change 旧字段 新字段 新类型

数据库名字不建议修改,用数据库备份改名(新建一个数据库,把原数据库数据复制过来)。mysql中utf-8中-无法识别,用utf8。ISO-8859-1用latin1。

注意(),逗号,分号。

如下数(整小)日字,oracle两个不同:number包含整小,varchar2。

选用timestamp而不用datetime,因为datetime不会自动设置时间。

int(11)默认11位不用写出来,2147483647即21亿10位,如果有负数还有最前面一位是符号位,所以一共11位

如下是表记录,select非常多,删除只是记录,表结构还在。


DDL后面不需要加from等词。

如下单引号可加可不加。


如下要写两个add。





3.DQL:select(相当于System.out)第一个但在倒数第二执行

如下3中第一排序字段相同的里面再进行第二排序。




如下查询id是1或3或5的学生。



d低下降,只要有数字都比null大。



如下有英语成绩有6个,不能代表总人数,缺考没算入。


select后的第一个字段要和group by后同。



如下是select语句执行顺序,红字书写顺序必须这样。

sql只有一种情况报错:语法写错。如下索引越界不会报错。

page和count是前端传来的两个参数,count不用变。

数量(count(*))降序只要一个(limit 1)。

如下将db3数据库整个连数据备份,本质create,insert。记住mysqldump和source。

4.约束和自增长:字段约束(字段的赋值规范)/主键约束(唯一非空且一张表只有一个主键)



如下验证主键唯一和非空,如下两个框都报错。








如下的Null列表示是否允许为空。

5.单表缺点和外键约束:多从外 引出

多表是外键约束的前提,外键约束解决下面多表的2个问题,add…多了一条线。alter table emploee drop foreign key fk_001。

如上是在已有表添加外键,如下建表就添加(开发中常用)。


如果要删除department表的id为001即整个研发部门,需要先在employee表中删除id=001的很多记录,很麻烦。现在想要删除department的001部门时employee为001的行也一起删了,所以用外键级联操作,如下create table …中省略同上。

6.三种多表关系:中间表两外键:一对一(简历),一对多(员工,部门),多对多(学生,课表)





7.多表查询:表…join表on

CREATE DATABASE day03;
USE day03;
-- 创建部门表
CREATE TABLE dept (id INT PRIMARY KEY AUTO_INCREMENT,NAME VARCHAR(20)
);
INSERT INTO dept (NAME) VALUES ('开发部'),('市场部'),('财务部');
-- 创建员工表
CREATE TABLE emp ( id INT PRIMARY KEY AUTO_INCREMENT,NAME VARCHAR(10),gender CHAR(1),   -- 性别salary DOUBLE,   -- 工资join_date DATE,  -- 入职日期dept_id INT
);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('孙悟空','男',7200,'2013-02-24',1);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('猪八戒','男',3600,'2010-12-02',2);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('唐僧','男',9000,'2008-08-08',2);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('白骨精','女',5000,'2015-10-07',3);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('蜘蛛精','女',4500,'2011-03-14',1);

如下要查出有用的记录需要emp.dept_id(emp外键)=dept.id(dept主键)。



因为select * ,所以将两个橙色横框拼接起来,棕色框。

7.1 外连接:左外=左表+内连接

如下注意记住左表不动。

如下第一个内连接没有销售部(销售部没有和员工表任何数据相交),第二个有销售部。



union all不去重,左外连接+右外连接。

8.子查询:优先于主查询产生结果


如下执行两句sql,要连接两次数据库,子查询一般要写在()里。


如下1,2是dept_id,可以把in换成 = any

如下第一行和第二行效果相同。

如下两个select等价。

如下还是写上面的题目,如果用内连接,沙僧会没有,左外或右外连接都可以。

如下将上面的id1列去除(即将dept表的id列去除不显示)。

on比where先执行。

-- 11111111111111111111111111111111111111111111111111111111111111111部门表
CREATE TABLE dept (id INT PRIMARY KEY PRIMARY KEY, -- 部门iddname VARCHAR(50), -- 部门名称loc VARCHAR(50) -- 部门位置
);
-- 添加4个部门
INSERT INTO dept(id,dname,loc) VALUES
(10,'教研部','北京'),
(20,'学工部','上海'),
(30,'销售部','广州'),
(40,'财务部','深圳');-- 1111111111111111111111111111111111111111111111111111111职务表,职务名称,职务描述
CREATE TABLE job (id INT PRIMARY KEY,jname VARCHAR(20),description VARCHAR(50)
);
-- 添加4个职务
INSERT INTO job (id, jname, description) VALUES
(1, '董事长', '管理整个公司,接单'),
(2, '经理', '管理部门员工'),
(3, '销售员', '向客人推销产品'),
(4, '文员', '使用办公软件');-- 11111111111111111111111111111111111111111111111111111111111111111111员工表
CREATE TABLE emp (id INT PRIMARY KEY, -- 员工idename VARCHAR(50), -- 员工姓名job_id INT, -- 职务idmgr INT , -- 上级领导joindate DATE, -- 入职日期salary DECIMAL(7,2), -- 工资bonus DECIMAL(7,2), -- 奖金dept_id INT, -- 所在部门编号CONSTRAINT emp_jobid_ref_job_id_fk FOREIGN KEY (job_id) REFERENCES job (id),CONSTRAINT emp_deptid_ref_dept_id_fk FOREIGN KEY (dept_id) REFERENCES dept (id)
);
-- 添加员工
INSERT INTO emp(id,ename,job_id,mgr,joindate,salary,bonus,dept_id) VALUES
(1001,'孙悟空',4,1004,'2000-12-17','8000.00',NULL,20),
(1002,'卢俊义',3,1006,'2001-02-20','16000.00','3000.00',30),
(1003,'林冲',3,1006,'2001-02-22','12500.00','5000.00',30),
(1004,'唐僧',2,1009,'2001-04-02','29750.00',NULL,20),
(1005,'李逵',4,1006,'2001-09-28','12500.00','14000.00',30),
(1006,'宋江',2,1009,'2001-05-01','28500.00',NULL,30),
(1007,'刘备',2,1009,'2001-09-01','24500.00',NULL,10),
(1008,'猪八戒',4,1004,'2007-04-19','30000.00',NULL,20),
(1009,'罗贯中',1,NULL,'2001-11-17','50000.00',NULL,10),
(1010,'吴用',3,1006,'2001-09-08','15000.00','0.00',30),
(1011,'沙僧',4,1004,'2007-05-23','11000.00',NULL,20),
(1012,'李逵',4,1006,'2001-12-03','9500.00',NULL,30),
(1013,'小白龙',4,1004,'2001-12-03','30000.00',NULL,20),
(1014,'关羽',4,1007,'2002-01-23','13000.00',NULL,10);-- 1111111111111111111111111111111111111111111111111111111111111111工资等级表
CREATE TABLE salarygrade (grade INT PRIMARY KEY,losalary INT,hisalary INT
);
-- 添加5个工资等级
INSERT INTO salarygrade(grade,losalary,hisalary) VALUES
(1,7000,12000),
(2,12010,14000),
(3,14010,20000),
(4,20010,30000),
(5,30010,99990);




9.事务:一组操作,要么同时成功,要么同时失败






如下若在执行一次sql前执行一次start transaction,若不commit,刷新数据库不显示。

10.四个接口:左java.sql包下的DCSR,右com.mysql.jdbc.Driver



如下mchange. .和c3p0. .一起。第一个导入的是mysql-connector…。

如下代码第一行new Driver()是导入com.mysql.jdbc(用mysql实现好的,就是上面导入的mysql-connector..jar包)而不是java.sql(自己不会重写抽象方法)下,参数是接口类型需要传入接口的实现类对象即new Driver()。registerDriver相当于set方法,get获取的是mysql.Driver.connect方法返回的Connection类即com.mysql.jdbc.JDBC4Connection(有mysql.的都是导入的jar包)。

加载DriverManager这个类用到打破双亲类加载:DriverManager是jdk自带的类,DriverManager类使用的是bootstrap引用类加载器。数据库是用户类用bootstrap加载不合适,所以DriverManager去加载h2的Driver需要把当前引用类加载器替换为当前系统或当前线程的应用app类加载器

package com.itheima01.jdbc;
import com.mysql.jdbc.Driver;
import java.sql.*;public class JdbcDemo { public static void main(String[] args) throws SQLException, ClassNotFoundException, IllegalAccessException, InstantiationException {//1. 注册驱动 (注意: 导入的mysql的驱动)/**   A. 查看mysql.Driver源码: 点new Driver()中Driver看源码*       发现static 代码块里 已经注册了驱动 -> 驱动自注册 相当于set一次就行*       带来问题: 外部的注册没有意义(重复注册)* *       解决: 保证mysql.Driver类被加载(静态代码块就会执行),如下两种方案:*          1. 创建对象*          2. 反射:a. 节省内存*                  b. 跟驱动包的关联只剩一个字符串:"com.mysql.jdbc.Driver"*          待会将字符串写入配置文件,只要改配置文件就行 就会跟 mysql驱动包的彻底解耦 *///111111111111111111111111111111111111111111111111111111111111111111111111111111111111
// DriverManager.registerDriver(new Driver()); //这行重复注册,下行new了就会加载 源码里的静态代码块,所以这行=下行//new Driver(); // Class对象 + 普通实例 //只要用了这个类,这个类就会被加载内存中方法区 //自动导包// new Driver()的内存消耗等价于下面两行: 其实只需要calss对象,不需要实例                  // Class<?> clazz = Class.forName("com.mysql.jdbc.Driver"); //获取这个类的class对象// Object obj = clazz.newInstance(); //利用class对象调用其空参构造来创建一个实例Class.forName("com.mysql.jdbc.Driver"); //获取Class对象,没有普通实例,因为普通实例没有意义 //用反射,com.mysql.jdbc.Driver这个类也会被加载//111111111111111111111111111111111111111111111111111111111111111111111111111111111111//2. 获取连接/**   只要涉及两个软件通信 : 网络三要素(必要非充分:一定要,但是有它们三不一定够如多了资源位置)*       1. 协议 :       jdbc:mysql  (主协议:子协议)*       2. ip :         数据库所在的计算机(自己:localhost或127.0.0.1)*       3. port :       mysql数据库3306*       资源位置:   数据仓库的名称*       *       协议://ip:port/资源位置*       https://www.baidu.com:443*/
//        String url = "jdbc:mysql://localhost:3306/day03";  //day03是数据库String url = "jdbc:mysql:///day03"; //ip:localhost port:3306 可以省略String user = "root";String pwd = "1234";Connection conn = DriverManager.getConnection(url, user, pwd);System.out.println("conn:" + conn); //引用类型打印地址//1111111111111111111111111111111111111111111111111111111111111111111111111111111111//3. 创建执行sql的语句对象/** Connection 接口的一些方法*   <1>. Statement createStatement();   创建执行sql的语句对象,相当于创建一个流*   <2>. PreparedStatement prepareStatement(sql); 创建一个预编译sql的语句对象*   <3>. 事务操作相关*/Statement statement = conn.createStatement();System.out.println("statement:" + statement);//11111111111111111111111111111111111111111111111111111111111111111111111111111111111//4. 执行sql,返回结果/** Statetment 接口的api*   1. ResultSet statement.executeQuery(sql);*           执行的查询语句 : DQL*           返回的查询结果: 结果集* *   2. int executeUpdate(sql) :*           执行的增删改语句: DML*           返回的结果: 被影响的行数* *   3. boolean execute(sql);  -> 不需要掌握,知道即可*               万能 :  DDL等 如create成功或失败是和异常相关,和返回值无关*               返回值: 非查询语句返回false,查询语句返回true*/String sql = "select * from emp";ResultSet resultSet = statement.executeQuery(sql);//11111111111111111111111111111111111111111111111111111111111111111111111111111111111 //5. 处理结果while(resultSet.next()){String name = resultSet.getString("name");
// String id = resultSet.getString("id"); //也可以,java程序以外的所有数据对java来说都是字符串int id = resultSet.getInt("id"); //底层先调用getString再parse intSystem.out.println(id+ ":" + name);}//11111111111111111111111111111111111111111111111111111111111111111111111111111111111//6. 释放资源resultSet.close();statement.close();conn.close();}
}


resultset不是返回一行数据,而是带有id=…。因为返回一行数据如 11孙悟空男,不知道怎么解析。

如下hasNext和next区别。

11.释放资源:finally,工具类封装

package com.itheima04.release;
import com.itheima05.utils.JdbcUtil;
import java.io.Closeable;
import java.io.IOException;
import java.sql.*;public class ReleaseDemo {public static void main(String[] args)  {ResultSet resultSet = null;Statement statement = null;Connection conn = null;try {/*Class.forName("com.mysql.jdbc.Driver");String url = "jdbc:mysql://localhost:3306/day03";String user = "root";String pwd = "1234";conn = DriverManager.getConnection(url, user, pwd);*/conn = JdbcUtil.getConnection(); //查询要获取连接,getConnection方法调用多遍,所以getConnection方法不写try catch,查询提示 查询失败,删除提示 删除失败,封装时不知道是查询还是删除,不好提示,所以往外抛。conn = JdbcUtil.getConnection(); //增删改也要获取连接//1111111111111111111111111111111111111111111111111111111111111111111111111            statement = conn.createStatement();String sql = "select * from emp"; resultSet = statement.executeQuery(sql);            while(resultSet.next()){String name = resultSet.getString("name");System.out.println(name);}} catch (Exception e) {e.printStackTrace();} finally{/* if(resultSet != null){try {resultSet.close();} catch (SQLException e) {e.printStackTrace();}}if(statement != null){try {statement.close();} catch (SQLException e) {e.printStackTrace();}}if(conn != null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}}*/// closeIo(resultSet,statement,conn);JdbcUtil.release(conn,statement,resultSet);}}//1111111111111111111111111111111111111111111111111111111111111111111111private static void closeIo(AutoCloseable... ios) { //AutoCloseable接口位于java.lang包下,不用导包for (AutoCloseable io : ios) {if(io != null){try {io.close();} catch (Exception e) {e.printStackTrace();}}}}
}
package com.itheima05.utils;
import java.sql.*;
/**
*   工具类:  0. 拥有很多 工具方法(重复的代码封装) 的类
*               命名规范: utils 包 -> xxUtil 类 (xx : 某个模块的名称)
*               Objects,Arrays,Collections...(JDK提供的)
*
*          1. 一般工具类中方法是静态的,不用实例化,节省内存
*
*          2. 封装方法的步骤
*             1. 先把要把封装的代码写出来
*             2. 观察不断重复的部分
*             3. 定义方法,然后直接复制过来
*             4. 设置参数和返回值
*
*       注意点: 1. 扩展性 : 不要导入mysql包中的类, 要导入java包中的类(这样换成oracle也可用)
*               2. 工具类中的异常一般是往外抛 : 一般异常是要在业务中处理
*/
public class JdbcUtil {static{try {Class.forName("com.mysql.jdbc.Driver");} catch (ClassNotFoundException e) {e.printStackTrace();}}//1111111111111111111111111111111111111111111111111111111111111111111111111111111public static Connection getConnection() throws SQLException {//此方法会被多次调用,注册驱动只需要一次 -> 所以用静态代码块 如上
//        Class.forName("com.mysql.jdbc.Driver");String url = "jdbc:mysql://localhost:3306/day03";String user = "root";String pwd = "1234";Connection conn = DriverManager.getConnection(url, user, pwd);return conn;}//1111111111111111111111111111111111111111111111111111111111111111111111111111111/*** 文档注释: a. 写在类上面 : 描述类的用途*          b. 写在方法上面 : 描述方法的用途 (返回值,参数)*/public static void release(Connection conn, Statement statement, ResultSet resultSet){//java.sql.Connectionif(resultSet != null){try {resultSet.close();} catch (SQLException e) {e.printStackTrace();}}if(statement != null){try {statement.close();} catch (SQLException e) {e.printStackTrace();}}if(conn != null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}
}

如下改进上面工具类。

/*
*   问题: 驱动,url,用户名和密码等信息 是有可能会变的, 变动频率比较低
*       1. 如果不变,直接写死在代码中
*       2. 变,但是频率高 : 一般设置成参数
*       3. 变,但是频率不高: 放在配置文件
*           1. 解耦 : 信息要改变的话,只要改配置文件,代码不用改,程序不需要重新编译和部署
*           2. 代码简化 : 无需调用的时候传参了
*/
//jdbc.properties文件,每个月改一次 //文件里没有关键字,也没有双引号,本来就是字符串
driverName = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/day03
user = root
pwd = 1234
package com.itheima05.utils;
import java.io.FileInputStream;
import java.sql.*;
import java.util.Properties;public class JdbcUtil02 {static String driverName;static String url;static String user;static String pwd;static{try {Properties p = new Properties();p.load(new FileInputStream("src/jdbc.properties"));driverName = p.getProperty("driverName");url = p.getProperty("url");user = p.getProperty("user");pwd = p.getProperty("pwd");Class.forName(driverName);} catch (Exception e) {e.printStackTrace();}}   //1111111111111111111111111111111111111111111111111111111111111111111111111111111111public static Connection getConnection() throws SQLException {Connection conn = DriverManager.getConnection(url, user, pwd);return conn;}//11111111111111111111111111111111111111111111111111111111111111111111111111111111111 public static void release(Connection conn, Statement statement, ResultSet resultSet){if(resultSet != null){try {resultSet.close();} catch (SQLException e) {e.printStackTrace();}}if(statement != null){try {statement.close();} catch (SQLException e) {e.printStackTrace();}}if(conn != null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}
}

12.JDBC事务操作:conn.setAutoCommit(false)

package com.itheima01.transaction;
import com.itheima.utils.JdbcUtil;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
// JDBC : 写一个转账案例
public class Demo {public static void main(String[] args)  {       System.out.println("请输入转出的账户:"); Scanner sc = new Scanner(System.in); // 控制台: 模拟页面String outUser = sc.nextLine();System.out.println("请输入转入的账户:");String inUser = sc.nextLine();   System.out.println("请输入转账的金额:");double money = sc.nextDouble();//sql里面最好写单引号 , 1000和jack改为 两个双引和两个+号//String sql1 = "update account set money = money-1000 where name = 'jack'";String sql1 = "update account set money = money-"+money+" where name = '"+outUser+"'";String sql2 = "update account set money = money+"+money+" where name = '"+inUser+"'";//11111111111111111111111111111111111111111111111111111111111111111111111111111111111111Connection conn = null;try {/**   事务操作: Connection*       1. setAutoCommit(false); 开启事务*       2. commit(); 提交事务*       3. rollback(); 事务回滚*   注意点: 在事务中, 开启事务的连接才具有手动提交事务的功能*           一组操作都必须要同一个 连接conn  要执行*/            conn = JdbcUtil.getConnection(); //访问数据库,try外面定义conn// Connection conn2 = JdbcUtil.getConnection();                    conn.setAutoCommit(false); //开启事务,禁止自动提交  Statement statement = conn.createStatement(); statement.executeUpdate(sql1);   //转出                      // int i = 1/0;  // ArithmeticException 算术异常 模拟银行爆炸                Statement statement2 = conn.createStatement(); statement2.executeUpdate(sql2);   //转入//1111111111111111111111111111111111111111111111111111111111111111111111111111111111111                            conn.commit(); //提交事务(和事务回滚只有其一执行) System.out.println("转账成功~~");} catch (Exception e) { // 注意: 提升异常级别(用于捕获算术异常)e.printStackTrace();if(conn != null){ //Connection conn放外面,这边访问的到try {conn.rollback(); //事务回滚} catch (SQLException e1) {e1.printStackTrace();}}System.out.println("转账失败");}}
}


13.登陆案例预编译改造:PreparedStatement,setString,executeQuery

package com.itheima02.login;
import com.itheima.utils.JdbcUtil;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
/*前提: 用户能够登录,说明已经注册过了注册成功的时候, 程序会把用户的信息保存到数据库*   登录案例: 逻辑: 请输入用户名和密码(用户)
*            我们: 校验数据库
*            sql :  select * from account where name = ? and pwd = ?; (name用户名唯一)
*           预测结果:  1. 0条 : 用户名不存在或密码错误
*                     2. 1条 : 登录成功
*/
public class LoginDemo {public static void main(String[] args) throws SQLException {System.out.println("请输入用户名:");Scanner sc = new Scanner(System.in);String name = sc.nextLine();  System.out.println("请输入密码:");String pwd = sc.nextLine();String sql = "select * from account where name = '"+name+"' and pwd = '"+pwd+"'";System.out.println(sql); //将参数(上面键盘输入)直接拼接进sql//1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111Connection conn = JdbcUtil.getConnection();Statement statement = conn.createStatement();           ResultSet resultSet = statement.executeQuery(sql); //将sql发送给数据库去处理if(resultSet.next()){ //有一条则为trueSystem.out.println("登录成功~~");}else{System.out.println("用户名不存在或密码错误");}}
}

根据(用户名和密码)或(1=1永真)为条件查询数据库,what可以随便写。

预编译知道了sql的语法结构了,已经把关键字认全了,后面再包含关键字or就不认了(当成字符串处理),可以防止sql注入

package com.itheima02.login;
import com.itheima.utils.JdbcUtil;
import java.sql.*;
import java.util.Scanner;public class LoginDemo02 {public static void main(String[] args) throws SQLException {System.out.println("请输入用户名:");Scanner sc = new Scanner(System.in);String name = sc.nextLine();  System.out.println("请输入密码:");String pwd = sc.nextLine();// 1. 改造sql       String sql = "select * from account where name = ? and pwd = ?";System.out.println(sql);Connection conn = JdbcUtil.getConnection();// 2. 预编译sql   // PreparedStatement 是 Statement的子接口PreparedStatement statement = conn.prepareStatement(sql);// 3. 设置参数// setString(int parameterIndex, String x)// parameterIndex : sql中的?的索引(从1开始,从左开始)  // String x: 参数statement.setString(1,name);statement.setString(2,pwd);// 4. 传参执行ResultSet resultSet = statement.executeQuery();if(resultSet.next()){System.out.println("登录成功~~");}else{System.out.println("用户名不存在或密码错误");}}
}


如下手动在数据库中增加一行。

package com.itheima03.prepare;
import com.itheima.utils.JdbcUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
/*
*   预编译的好处:  1. 防止sql注入
*                 2. 阅读性强(sql)
*                 3. 批量处理sql,效率高 (节省了sql重复编译过程)
*/
public class PrepareDemo {public static void main(String[] args) throws SQLException {//        method01(); //用预编译,如下不用预编译String name = "ww";int money = 200;String sql1 = "insert into account values(null,'"+name+"',"+money+",null)";String name2 = "www";int money2 = 2002;String sql2 = "insert into account values(null,'"+name2+"',"+money2+",null)";     Connection conn = JdbcUtil.getConnection();Statement statement = conn.createStatement(); //不用预编译statement.executeUpdate(sql1);// 编译+运行statement.executeUpdate(sql2);//编译+运行,和上行一共两次编译        statement.close();conn.close();}//1111111111111111111111111111111111111111111111111111111111111111111111111private static void method01() throws SQLException {String sql = "insert into account values(null,?,?,null)";        Connection conn = JdbcUtil.getConnection();PreparedStatement pstm = conn.prepareStatement(sql);   pstm.setString(1,"zs");pstm.setDouble(2,1000);pstm.executeUpdate();//运行pstm.setString(1,"ls");pstm.setDouble(2,2000);pstm.executeUpdate();//运行        pstm.close();conn.close();}
}

14.c3p0连接池:jdbc2.0才引进连接池,不是线程池(连接池的技术标准就是DataSource替代DriverManager)

package com.itheima04.c3p0;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;public class C3p0Demo {  public static void main(String[] args) throws PropertyVetoException, SQLException {//        method01(); //这行方法和下面xml文件无关/** 配置文件方式 :   默认情况下, c3p0将会在 类加载器路径(在当前的工程下, src路径)下*     寻找一个名为c3p0-config.xml 配置文件(xml文件是复杂的Properties文件,也是key-value)* *   套路: 1. 在src下创建名为c3p0-config.xml配置文件(内容直接复制)*        2. 创建ComboPooledDataSource核心类      */ComboPooledDataSource cpds = new ComboPooledDataSource(); //1. 底层自动会去类加载器路径(写代码:src下) 去寻找一个名为c3p0-config.xml 配置文件// 2. 自动解析: 读取xml中配置信息 , 设置给c3p0即cpds    String sql = "select * from account";  // 同下面              Connection conn = cpds.getConnection();  PreparedStatement pstm = conn.prepareStatement(sql);ResultSet resultSet = pstm.executeQuery();while(resultSet.next()){String name = resultSet.getString("name");System.out.println(name + "--");}conn.close();// 将连接还给连接池cpds.close(); // 销毁}//111111111111111111111111111111111111111111111111111111111111111111111111111111111111111private static void method01() throws PropertyVetoException, SQLException {// 如下硬编码: 用代码来实现参数的设置。一般不用硬编码,用配置文件ComboPooledDataSource cpds = new ComboPooledDataSource();//ComboPooledDataSource是DataSource实现类cpds.setDriverClass( "com.mysql.jdbc.Driver"); //需要mysql-connector-java-x.x.x-bin.jarcpds.setJdbcUrl( "jdbc:mysql://localhost:3306/day05" );cpds.setUser("root");cpds.setPassword("1234");String sql = "select * from account";              Connection conn = cpds.getConnection();   PreparedStatement pstm = conn.prepareStatement(sql);ResultSet resultSet = pstm.executeQuery();while(resultSet.next()){String name = resultSet.getString("name");System.out.println(name); //System.err.println(name);//红色}conn.close();// 将连接还给连接池cpds.close(); // 销毁}
}
//c3p0-config.xml
<c3p0-config>    <default-config>   <!-- 使用默认的配置读取连接池对象 --><!--  如下连接参数 --> <property name="driverClass">com.mysql.jdbc.Driver</property><property name="jdbcUrl">jdbc:mysql://localhost:3306/day05</property><property name="user">root</property><property name="password">1234</property><!-- 如下连接池参数 --><!--initialPoolSize : 初始化连接数  3maxPoolSize: 最大连接数  5checkoutTimeout : 连接超时时间 2000ms(默认10s,访问数超过最大连接数, 有人必须要等,2秒连不上给个提示或报错)                        maxIdleTime :最大的闲置时间,连接超过maxIdleTime没人使用闲置就销毁maxPoolSize中连接,留到minPoolSize中数量,因为费内存--><property name="initialPoolSize">3</property><property name="maxPoolSize">5</property><property name="minPoolSize">2</property><property name="checkoutTimeout">2000</property><property name="maxIdleTime">1000</property></default-config><!--111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111--><named-config name="xx"><!--  连接参数 --><property name="driverClass">com.mysql.jdbc.Driver</property><property name="jdbcUrl">jdbc:mysql://localhost:3306/beitai</property><property name="user">root</property><property name="password">root</property><!-- 连接池参数 --><property name="initialPoolSize">5</property><property name="maxPoolSize">15</property><property name="checkoutTimeout">2000</property><property name="maxIdleTime">1000</property></named-config>
</c3p0-config>
package com.itheima04.c3p0;
import org.junit.Test;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;public class ParseDemo {@Testpublic void method01() throws IOException {Properties p = new Properties(); //Properties就是一个map            p.load(new FileInputStream("src/jdbc.properties")); //相对路径:当前工程src下String url = p.getProperty("url"); //.properties文件中有url=...System.out.println(url);}    @Testpublic void method02() throws IOException {/**     1. 类加载器 classloader 【底层: 输入流】*  作用: 将 .class文件(硬盘) 加载进内存(兼职把jdbc.properties加载进来)。* *     2. classloader怎么知道.class文件在哪里?*  classloader有个默认加载路径:out路径下项目名路径(src和out/production/项目名路径里文件全一样)。* *  相比method01,一般用method02更通用,因为每个工程都会有类加载器路径,但是每个工程的相对路径不一定是当前工程src下。*  如web阶段main方法不在项目里,每个项目的入口是main方法,main方法在tomcat里,*  所以工程的相对路径会变,但是类加载器的路径不变,一直指向.class文件路径。*/ClassLoader classLoader = ParseDemo.class.getClassLoader(); //利用当前类获取类加载器路径InputStream is = classLoader.getResourceAsStream("jdbc.properties"); //获取资源转成流 Properties p = new Properties();p.load(is);String url = p.getProperty("url");System.out.println(url + "-2");}
}
package com.itheima04.c3p0;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.sql.Connection;
import java.sql.SQLException;
//c3p0-config.xml验证
public class C3p0Demo02 { public static void main(String[] args) throws SQLException {            ComboPooledDataSource ds = new ComboPooledDataSource(); //使用默认配置即c3p0-config.xml中<default-config>            // ComboPooledDataSource ds = new ComboPooledDataSource("xx"); //使用命名配置即c3p0-config.xml中<named-config name="xx"> //备胎项目用。               for (int i = 0; i < 6; i++) { //循环模拟: 有几多个用户Connection conn = ds.getConnection();System.out.println(conn); //没有下面close的话,5个连接6个人拿超过2s,会发生timed out报错if(i == 3){ //i=3打印了下又还给连接池,所以i=3打印了两次。             conn.close(); //验证了确实连接还给连接池}}}
}

5个连接6个人拿是不够的。如下最后@2d…打印了2次并且没有发生timed out连接超时错误。

15.druid连接池:自动ds.set

package com.itheima05.druid;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;public class DruidDemo {public static void main(String[] args) throws Exception {//        method01();   //如下配置文件的方式InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");Properties p = new Properties();p.load(is);         DataSource ds = DruidDataSourceFactory.createDataSource(p);  //自动解析.properties文件String sql = "select * from account";        Connection conn = ds.getConnection(); //获取连接PreparedStatement pstm = conn.prepareStatement(sql);ResultSet resultSet = pstm.executeQuery();while(resultSet.next()){String name = resultSet.getString("name");System.out.println(name + "-++");}conn.close();  //将连接还给连接池}//111111111111111111111111111111111111111111111111111111111111111111111111private static void method01() throws SQLException {DruidDataSource ds = new DruidDataSource();ds.setDriverClassName("com.mysql.jdbc.Driver");ds.setUrl("jdbc:mysql:///day05");ds.setUsername("root");ds.setPassword("1234");String sql = "select * from account";               Connection conn = ds.getConnection();  PreparedStatement pstm = conn.prepareStatement(sql);ResultSet resultSet = pstm.executeQuery();while(resultSet.next()){String name = resultSet.getString("name");System.out.println(name + "--");}conn.close();// 将连接还给连接池
//       ds.close(); // 销毁 //实际开发服务器不关,连接池不会销毁}
}
//druid.properties文件,如下key固定写法
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///day05
username=root
password=1234

封装druid连接池工具类:ds = DruidDataSourceFactory。File-New-Project-Java。

package com.itheima.utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;public class JdbcUtil {private static DataSource ds; //下面getConn()也能访问到static{ //静态代码块只运行一次try {Properties p = new Properties();InputStream is = JdbcUtil.class.getClassLoader().getResourceAsStream("druid.properties");            p.load(is);            ds = DruidDataSourceFactory.createDataSource(p); //DataSource ds = 移到最上面了} catch (Exception e) {e.printStackTrace();}}public static DataSource getDs() { //属性私有化后提供对外公开的方法return ds;}public static Connection getConn() throws SQLException {//        Connection connection =  ds.getConnection();
//        return connection;return ds.getConnection(); //等同上面两行}public static void release(AutoCloseable... ios){for (AutoCloseable io : ios) {if(io != null){try {io.close();} catch (Exception e) {e.printStackTrace();}}}}
}
package com.itheima.utils;
import org.junit.Test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
//测试上面JdbcUtil类
public class TestDemo {@Testpublic void method01() throws SQLException {String sql = "select * from account";    Connection conn = JdbcUtil.getConn();PreparedStatement pstm = conn.prepareStatement(sql);ResultSet resultSet = pstm.executeQuery();while(resultSet.next()){String name = resultSet.getString("name");System.out.println(name);}JdbcUtil.release(resultSet,pstm,conn); //注意: conn的close是将连接还给连接池}
}

如下是数据库中name这一列,不是.properties文件的key。

16.execute/update方法:template =

package com.itheima01.template;
import com.itheima.utils.JdbcUtil;
import org.junit.Test;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
/*
*  JdbcTemplate:是spring的框架的一部分,spring框架是工具箱。作用: 简化jdbc代码编写
*       使用:1. 数据库的操作:DCMQ
*            2. 核心类: JdbcTemplate。构造方法:JdbcTemplate(DataSource ds)
*               核心方法: A. void execute : 理论上可以执行任意sql,适合执行DDL,因为void无返回值
*                        B. int update  : 适合执行DML
*                        C. 多种多样 query : 适合执行DQL,返回多种多样
*/
public class TemplateDemo01 {@Testpublic void execute(){String sql = "create table student(id int primary key auto_increment,name varchar(20),age int)";DataSource ds = JdbcUtil.getDs(); //拿到连接池JdbcTemplate template = new JdbcTemplate(ds);        template.execute(sql); //无返回值System.out.println("执行结束");}@Testpublic void update01(){String sql = "insert into student values(null,?,?),(null,?,?)";JdbcTemplate template = new JdbcTemplate(JdbcUtil.getDs());/** int update(String sql, Object... args)*   Object... args:*       1. Object原因是参数类型是不确定的 -> Object*       2. ... 原因是参数个数不确定*   返回值: 被影响的行数*/int count = template.update(sql, "zs", 18, "ls", 19);System.out.println(count); //2}@Testpublic void update02(){String sql = "update student set age = ? where id = ?";JdbcTemplate template = new JdbcTemplate(JdbcUtil.getDs());        Object[] args = {99,1}; //可变参数本质是数组int update = template.update(sql, args); System.out.println(update);}
}

17.queryForXX/query方法:Map.Entry < String, Object > ,Map < String, Object > 两个数据类型

一行map是一个对象,query方法用的是多个对象这个。

package com.itheima01.template;
import com.itheima.utils.JdbcUtil;
import org.junit.Test;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
import java.util.Map;
import java.util.Set;
/*
*   C. 多种多样 query : 适合执行DQL
*       1. queryForXX : XX表示返回值类型
*           a. queryForObject
*           b. queryForMap
*           c. queryForList
*       2. query(RowMapper 行映射器)
*/
public class TemplateDemo02 {JdbcTemplate template = new JdbcTemplate(JdbcUtil.getDs());@Testpublic void queryForObject01(){String sql = "select count(*) from student";/** <T> T queryForObject(String sql, Class<T> requiredType)*     requiredType : 返回值类型 -> Class对象**     EmptyResultDataAccessException : 空结果异常*       查询不到任何数据,会报这个错*/Integer count = template.queryForObject(sql, Integer.class);System.out.println(count);}@Testpublic void queryForObject02(){String sql = "select name from student where id = ?";String s = null;try {s = template.queryForObject(sql, String.class,3); //3传入上面?} catch (DataAccessException e) {e.printStackTrace();System.out.println("查询不到任何结果");}System.out.println(s);}@Testpublic void queryForMap(){String sql = "select * from student where id = ?";Map<String, Object> map = template.queryForMap(sql, 1);
//        System.out.println(map);  //{id=1,name=zs,age=99}Set<Map.Entry<String, Object>> entrySet = map.entrySet();for (Map.Entry<String, Object> entry : entrySet) { //一条String key = entry.getKey();Object value = entry.getValue();System.out.println(key + "=" + value); //竖着打印id=1 name=zs age=99}}@Testpublic void queryForList(){String sql = "select * from student";List<Map<String, Object>> list = template.queryForList(sql);       for (Map<String, Object> map : list) {System.out.println(map); //{id=1,name=zs,age=99} 换行 {id=2,name=ls,age=19}}}
}
package com.itheima01.template;
/*
*  JavaBean三要素(必须要有):
*       1. private属性,属性名和表中字段名是一致的!!! 都是引用类型(因为数据库中空为null,不是0)
*       2. public get set方法
*       3. public 空参构造
*/
public class Student {private Integer id;private String name;private Integer age;@Overridepublic String toString() {return "Student{" +"id=" + id +", name='" + name + '\'' +", age=" + age +'}';}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}
}
package com.itheima01.template;
import com.itheima.utils.JdbcUtil;
import org.junit.Test;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;public class TemplateDemo03 {JdbcTemplate template = new JdbcTemplate(JdbcUtil.getDs());@Testpublic void query01(){String sql = "select * from student";        /** List<T> query(String sql, RowMapper<T> rm)*  RowMapper : 行映射器 (接口)。方法参数中有接口类型, 那么调用的时候必须传入接口的实现类对象*/RowMapper<Student> rowMapper = new RowMapper<Student>() {/**  如下Student mapRow(ResultSet resultSet, int i)   映射行: resultSet转换为Student   *       1. resultSet: 结果集(每行)*       2. i: 当前的行索引(没什么用)*/@Overridepublic Student mapRow(ResultSet resultSet, int i) throws SQLException {String name = resultSet.getString("name");int id = resultSet.getInt("id");int age = resultSet.getInt("age");Student s = new Student();s.setId(id);s.setName(name);s.setAge(age);System.out.println(i);return s;}};                     // RowMapper行映射器 像 动态代理中 InvocationHandler,mapRow方法像invoke方法List<Student> list = template.query(sql, rowMapper);System.out.println(list); //打印出,list里是student类// [Student{id=1,name='zs',age=99},Student{id=2,name='ls',age=19}]                }@Testpublic void query02(){String sql = "select * from student";/*BeanPropertyRowMapper: 类1. RowMapper接口的实现类2. BeanPropertyRowMapper(xx.class); 返回值的泛型*//** BeanPropertyRowMapper (底层反射),思路如下:*    1. 实现RowMapper接口*    2. 重写mapRow方法 : 每行ResultSet -> javaBean*          1. 获取结果集中的数据*               知道结果集有哪些字段 -> 结果集元数据*                值 = resultSet.get(字段);*             id值 = id* *            2. 设置到javabean中去 (需要传参: Student.class)*                clazz = Student.class //获取类*               Student s = clazz.newInstance(); // javabean规范: 默认调用空参构造 *            // Student s = new Student(); //等同于上面两行*        *           //并不知道Student对象有哪些方法,通过反射如下*             setIdMethod = clazz.getMethod("setId",int.class); * *             //怎么知道Student对象中有setId方法呢?*             //因为javabean规范 : 必有set方法。额外要求:set+名字(必须要和表中的字段名一致)*             setIdMethod.invoke(s,id值);*/RowMapper<Student> rowMapper = new BeanPropertyRowMapper<>(Student.class);List<Student> list = template.query(sql, rowMapper);System.out.println(list); //打印出同query01()}
}

18.redis安装:加载.conf

朋友圈数据缓存在手机内存,视频大量弹幕即海量数据先缓存再写入关系型数据库。如下两个存储文件第一次用是没有的。蓝横线是上一行的快捷方式,右击属性最后加上空格x.conf文件路径。Mysql默认3306端口,tomcat默认8080端口。



redis-cli.exe命令行客户端不好用,用图形化客户端redis-desktop-manager-0.7.6.15.exe:链接:https://pan.baidu.com/s/1iJZcnSbRsejUgTlfxu_EKQ 提取码:f8ym 。Add New Connection如下:

redis服务器软件关了, 没有reload就不会保存。redis安装后默认有16个仓库,默认使用db0,用select换数据库。

19.操作:set/get,push/pop,add/rem




如下list重索序,不需要知道集合长度,遍历只要0到-1索引。


如下理解score是分数可重复。

20.持久化策略:存文件,.aof和.rdb文件删了,数据库里数据全没了

RDB(粗略):每次都保存全部数据,所以保存慢,因为是二进制,所以读取快。如0-15分钟操作2次,拍1次照保存硬盘。但15-27分钟又操作2次(只符合save 900 1策略),这时redis崩溃了,15-30分钟才拍照保存,所以丢失了2条数据。所以不频繁操作会丢数据。

AOF(细):写日志不是把整张照记录起来,而是记录一步(保存快),读取慢因为要从头看到尾才能把整个过程联系起来。AOF只能三者选其一,不像RDB三个策略都在线(默认开启),AOF是RDB(会丢数据)的补丁。在redis_windows.conf修改appendonly为yes,所以之前将redis_windows.conf文件名添加到快捷方式的目标中。

如下场景:一分钟6*2000=12000次(此时RDB符合save 60 10000,拍一次照,剩下20秒来不及拍,因为太频繁拍照会卡)。为什么先用RDB?因为读取快。

21.Jedis:java程序连接redis,拿到jedis

如下放入web/WEB-INF/lib并右击add as library。

package com.itheima01.jedis;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;public class JedisDemo {   @Testpublic void method01(){       String host = "127.0.0.1"; int port = 6379;Jedis jedis = new Jedis(host, port);  // 1. 创建连接,不用连接池       jedis.set("book","thinking");   // 2. 访问redisjedis.hset("student","name","zs");        jedis.close();  // 3. 关闭连接System.out.println("测试");}@Testpublic void method02(){       String host = "127.0.0.1";int port = 6379;JedisPoolConfig config = new JedisPoolConfig(); //连接池config.setMaxTotal(5); //最大连接数config.setMaxWaitMillis(2000); // 最长等待时间config.setMaxIdle(2); // 最大空闲数:最多允许两个连接不干活,超过两个会被回收掉,达到释放内存目的JedisPool pool = new JedisPool(config, host, port); //1. 初始化连接池        Jedis jedis = pool.getResource(); //2. 获取连接       String book = jedis.get("book"); //3. 访问redisSystem.out.println(book);  //thinking      jedis.close();  //4. 将连接还给连接池pool.close(); // 销毁连接池,一般只有应用关闭时才用,释放内存}@Testpublic void method03(){ //测试封装的框架Jedis jedis = JedisUtil.getResource();String book = jedis.get("book");System.out.println(book + "-------");jedis.close();}
}
package com.itheima01.jedis;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.ResourceBundle;public class JedisUtil {private static JedisPool pool;/* static{String host = "127.0.0.1";int port = 6379;JedisPoolConfig config = new JedisPoolConfig();config.setMaxTotal(5);  //最大连接数config.setMaxWaitMillis(2000);  // 最长等待时间config.setMaxIdle(2);  // 最大空闲数pool = new JedisPool(config, host, port);}*///如下用jedis.properties替代如上 /*static{Properties p = new Properties();InputStream is = JedisUtil.class.getClassLoader().getResourceAsStream("jedis.properties");try { p.load(is);String host = p.getProperty("host");Integer port = Integer.parseInt(p.getProperty("port"));Integer maxTotal = Integer.parseInt(p.getProperty("maxTotal"));Integer maxWaitMillis = Integer.parseInt(p.getProperty("maxWaitMillis"));Integer maxIdle = Integer.parseInt(p.getProperty("maxIdle"));            //如下同上面JedisPoolConfig config = new JedisPoolConfig();config.setMaxTotal(maxTotal); //最大连接数config.setMaxWaitMillis(maxWaitMillis); // 最长等待时间config.setMaxIdle(maxIdle); // 最大空闲数pool = new JedisPool(config, host, port);} catch (IOException e) { //输入流有异常e.printStackTrace();}}*///如下可替代如上 static{/** ResourceBundle : 资源堆,用来替代Properties成为properties文件专属解析类*    1. 底层: 类加载器  -> 文件必须放在src下*    2. 只能加载properties文件 -> 文件的后缀名.properties不要写。*/ResourceBundle bundle = ResourceBundle.getBundle("jedis");String host = bundle.getString("host");Integer port = Integer.parseInt(bundle.getString("port"));Integer maxTotal = Integer.parseInt(bundle.getString("maxTotal"));Integer maxWaitMillis = Integer.parseInt(bundle.getString("maxWaitMillis"));Integer maxIdle = Integer.parseInt(bundle.getString("maxIdle"));//如下同上面JedisPoolConfig config = new JedisPoolConfig();config.setMaxTotal(maxTotal); //最大连接数config.setMaxWaitMillis(maxWaitMillis); // 最长等待时间config.setMaxIdle(maxIdle); // 最大空闲数pool = new JedisPool(config, host, port);}public static Jedis getResource(){Jedis jedis = pool.getResource();return jedis;}
}
//jedis.properties文件
host = 127.0.0.1
port = 6379
maxTotal = 5
maxWaitMillis = 2000
maxIdle = 2

22.案例_好友列表:json = om.

22.1 前端:index.html



//index.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script src="js/jquery-3.3.1.min.js"></script><script>$(function () { //页面加载事件$.get("/FriendServlet","",function (data) { //data// console.log(data)var content = ""$(data).each(function (index,element) {content += "<li>" + element.name + "</li>"})$("#myid").html(content) //因为<li>是html},"json")})</script>
</head><!--111111111111111111111111111111111111111111111111111111111111-->
<body><ul id="myid"></ul>
</body>
</html>


如下就是index.html效果。

22.2 web:FriendServlet .java

package com.heima.example.web;
import com.heima.example.service.FriendService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@WebServlet(urlPatterns = "/FriendServlet")
public class FriendServlet extends HttpServlet {  @Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doGet(request, response);}@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {FriendService service = new FriendService(); //调用service层代码String json = service.findAllFriend();response.setContentType("text/html;charset=utf-8");response.getWriter().print(json);}
}

22.3 service:FriendService.java

package com.heima.example.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.heima.example.bean.Friend;
import com.heima.example.dao.FriendDao;
import com.itheima01.jedis.JedisUtil;
import com.sun.org.apache.bcel.internal.generic.NEW;
import redis.clients.jedis.Jedis;
import java.util.List;
/*
*  service层: 业务逻辑 + 缓存 cache
*  缓存弊端: 数据不更新 (查询走缓存,如果执行增删改, 重新查询数据库,更新缓存)
*  如上括号里的更新缓存也会存在缓存延迟的情况(如朋友圈删除动态有时也能看见)
*  朋友圈不是实时同步,如果实时同步对服务器来说压力大。好友列表的在线状态是实时同步的,用心跳长连接。
*/
public class FriendService { //service文件夹下
//选中再ctrl + shift + u转为大写,"example_friend_list"变量改了,全局FRIEND_LIST_CACHE常量不用改public static final String FRIEND_LIST_CACHE = "example_friend_list"; public String findAllFriend() throws JsonProcessingException {        Jedis jedis = JedisUtil.getResource();String json = jedis.get(FRIEND_LIST_CACHE); //直接从缓存里取if(json == null){ //就从mysql数据库中取                      FriendDao dao = new FriendDao();List<Friend> list = dao.findAll();                         ObjectMapper om = new ObjectMapper();json = om.writeValueAsString(list); //list转换为jsonjedis.set(FRIEND_LIST_CACHE,json); //记得往缓存里放一份json即字符串System.out.println("从mysql中查");}else{System.out.println("从redis中查");}jedis.close(); //记得还给连接池,不然5个用完就崩了return json;}
}

22.4 dao:FriendDao.java

package com.heima.example.dao;
import com.heima.example.bean.Friend;
import com.heima.example.utils.JdbcUtil;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;public class FriendDao {  //Dao文件夹下public List<Friend> findAll() {String sql = "select * from user";JdbcTemplate template = JdbcUtil.getTemplate();List<Friend> list = template.query(sql, new BeanPropertyRowMapper<>(Friend.class));return list;}
}

22.5 bean:Friend.java

package com.heima.example.bean;public class Friend { //bean文件夹下private Integer id;private String name;private String password;@Overridepublic String toString() {return "Friend{" +"id=" + id +", name='" + name + '\'' +", password='" + password + '\'' +'}';}    public Integer getId() {return id;}    public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}
}

如下刷新浏览器index.html页面。

23.总结:存储过程(PL/SQL代码集,像没有返回值的自定义函数)和函数需要用户显示调用才执行,而触发器是由一个事件来触发运行,当某个事件发生时会自动地隐式运行,不能被显示的调用。sql多用group by

23.1 数据库优化:项目管理PMP证书,数据库DBA/OCM证书


1. 数据库开启缓存,还要避免缓存失效如下第一行:

3. 小结果集适合建索引。如果占表超过50%不适合建索引,因为数据量达到一定量后会走全表扫描:原因是mysql内部有一个优化器进行最优策略即聚集索引和非聚集索引查询原理,数据量太大会先走非聚集索引,然后才走聚集索引获取数据。太频繁加个redis。

4. 有limit 1,查到一行就不继续往下走。

5. 数据库创建完一次连接后会永久处于连接状态,已经永久连接了,apache还给数据库不断发连接,给数据库增加了压力,大公司会解决这个。

6. myISAM适合大数据量查询,因为进行update时会进行表锁,这时候读表都是无效的,必须等插入操作完成。innoDB与myISAM相反,且支持B树索引,ACID(事务原子性,一致性,,,),行锁。

7. 大量insert或delete时会把表整个锁起来,导致大量web服务器请求过来进不去表,导致宕机,所以用limit进行拆分。

8. 不同数据类型占用硬盘空间不一样,如果占用硬盘空间小且紧凑,这样硬盘数据读写快。

9. 字段长度统一,数据库计算偏移量轻松。从前端查出来的数据会多出来一些空格,用trim去除空格再封装进对象。

10. 报错:该对象属性不为null或mysql查询数据为null…,用’空格’。数据库一字段查出赋值到java对象上,如果字段为null会报空指针异常。

11. enum速度比varchar快。

12. 不管任何方式查询表,最终都会通过主键定位到数据,建立主键会有效提高性能。id主键多用int速度比varchar快。

14. 避免使用rand(),order by rand()把数据库累死。

15. 两个字段类型不一致,索引建不上。

23.2 数据库备份和恢复:冷热备份

1.冷备份:适用于myisam引擎,不适用于innoDB引擎:关闭mysql,如下或可以点进book文件夹里将.frm(表结构)和.MYD(表数据)和.MYI(表索引)文件拷贝出来,这三个组合到一起就是一张表,恢复的时候只需把你copy出来的这些文件再重新粘贴回去即可。

2.热备份:执行mysql安装目录下的bin/里面的这个mysqldump.exe工具。mysqldump是工具 -u是用户名 -p是密码 -A是全部的意思 -d是表结构 -t是表数据 > 是重定向的意思 > 右边是需要输出的路径和文件名。
2.1 全备份:mysqldump -uroot -p123456 -A > /back/backdb.sql
2.2 备份指定库命令:mysqldump -uroot -p123456 db1,db2 > /back/backdb.sql
2.3 备份指定表命令:mysqldump -uroot -p123456 db1 tb1 tb2, db2 tb2> /back/backdb.sql
2.4 备份表结构命令:mysqldump -uroot -p123456 -A -d > /back/backdb.sql
2.5 备份表数据命令:mysqldump -uroot -p123456 -A -t > /back/backdb.sql
2.6 恢复:source命令在执行时会显示详细信息,能看到执行到哪出错了:source /back/backdb.sql
float(8,6)小数点前面占2位,小数点后面占6位。

24.生成数据:crontab

https://pan.baidu.com/s/1QL5TK3mcowVjCIPPCdhstw,提取码:0p53。

打开文件一行一行读出来,字符拆分存入结构体再存入容器:mkdir src bin ini data,data/ftp/surfdata/,cd src/,vi makefile如下。

// 记住复制时第一行完整CC,一个tab缩进。crtsurfdata:crtsurfdata.cpp _public.h _public.cpp即 _public.h _public.cpp改变也要重新编译。
CC=g++
FLAG=-g
#FLAG=-02all:crtsurfdata
crtsurfdata:crtsurfdata.cpp _public.h _public.cpp$(CC) $(FLAG) -o crtsurfdata crtsurfdata.cpp _public.cppcp crtsurfdata ../bin/.
clean:rm -rf crtsurfdata


日志切换怎么做的:先把日志文件关闭,生成日志备份的文件名再rename,重新打开新的日志文件。 在多进程中不能采用日志切换的方法,在某一个进程中关闭指针文件,其他就全部关了不会再打开。

如果2秒这样执行很频繁的话,自身调度没问题,但像60s一分钟执行一次(运行一次1秒都不到就过去了),挂着一分钟会常驻内存浪费系统资源。所以用linux中crontab,每个用户都有自己的crontab任务调度。

cron(特定时间点执行shell指令)是unix下自带的工具,伴随着系统的启动,是一个守护进程。如下是cron表达式,后面接空格再加上要执行的shell脚本。

如下每星期每月每日每小时的第20分钟到第40分钟每分钟都会执行一次(1小时执行21次)。


crontab -e进入如下,dow表示星期。

如下是crtsurfdata.cpp生成全国气象站点观测的分钟数据:结构体值,容器值,txt文件。

#include "_public.h" // 安徽,58102,亳州,33.47,115.44,39.1,_public.h同目录
// 全国气象 (站点参数stcode) 数据结构
struct st_stcode
{char provname[31];   // 省名称char obtid[11];      // 站点代码 //也可能是字母所以用字符串表达char cityname[31];   // 城市名double lat;          // 纬度double lon;          // 经度double height;       // 海拔高度
};
// 全国气象站点分钟 (生成的观测数据surfdata) 数据结构
struct st_surfdata
{char obtid[11];      // 站点代码char ddatetime[21];  // 数据时间:格式yyyy-mm-dd hh24:mi:ss。int  t;              // 气温:单位,0.1摄氏度int  p;              // 气压:0.1百帕 int  u;              // 相对湿度,0-100之间的值。int  wd;             // 风向,0-360之间的值。int  wf;             // 风速:单位0.1m/sint  r;              // 降雨量:0.1mmint  vis;            // 能见度:0.1米
};
vector<struct st_stcode> vstcode;   // 存放全国站点参数的容器  //将结构体放入容器中,就直接操作容器了
vector<struct st_surfdata> vsurfdata;   // 存放全国气象站点分钟观测数据的容器
// 从站点参数文件中加载到vstcode容器中,在函数里字符串只能传地址
bool LoadSTCode(const char *inifile);
// 创建全国气象站点分钟观测数据,存放在vsurfdata容器中
void CrtSurfData();
// 把容器vsurfdata中的全国气象站点分钟观测数据写入文件
bool CrtSurfFile(const char *outpath);
CLogFile logfile;
void EXIT(int sig);int main(int argc,char *argv[],char *envp[])    //*argv[]:指针的数组,也可以**argv指针的指针
{  if (argc!=4){  printf("\n本程序用于生成全国气象站点观测的分钟数据。\n");printf("/root/qxidc/bin/crtsurfdata 站点参数 数据文件存放的目录 日志文件名\n");printf("例如:/root/qxidc/bin/crtsurfdata /root/qxidc/ini/stcode.ini /root/qxidc/data/ftp/surfdata /root/qxidc/log/crtsurfdata.log\n");  //目录自动创建return -1;  //程序没有捕获运行结果,所以0,-1无所谓,return -1失败跳出大循环,return 0成功}// 关闭全部输入输出和信号,也可写成void CloseIOAndSignal(); 下面printf失效CloseIOAndSignal();// 因为程序运行需要退出,所以捕获退出信号(ctrl+c或kill/kill all),signal将这两个信号值作为sig参数传给EXIT函数signal(SIGINT,EXIT); signal(SIGTERM,EXIT);  //可以ctrl+c或kill/kill all通知程序退出  if (logfile.Open(argv[3],"a+")==false){printf("打开日志文件失败(%s)。\n",argv[3]); return -1;}while (true)  //可把while (true)一行和sleep(60)去除,用crontab调度{// 从站点参数文件中加载到vstcode容器中,argv[0]计算机从0开始if (LoadSTCode(argv[1])==false) { sleep(60); continue; }    logfile.Write("加载参数文件(%s)成功!\n",argv[1]);CrtSurfData();  // 生成全国气象站点分钟观测数据,存放在vsurfdata容器中// 把容器vsurfdata中的全国气象站点分钟观测数据写入文件if (CrtSurfFile(argv[2])==false) { sleep(60); continue; }sleep(60);  // 进入死循环60s执行一次,自身调度自己}return 0;
} //11111111111111111111.从站点参数文件中加载到vstcode容器中
bool LoadSTCode(const char *inifile)
{vstcode.clear();CCmdStr CmdStr;  // CmdStr类(_public.h中)切分并暂存字符串struct st_stcode stcode;  // st_stcode为数据结构,stcode为结构体变量CFile File;if (File.Open(inifile,"r") == false){logfile.Write("File.Open(%s) 失败。\n",inifile); return false;}char strbuffer[101];  //101个字节可以存放50个汉字  strbuffer就是stcode.ini站点参数内容while (true){memset(&stcode,0,sizeof(struct st_stcode));// memset(strbuffer,0,sizeof(strbuffer)); 因为Fgets里有初始化,所以这行省去    if (File.Fgets(strbuffer,100)==false) break; // 读取一行,在循环里就多行读取,break不能写成return falseCmdStr.SplitToCmd(strbuffer,",",true); // true为删除空格,CmdStr里存了一行拆分后的片段,循环里就是多行CmdStr.GetValue(0, stcode.provname);  //stcode.provname就存了拆分的值,在循环里CmdStr.GetValue(1, stcode.obtid);     //GetValue做了重载,有第三个参数取长度CmdStr.GetValue(2, stcode.cityname);CmdStr.GetValue(3,&stcode.lat);  //double 传地址&CmdStr.GetValue(4,&stcode.lon);     CmdStr.GetValue(5,&stcode.height);vstcode.push_back(stcode);   // printf("strbuffer=%s",strbuffer);   //不需写成strbuffer=%s\n,因为文件本身有换行符// printf("provname=%s,obtid=%s,lat=%.2lf\n.....",stcode.provname,stcode.obtid,stcode.lat,......);}return true;
}//1111111111111111112.创建全国气象站点分钟观测数据,存放在vsurfdata容器中,//Crt是create缩写
void CrtSurfData()  // 返回一定会成功的,所以不需要返回值void
{vsurfdata.clear();  // 清空容器srand(time(0));     // 播随机数种子  char strLocalTime[21];  LocalTime(strLocalTime,"yyyy-mm-dd hh24:mi"); // LocalTime3个参数:一:存放时间的字符串。二:取出当前时间用什么格式。三:时间偏移量秒。将memset(strLocalTime,0,sizeof(strLocalTime));放入LocalTime()中strcat(strLocalTime,":00"); //不计秒,里面800多行记录时间和文件名时间一样就把秒变成00struct st_surfdata stsurfdata;for (int ii=0;ii<vstcode.size();ii++)  //根据站点代码个数{memset(&stsurfdata,0,sizeof(struct st_surfdata)); STRCPY(stsurfdata.obtid,10,vstcode[ii].obtid);  // 站点代码  //STRCPY(目的,长度,源)// LocalTime(stsurfdata.ddatetime);STRCPY(stsurfdata.ddatetime,20,strLocalTime);   // 数据时间采用当前时间stsurfdata.t=rand()%351;       // 气温:单位,0.1摄氏度  // 结构体值 //0-350间整数就行stsurfdata.p=rand()%265+10000; // 气压:0.1百帕  //0到264再加10000就是10000到10264stsurfdata.u=rand()%100+1;     // 相对湿度,1-100之间的值。stsurfdata.wd=rand()%360;      // 风向,0-360之间的值。stsurfdata.wf=rand()%150;      // 风速:单位0.1m/sstsurfdata.r=rand()%16;        // 降雨量:0.1mmstsurfdata.vis=rand()%5001+100000;  // 能见度:0.1米vsurfdata.push_back(stsurfdata);}
}//1111111111111111113.把容器vsurfdata中的全国气象站点分钟观测数据写入文件
bool CrtSurfFile(const char *outpath)
{CFile File;char strLocalTime[21];LocalTime(strLocalTime,"yyyymmddhh24miss");char strFileName[301]; //目录名+文件名即下行outpath+/SURF_ZH_%s_%d.txt,初始化也在SNPRINTF中SNPRINTF(strFileName,300,"%s/SURF_ZH_%s_%d.txt",outpath,strLocalTime,getpid());  // outpath目录名   if (File.OpenForRename(strFileName,"w")==false){logfile.Write("File.Open(%s) 失败!\n",strFileName); return false; // 不用printf显示到屏幕了}for (int ii=0;ii<vsurfdata.size();ii++) //装进容器里就是为了这行循环{// 站点代码,数据时间,气温,气压,相对湿度,风向,风速,降雨量,能见度  //存进容器的是整数,除以小数得小数显示到文件中File.Fprintf("%s,%s,%.1f,%.1f,%d,%d,%.1f,%.1f,%.1f\n",\vsurfdata[ii].obtid,vsurfdata[ii].ddatetime,vsurfdata[ii].t/10.0,vsurfdata[ii].p/10.0,\vsurfdata[ii].u,vsurfdata[ii].wd,vsurfdata[ii].wf/10.0,vsurfdata[ii].r/10.0,vsurfdata[ii].vis/10.0);}File.CloseAndRename();   // 关闭文件logfile.Write("生成数据文件(%s)成功,数据时间=%s,记录数=%d!\n\n",strFileName,vsurfdata[0].ddatetime,vsurfdata.size()); // 数据时间=和文件名时间一样就是最后秒改为00(即文件里839行【vsurfdata.size()行】记录时间)vstcode.clear(); vsurfdata.clear(); //这里不清空容器,记录数=839,1678....(除非每次运行都重新make,不然要清空容器)return true;
}
void EXIT(int sig)
{logfile.Write("程序退出,sig=%d\n\n",sig);  // killall crtsurfdata ,.log日志文件中记录了:时间..程序退出,sig=15exit(0); //exit(0)程序退出自然会调用logfile的析构函数,所以这里不需要调用
}


一个.ini文件839行记录不同时间可生成多个.txt文件也有839行记录,一个.txt文件站点代码不同但时间一样。下面为改进版可选择生成.xml文件。

//crtsurfdata1.cpp生成的数据文件支持txt和xml两种格式
#include "_public.h"
#include "_shqx.h" // _shqx.h里包含st_stcode,st_surfdata两个结构体
vector<struct st_stcode> vstcode;   // 存放全国站点参数的容器
vector<struct st_surfdata> vsurfdata;   // 存放全国气象站点分钟观测数据的容器
// 从站点参数文件中加载到vstcode容器中
bool LoadSTCode(const char *inifile);
// 创建全国气象站点分钟观测数据,存放在vsurfdata容器中
void CrtSurfData();
// 把容器vsurfdata中的全国气象站点分钟观测数据写入文件
bool CrtSurfFile(const char *outpath,const char *datafmt);
CLogFile logfile;
void EXIT(int sig);int main(int argc,char *argv[],char *envp[])
{if (argc!=5){printf("\n本程序用于生成全国气象站点观测的分钟数据,与crtsurfdata程序不同,本程序生成的数据文件支持txt和xml两种格式。\n");printf("/htidc/shqx/bin/crtsurfdata1 站点参数 数据文件存放的目录 日志文件名 数据格式[txt|xml]\n");printf("例如:/htidc/shqx/bin/crtsurfdata1 /htidc/shqx/ini/stcode.ini /data/shqx/ftp/surfdata1 /log/shqx/crtsurfdata1.log txt\n");printf("      /htidc/shqx/bin/crtsurfdata1 /htidc/shqx/ini/stcode.ini /data/shqx/ftp/surfdata1 /log/shqx/crtsurfdata1.log xml\n");return -1;}// 关闭全部的信号和输入输出CloseIOAndSignal();// 处理程序退出的信号signal(SIGINT,EXIT); signal(SIGTERM,EXIT);if (logfile.Open(argv[3],"a+")==false){printf("打开日志文件失败(%s)。\n",argv[3]); return -1;}while (true){// 从站点参数文件中加载到vstcode容器中if (LoadSTCode(argv[1])==false) { sleep(60); continue; }logfile.Write("加载参数文件(%s)成功!\n",argv[1]);CrtSurfData();  // 创建全国气象站点分钟观测数据,存放在vsurfdata容器中// 把容器vsurfdata中的全国气象站点分钟观测数据写入文件if (CrtSurfFile(argv[2],argv[4])==false) { sleep(60); continue; }sleep(60);}return 0;
}//1111111111111111111.从站点参数文件中加载到vstcode容器中
bool LoadSTCode(const char *inifile)
{vstcode.clear();CCmdStr CmdStr;struct st_stcode stcode;CFile File;if (File.Open(inifile,"r") == false){logfile.Write("File.Open(%s) 失败。\n",inifile); return false;}char strbuffer[101];while (true){memset(&stcode,0,sizeof(struct st_stcode));if (File.Fgets(strbuffer,100)==false) break;CmdStr.SplitToCmd(strbuffer,",",true);CmdStr.GetValue(0, stcode.provname);  //结构体值CmdStr.GetValue(1, stcode.obtid);CmdStr.GetValue(2, stcode.cityname);CmdStr.GetValue(3,&stcode.lat);CmdStr.GetValue(4,&stcode.lon);CmdStr.GetValue(5,&stcode.height);vstcode.push_back(stcode);}return true;
}//1111111111111111112.创建全国气象站点分钟观测数据,存放在vsurfdata容器中
void CrtSurfData()
{vsurfdata.clear();  // 清空容器srand(time(0));  // 播随机数种子char strLocalTime[21];LocalTime(strLocalTime,"yyyy-mm-dd hh24:mi");strcat(strLocalTime,":00");struct st_surfdata stsurfdata;for (int ii=0;ii<vstcode.size();ii++){memset(&stsurfdata,0,sizeof(struct st_surfdata));STRCPY(stsurfdata.obtid,10,vstcode[ii].obtid);  // 站点代码STRCPY(stsurfdata.ddatetime,20,strLocalTime);   // 数据时间采用当前时间stsurfdata.t=rand()%351;       // 气温:单位,0.1摄氏度stsurfdata.p=rand()%265+10000; // 气压:0.1百帕stsurfdata.u=rand()%100+1;     // 相对湿度,0-100之间的值。stsurfdata.wd=rand()%360;      // 风向,0-360之间的值。stsurfdata.wf=rand()%150;      // 风速:单位0.1m/sstsurfdata.r=rand()%16;        // 降雨量:0.1mmstsurfdata.vis=rand()%5001+100000;  // 能见度:0.1米vsurfdata.push_back(stsurfdata);}
}//1111111111111111113.把容器vsurfdata中的全国气象站点分钟观测数据写入文件
bool CrtSurfFile(const char *outpath,const char *datafmt)
{CFile File;char strLocalTime[21];LocalTime(strLocalTime,"yyyymmddhh24miss");char strFileName[301];SNPRINTF(strFileName,300,"%s/SURF_ZH_%s_%d.%s",outpath,strLocalTime,getpid(),datafmt);   if (File.OpenForRename(strFileName,"w")==false){logfile.Write("File.Open(%s) 失败!\n",strFileName); return false;}if (strcmp(datafmt,"xml")==0) File.Fprintf("<data>\n"); //开头加for (int ii=0;ii<vsurfdata.size();ii++){// 站点代码,数据时间,气温,气压,相对湿度,风向,风速,降雨量,能见度if (strcmp(datafmt,"xml")==0){File.Fprintf("<obtid>%s</obtid><ddatetime>%s</ddatetime><t>%.1f</t><p>%.1f</p><u>%d</u><wd>%d</wd><wf>%.1f</wf><r>%.1f</r><vis>%.1f</vis><endl/>\n",\vsurfdata[ii].obtid,vsurfdata[ii].ddatetime,vsurfdata[ii].t/10.0,vsurfdata[ii].p/10.0,\vsurfdata[ii].u,vsurfdata[ii].wd,vsurfdata[ii].wf/10.0,vsurfdata[ii].r/10.0,vsurfdata[ii].vis/10.0);}else{File.Fprintf("%s,%s,%.1f,%.1f,%d,%d,%.1f,%.1f,%.1f\n",\vsurfdata[ii].obtid,vsurfdata[ii].ddatetime,vsurfdata[ii].t/10.0,vsurfdata[ii].p/10.0,\vsurfdata[ii].u,vsurfdata[ii].wd,vsurfdata[ii].wf/10.0,vsurfdata[ii].r/10.0,vsurfdata[ii].vis/10.0);}}if (strcmp(datafmt,"xml")==0) File.Fprintf("</data>\n"); //结尾加File.CloseAndRename();   // 关闭文件logfile.Write("生成数据文件(%s)成功,数据时间=%s,记录数=%d!\n\n",strFileName,vsurfdata[0].ddatetime,vsurfdata.size());vstcode.clear(); vsurfdata.clear();return true;
}
void EXIT(int sig)
{logfile.Write("程序退出,sig=%d\n\n",sig);exit(0);
}

如下生成一个xml文件里也有800多行,最后也以< data >结尾。

25.ftp:ftp是tcp/ip协议族中一员,分客户端和服务端

25.1 安装:linux操作系统的用户也是ftp的用户,可以配置专用的ftp用户,专用的ftp用户只能用于ftp,不能登录操作系统


如下是ftp安装:安装ftp客户端:yum -y install ftp (remove)(只要ftp服务端安装并配置好防火墙等,任何主机安装了客户端都可以连上)。安装ftp服务端:yum -y install vsftpd (本地和远程服务器都可安装)。

如下是ftp服务端配置:1.防火墙开启 21 端口和5500-6000高端口
如果采用被动模式,防火墙开通21端口
(命令含义: --zone#作用域 --add-port=21/tcp#添加端口,格式为:端口/通讯协议 --permanent # 永久生效)
#firewall-cmd --zone=public --add-port=21/tcp --permanent
如果采用主动模式,防火墙还要开通20端口
#firewall-cmd --zone=public --add-port=20/tcp --permanent
#firewall-cmd --zone=public --add-port=5500-6000/tcp --permanent
(设置被动模式的高端口范围为5500-6000,passive命令为on,完成后必须init 6重启,netstat -na --ip,firewall-cmd --list-ports,防火墙配置不好可以直接关闭防火墙)

2.配置ftp高端口参数和关闭 selinux
#vi /etc/vsftpd/vsftpd.conf (必选装好vsftpd服务端,init6)

listen=YES  //将上面含有listen都注释掉
listen_ipv6=NO //关闭ipv6的监听,不然会ls,dir不出命令pam_service_name=vsftpd
userlist_enable=YES
tcp_wrappers=YESpasv_min_port=5500
pasv_max_port=6000pasv_enable=YES
pasv_address=47.100.167.156 //服务端公网ip

#vi /etc/selinux/config,修改成 selinux=disabled,执行 setenforce 0 使修改马上生效。

3.登录阿里云控制台实例安全组(如上配置不成功,手动配置端口)。

点击安全组列表中配置规则。

4.vsftpd服务端相关操作
systemctl start vsftpd # 启动
systemctl stop vsftpd # 停止
systemctl status vsftpd # 查看
systemctl enable vsftpd # 开机自动启动vsftpd服务(类似windows管理工具中服务)
systemctl disable vsftpd # 禁用vsftpd服务

5.防火墙服务相关操作
以下是centos7的命令:
centos7中的防火墙名改成了firewall
systemctl restart firewalld.service # 重启防火墙
systemctl stop firewalld.service # 关闭防火墙
systemctl start firewalld.service # 启动防火墙
systemctl status firewalld.service # 查看防火墙服务状态
systemctl enable firewalld.service # 开机禁用防火墙
以下是centos6的命令:
chkconfig iptables off # 开机禁用防火墙
service vsftpd restart # 重启ftp服务端
service iptables stop #关闭防火墙

25.2 ftp命令:get,put

登录ftp服务器:linux命令行输入:ftp ip地址,然后输入用户名和密码(adduser ,passwd ),必须先开启ftp服务

windows的命令提示符下有ftp客户端程序,但是不好用。 采用资源管理器,输入:ftp://服务器ip,如下图:

在空白的位置点鼠标右键,选择登录菜单, 输入用户名和密码登录ftp服务器界面,直接将文件拖到windows界面。

查看/下载/上传文件:ftp的传输模式分ASCII码和二进制两种,二进制可传输任何文件,而ASCII码方式只能传输.txt、.html,实际开发中,不管什么文件都用二进制传输文件省事

dir:显示服务器目录和文件列表
ls:显示服务器目录和文件列表
cd:进入服务器指定的目录
lcd:进入本地客户端指定的目录。lcd空可查看本地位置
(dir命令可以使用通配符“*”和“?”,比如,显示当前目录中所有扩展名为jpg的文件,可使用命令 dir *.jpg)

type:查看当前的传输方式
ascii:设定传输方式为ASCII码方式
binary:设定传输方式为二进制方式
get/recv:下载单个文件get filename newname(filename为下载的ftp服务器上的文件名,newname为保存在本都计算机上时使用的名字,get/recv只能取文件(文件夹需要打包压缩),加-r可以操作文件夹
mget:下载多个文件mget filename filename ....(mget命令支持通配符“*”和“?”,比如:mget *.jpg 表示下载ftp服务器当前目录下的所有扩展名为jpg的文件。)
prompt:关闭/打开互交提示。关闭后,mget不用输入y

put/send:上传单个文件put filename newname,filename为上传的本地文件名,newname为上传至ftp服务器上时使用的名字,如果不指定newname,文件将以原名上传。
mput:上传多个文件mput filename filename ....,mput命令支持通配符“*”和“?”
bye:结束与服务器的ftp会话并退出ftp环境
pwd:查看ftp服务器上的当前工作目录
rename filename newfilename:重命名ftp服务器上的文件
delete filename:删除ftp服务器上一个文件。
mdelete [remote-files] :删除多个文件。
mkdir pathname:在服务器上创建目录。
rmdir pathname:删除服务器上的目录。
passive:主动模式与被动模式切换。
nlist:列出服务器目录中的文件名,如:nlist /home/w /tmp/tmp.list,表示把服务器上/home/w目录下的文件列出来,结果输出到本地的/tmp/tmp.list文件中,输出文件名是全路径。
help [cmd]:显示ftp命令的帮助信息,cmd是命令名,如果不带参数,则显示所有ftp命令。

25.3 ftp应用:OpenForRename,Fgets

数据完整性:在一文件夹里 ls | wc 统计数据行数

如下exit退出csh,不然export等命令行命令在cshell中执行不了。



以下用C语言实现ftp客户端,File.Open>写文件>File.close是需要时间的,写文件过程中ftp将这文件取走的话,取走的文件不完整,如果是银行数据不可行。如下有两个数据不完整不安全环节。

第一环节供ftp用:创建文件SNPRINTF命名为.txt.tmp,读取Open文件.txt.tmp,数据写完后才会close再rename为.txt,避免文件还没Close被取走。第二环节供处理数据程序用:避免ftp没有get完被处理数据程序取走。

下面在本地rename,这样两步就能避免中间状态文件被读取。

下面修改crtsurfdata.cpp。

sleep(20);// 停20秒方便观看。

C语言实现ftp下载文件:.a就是静态库相当.c/.cpp一个别名,编译时不让人看见,makefile中libftp.a就是ftplib.c。

如下demo18用到_ftp.h(连着_ftp.cpp一起编译),_ftp.h又用到ftplib.h(连着ftplib.c一起编译,.h必用到其实现的.c/.cpp)ftplib.h和ftplib.c源代码链接:https://pan.baidu.com/s/1P_GTtiOpMnO3KqU6_VVnzQ,提取码:u77a。

// 记住复制时第一行完整CC,一个tab缩进
CC=g++
FLAG=-g
#FLAG=-02all:crtsurfdata libftp.a demo18 ftpgetfileslibftp.a:ftplib.cgcc -c -o libftp.a ftplib.cdemo18:demo18.cpp _ftp.h _ftp.cpp libftp.ag++ $(FLAGS) -o demo18 demo18.cpp _public.cpp _ftp.cpp libftp.aftpgetfiles:ftpgetfiles.cpp _ftp.h _ftp.cpp libftp.a$(CC) $(FLAG) -o ftpgetfiles ftpgetfiles.cpp _public.cpp _ftp.cpp libftp.acp ftpgetfiles ../bin/.crtsurfdata:crtsurfdata.cpp _public.h _public.cpp$(CC) $(FLAG) -o crtsurfdata crtsurfdata.cpp _public.cppcp crtsurfdata ../bin/.clean:rm -rf crtsurfdata demo18 ftpgetfiles libftp
// 本程序演示Cftp客户端类,采用ftp协议从服务器上获取文件,demo18.cpp
#include "_ftp.h"
int main(int argc,char *argv[])
{Cftp ftp; // 登录远程FTP服务器if (ftp.login("193.112.16.23:21","用户名","密码",FTPLIB_PASSIVE) == false){printf("ftp.login() failed.\n"); return -1;}  ftp.mtime("/home/y/surfdata/SURF_ZH_20200209122402_2815.txt");ftp.size("/home/y/surfdata/SURF_ZH_20200209122402_2815.txt");printf("=%s=\n",ftp.response());  // 改变本地目录chdir("/tmp");// 进入ftp服务器上文件存放的目录if (ftp.chdir("/home/y/surfdata") == false){printf("ftp.chdir() failed.\n"); return -1;}// 获取对方目录文件的列表,存放在"/tmp/ftp.list"文件中if (ftp.nlist("*.txt","/tmp/ftp.list")== false){printf("ftp.nlist() failed.\n"); return -1;}CFile File;File.OpenForRead("/tmp/ftp.list","r");    // 逐行读取文件的内容,并把文件get到本地char strFileName[101];while (true){// 从文件中读取一行memset(strFileName,0,sizeof(strFileName));if (File.Fgets(strFileName,100) == false) break;    strFileName[strlen(strFileName)-1]=0; //去除最后回车符    printf("get %s ...",strFileName);// 从远程取文件if (ftp.get(strFileName,strFileName)==false){printf("ftp.get(%s) failed.\n",strFileName); break;}printf("ok.\n");}File.CloseAndRemove();ftp.logout();return 0;
}

// _ftp.h
#ifndef __FTP_H
#define __FTP_H
#include "_public.h"
#include "ftplib.h"
class Cftp
{public:// ftp连接句柄netbuf *m_ftpconn;// 文件的大小unsigned int m_size;// 文件的时间modifytimechar m_mtime[21];Cftp();~Cftp();// 存放login()方法登录失败的原因bool m_connectfailed;bool m_loginfailed;bool m_optionfailed;  void initdata();// 登录ftp服务器// in_host 服务器地址和端口,中间用":"分隔,如"192.168.1.1:21"// in_username ftp用户名// in_password ftp的密码// in_mode 传输模式,FTPLIB_PASSIVE是被动模式,FTPLIB_PORT是主动模式bool login(const char *in_host,const char *in_username,const char *in_password,const int in_mode=FTPLIB_PASSIVE);  // 注销bool logout();// 获取ftp服务器上文件的时间bool mtime(const char *in_remotefilename);// 获取ftp服务器上文件的大小bool size(const char *in_remotefilename);// 向服务端发送site命令bool site(const char *in_command);// 改变ftp远程目录bool chdir(const char *in_remotedir);// 在ftp服务器上创建目录bool mkdir(const char *in_remotedir);// 删除ftp服务器上的目录bool rmdir(const char *in_remotedir);// 发送list命令列出ftp服务器目录中的文件,结果保存到本地文件中// 如果是列出当前目录,in_remotedir用"","*","."都行。bool nlist(const char *in_remotedir,const char *out_listfilename);// 发送dir命令列出ftp服务器目录中的文件,结果保存到本地文件中bool dir(const char *in_remotedir,const char *out_listfilename);// 从ftp服务器上获取文件// in_remotefilename 待获取的远程文件名// in_localfilename  本地文件名,可以与in_remotefilename不同// bCheckMTime 文件传输完成后,是否核对文件传输前后的时间,保证文件的完整性// 注意,文件在传输的过程中,采用临时文件命名的方法,即在in_localfilename后加".tmp",在传输// 完成后才正式改为in_localfilenamebool get(const char *in_remotefilename,const char *in_localfilename,const bool bCheckMTime=true);// 向ftp服务器发送文件// in_localfilename 本地待发送的文件名// in_remotefilename 远程文件名// bCheckSize 文件传输完成后,是否核对本地和远程文件的大小,保证文件的完整性// 注意,文件在传输的过程中,采用临时文件命名的方法,即在in_remotefilename后加".tmp",在传输// 完成后才正式改为in_remotefilenamebool put(const char *in_localfilename,const char *in_remotefilename,const bool bCheckSize=true);// 删除ftp服务器上的文件bool ftpdelete(const char *in_remotefilename);// 把ftp服务器上的文件改名bool ftprename(const char *in_srcremotefilename,const char *in_dstremotefilename);// 获取服务器返回信息的最后一条 return a pointer to the last response receivedchar *response();
};
#endif

如下bool Cftp::mtime()中AddTime()在_public.cpp实现,注意"yyyymmddhh24miss"格式

// _ftp.cpp
#include "_ftp.h"
Cftp::Cftp()
{m_ftpconn=0;initdata();FtpInit();m_connectfailed=false;m_loginfailed=false;m_optionfailed=false;
}Cftp::~Cftp()
{logout();
}void Cftp::initdata()
{m_size=0;memset(m_mtime,0,sizeof(m_mtime));
}bool Cftp::login(const char *in_host,const char *in_username,const char *in_password,const int in_mode)
{if (m_ftpconn != 0) { FtpQuit(m_ftpconn); m_ftpconn=0; }m_connectfailed=m_loginfailed=m_optionfailed=false;if (FtpConnect(in_host,&m_ftpconn) == false)  { m_connectfailed=true; return false; }if (FtpLogin(in_username,in_password,m_ftpconn) == false)  { m_loginfailed=true; return false; }if (FtpOptions(FTPLIB_CONNMODE,(long)in_mode,m_ftpconn) == false) { m_optionfailed=true; return false; }return true;
}bool Cftp::logout()
{if (m_ftpconn == 0) return false;FtpQuit(m_ftpconn);m_ftpconn=0;return true;
}bool Cftp::get(const char *in_remotefilename,const char *in_localfilename,const bool bCheckMTime)
{if (m_ftpconn == 0) return false;// 创建本地文件目录MKDIR(in_localfilename);char strlocalfilenametmp[301];memset(strlocalfilenametmp,0,sizeof(strlocalfilenametmp));snprintf(strlocalfilenametmp,300,"%s.tmp",in_localfilename);// 获取远程服务器的文件的时间if (mtime(in_remotefilename) == false) return false;// 取文件if (FtpGet(strlocalfilenametmp,in_remotefilename,FTPLIB_IMAGE,m_ftpconn) == false) return false;// 判断文件获取前和获取后的时间,如果时间不同,表示文件已改变,返回失败if (bCheckMTime==false){char strmtime[21];strcpy(strmtime,m_mtime);if (mtime(in_remotefilename) == false) return false;if (strcmp(m_mtime,strmtime) != 0) return false;}// 重置文件时间UTime(strlocalfilenametmp,m_mtime);// 改为正式的文件if (rename(strlocalfilenametmp,in_localfilename) != 0) return false; // 获取文件的大小m_size=FileSize(in_localfilename);return true;
}bool Cftp::mtime(const char *in_remotefilename)
{if (m_ftpconn == 0) return false;memset(m_mtime,0,sizeof(m_mtime));char strmtime[21];memset(strmtime,0,sizeof(strmtime));if (FtpModDate(in_remotefilename,strmtime,14,m_ftpconn) == false) return false;AddTime(strmtime,m_mtime,0+8*60*60,"yyyymmddhh24miss");return true;
}bool Cftp::size(const char *in_remotefilename)
{if (m_ftpconn == 0) return false;m_size=0;if (FtpSize(in_remotefilename,&m_size,FTPLIB_IMAGE,m_ftpconn) == false) return false;return true;
}bool Cftp::site(const char *in_command)
{if (m_ftpconn == 0) return false;if (FtpSite(in_command,m_ftpconn) == false) return false;return true;
}bool Cftp::chdir(const char *in_remotedir)
{if (m_ftpconn == 0) return false;if (FtpChdir(in_remotedir,m_ftpconn) == false) return false;return true;
}bool Cftp::mkdir(const char *in_remotedir)
{if (m_ftpconn == 0) return false;if (FtpMkdir(in_remotedir,m_ftpconn) == false) return false;return true;
}bool Cftp::rmdir(const char *in_remotedir)
{if (m_ftpconn == 0) return false;if (FtpRmdir(in_remotedir,m_ftpconn) == false) return false;return true;
}bool Cftp::dir(const char *in_remotedir,const char *out_listfilename)
{if (m_ftpconn == 0) return false;if (FtpDir(out_listfilename,in_remotedir,m_ftpconn) == false) return false;return true;
}bool Cftp::nlist(const char *in_remotedir,const char *out_listfilename)
{if (m_ftpconn == 0) return false;// 创建本地文件目录MKDIR(out_listfilename);if (FtpNlst(out_listfilename,in_remotedir,m_ftpconn) == false) return false;return true;
}bool Cftp::put(const char *in_localfilename,const char *in_remotefilename,const bool bCheckSize)
{if (m_ftpconn == 0) return false;char strremotefilenametmp[301];memset(strremotefilenametmp,0,sizeof(strremotefilenametmp));snprintf(strremotefilenametmp,300,"%s.tmp",in_remotefilename);if (FtpPut(in_localfilename,strremotefilenametmp,FTPLIB_IMAGE,m_ftpconn) == false) return false;if (FtpRename(strremotefilenametmp,in_remotefilename,m_ftpconn) == false) return false;// 判断已上传的文件的大小与本地文件是否相同,确保上传成功。if (bCheckSize==true){if (size(in_remotefilename) == false) return false;if (m_size != FileSize(in_localfilename)) return false; }return true;
}bool Cftp::ftpdelete(const char *in_remotefilename)
{if (m_ftpconn == 0) return false;if (FtpDelete(in_remotefilename,m_ftpconn) == false) return false;return true;
}bool Cftp::ftprename(const char *in_srcremotefilename,const char *in_dstremotefilename)
{if (m_ftpconn == 0) return false;if (FtpRename(in_srcremotefilename,in_dstremotefilename,m_ftpconn) == false) return false;return true;
}char *Cftp::response()
{if (m_ftpconn == 0) return 0;return FtpLastResponse(m_ftpconn);
}

结构化数据:有记录有条数,操作数据库,传文件,调http接口。非结构化数据:图片,视频,word文档,ftp(数据中心中结构化数据用操作数据库推上云,非结构化数据用ftp推上云)。


上面strbuffer[…-1]只删除linux下\n换行符效果如下,window下\r不能删除,也可能会有两个\r。




如下获取到了服务器上文件时间,filename是相对路径,后面拼成绝对路径。

// 从ftp服务器上采集文件,ftpgetfiles.cpp,TCP客户端
#include"_public.h"
#include"_ftp.h"
struct st_arg
{char host[51];int  mode;char username[31];char password[31];char localpath[301];char remotepath[301];char matchname[301];int  ptype;   char remotepathbak[301];char listfilename[301];char okfilename[301];int  timetvl;
} starg;
/*
struct st_fileinfo
{ //在_public.h中定义了char filename[301];char mtime[21];
};
*/
vector<struct st_fileinfo> vokfilename,vokfilename1;
vector<struct st_fileinfo> vlistfile,vlistfile1;
Cftp ftp;
CLogFile  logfile;  //全局变量,日志操作类对象
//把nlist获取的文件名加载到vlistfile容器中
bool LoadListFile();
//把okfilename文件的内容加载到voklistname容器中
bool LoadOKFileName();
//比较vlistfile容器与vokfilename中的文件,得到新的两个容器
bool CompVector();
//把vokfilename1容器里的内容先写入okfilename文件中,覆盖之前的旧okfilename文件
bool WriteToOKFileName();
//ptype=1,把采集成功的文件追加到okfilename文件中
bool AppendToOKFileName(struct st_fileinfo *stfileinfo);
//退出信号的处理函数
void EXIT(int sig);
//本程序的业务流程的主函数
bool _ftpgetfile();
//显示文件的帮助
void _help(char *argv[]);
//把xml参数读取到starg结构体中
bool _xmltoarg(char *strxmlbuffer);int main(int argc,char *argv[])  //这样写,()里不为空则将命令行作为参数传入
{if(argc != 3){_help(argv); //argv是数组名也就是地址return -1;}//关闭全部的信号和输入输出CloseIOAndSignal();//处理程序退出的信号signal(SIGINT,EXIT);     //中断信号signal(SIGTERM,EXIT);     //kill信号  if(logfile.Open(argv[1],"a+") == false){logfile.Write("打开日志文件(%s)失败。\n",argv[1]); // logout会自动调用_ftp.h里析构函数,所以不用写return -1;}if(_xmltoarg(argv[2]) == false)return -1;while(true){if((ftp.login(starg.host,starg.username,starg.password,starg.mode)) == false){ logfile.Write("ftp.login(%s,%s,%s) failed.\n",starg.host,starg.username,starg.password);//return -1;sleep(starg.timetvl);continue;}    //logfile.Write("ftp.login ok.\n"); //本程序的业务流程的主函数_ftpgetfile();ftp.logout();sleep(starg.timetvl);}return 0;
}//111111111111111111111111111111111111111111111111111111111111.本程序的业务流程的主函数
bool _ftpgetfile()
{//先chdir进入目录,这样listfilename输出就不是全路径了,节省文件大小,减小网络开销if(ftp.chdir(starg.remotepath) == false) {logfile.Write("ftp.chdir(%s) failed.\n",starg.remotepath);return false;}//logfile.Write("chdir ok.\n");//nlist列出服务器目录文件并自动创建listfilename前面目录 。ftp.mtime比ftp.nlist更耗时//每次取时和listfilename做对比实现增量采集,已chdir进入目录所以用圆点(空和星号都可以)if(ftp.nlist(".",starg.listfilename) == false)   {logfile.Write("ftp.nlist(%s) failed.\n",starg.remotepath);return false;}//logfile.Write("nlist ok.\n");//把nlist获取的文件加载到vlistfile容器中,一般LoadListFile不会返回失败,但是里面有ftp.mtime联网可能会返回失败if(LoadListFile() == false){logfile.Write("LoadListFile failed.\n");return -1;}  //chdir(starg.localpath); //切换本地工作目录,如果这目录不存在就切换不成功,所以不用这方法,用绝对路径if(starg.ptype == 1){//okfilename是xml文件格式<filename>SURF_ZH_20190913131401_22226.txt</filename><mtime>20191026212900</mtime>//加载okfilename文件里的内容到容器vokfilename里LoadOKFileName();//把vlistfile容器中的文件与vokfilename中的文件对比,得到新的两个容器
//1.在vlistfile中存在,并已经采集成功的文件vokfilename1
//2.在vlistfile中存在,新文件(vlistfile有,vokfilename没有)或要重新采集(vlistfile与vokfilename文件时间不同)的文件vlistfile1CompVector();    //把vokfilename1容器里的内容先写入okfilename文件中,覆盖之前的旧okfilename文件WriteToOKFileName();    //把vlistfile1容器中的内容复制到vlistfile容器中让下面代码都可用//vlistfile.clear();vlistfile.swap(vlistfile1);}for(int i=0;i<vlistfile.size();i++){char strremotefilename[301],strlocalfilename[301]; //不切换本地工作目录了,定义两个变量拼成绝对路径  SNPRINTF(strlocalfilename,300,"%s/%s",starg.localpath,vlistfile[i].filename); SNPRINTF(strremotefilename,300,"%s/%s",starg.remotepath,vlistfile[i].filename);//vlistfile为string时,vlistfile[i].c_str()。为struct时push_back进去,vlistfile[i].filenamelogfile.Write("get %s ...",strremotefilename);//获取文件if(ftp.get(strremotefilename,strlocalfilename,true) == false ){logfile.WriteEx("failed.\n"); //WriteEx不写时间break;}logfile.WriteEx("ok.\n");//删除文件if (starg.ptype==2) ftp.ftpdelete(strremotefilename);    //转存到备份目录if(starg.ptype == 3){char strremotefilenamebak[301];  //remotepathbak不要自己创建SNPRINTF(strremotefilenamebak,300,"%s/%s",starg.remotepathbak,vlistfile[i].filename); //拼成ftp.ftprename(strremotefilename,strremotefilenamebak);}//ptype=1,把采集成功的文件追加到okfilename文件中if(starg.ptype == 1){AppendToOKFileName(&vlistfile[i]);}      }return true;
}
void EXIT(int sig)
{logfile.Write("程序退出,sig=%d\n",sig);exit(0);
}//1111111111111111111111111111111111111111111111111111111.1 加载对方服务器文件列表
bool LoadListFile() // 和crtsurfdata.cpp中LoadSTCode()差不多
{vlistfile.clear();CFile file;if(file.Open(starg.listfilename,"r") == false){logfile.Write("file.Open(%s) failed.\n",starg.listfilename);return false;}  struct st_fileinfo stfileinfo;while(true){memset(&stfileinfo,0,sizeof(stfileinfo));if(file.Fgets(stfileinfo.filename,300,true) == false) break;//或许放在对比完下载文件的时候更合适?if(MatchFileName(stfileinfo.filename,starg.matchname) == false)continue; //文件名不符合要求的话就不要if(starg.ptype == 1 ){//获取服务器文件时间  //这边可能会网络交互时返回失败,所以记日志 //只有ptype == 1才需要获取文件时间if(ftp.mtime(stfileinfo.filename) == false){logfile.Write("ftp.mtime(%s) failed.\n",stfileinfo.filename);return false;}strcpy(stfileinfo.mtime,ftp.m_mtime);  //m_mtime在_ftp.h中AddTime,mtime成员函数中}      vlistfile.push_back(stfileinfo);//logfile.Write("vlistfile filename=%s,mtime=%s\n",stfileinfo.filename,stfileinfo.mtime);}return true;
}//111111111111111111111111111111111111111111111111111.2 加载采集成功文件列表
bool LoadOKFileName()   //打开,读,解析到结构体,push进容器
{vokfilename.clear();CFile file;//如果程序是第一次采集,okfilename是不存在的,并不是错误,也返回trueif(file.Open(starg.okfilename,"r") == false)return true;struct st_fileinfo stfileinfo;char strbuffer[301];while(true){memset(&stfileinfo,0,sizeof(stfileinfo));if(file.Fgets(strbuffer,300,true) == false) break;GetXMLBuffer(strbuffer,"filename",stfileinfo.filename,300);GetXMLBuffer(strbuffer,"mtime",stfileinfo.mtime,20);vokfilename.push_back(stfileinfo);//logfile.Write("vokfilename filename=%s,mtime=%s\n",stfileinfo.filename,stfileinfo.mtime);}return true;
}//11111111111111111111111111111111111111.3 把vlistfile容器中的文件与vokfilename中的文件对比,得到新的两个容器
bool CompVector()
{vokfilename1.clear();vlistfile1.clear();for(int i=0;i<vlistfile.size();i++)  //这个循环得到vlistfile1{int j=0; //在外面定义下面可以用jfor(j=0;j<vokfilename.size();j++)  //这个循环得到vokfilename1{if( (strcmp(vlistfile[i].filename,vokfilename[j].filename)) == 0 &&(strcmp(vlistfile[i].mtime,vokfilename[j].mtime)) == 0 )  //文件名和时间等的话不需要再取{vokfilename1.push_back(vlistfile[i]); break; //不满足上面两个条件,break跳出}}//如下跳出for(j=0;j<vokfilename.size();j++)循环得到vlistfile1if(j == vokfilename.size()) //因为j<vokfilename.size(),所以这行意思是肯定没找到两个相等的vlistfile1.push_back(vlistfile[i]);   //if省略了{}}
/*//用于调试for(int i=0;i<vlistfile1.size();i++){logfile.Write("vlistfile1 filename=%s,mtime=%s\n",vlistfile1[i].filename,vlistfile1[i].mtime);}for(int i=0;i<vokfilename1.size();i++){logfile.Write("vokfilename1 filename=%s,mtime=%s\n",vokfilename1[i].filename,vokfilename1[i].mtime);}
*/return true;
}//11111111111111111111111111111.4 把vokfilename1容器里的内容先写入okfilename文件中,覆盖之前的旧okfilename文件
bool WriteToOKFileName()
{CFile file;if(file.Open(starg.okfilename,"w") == false){logfile.Write("file.Open(%s) failed.\n",starg.okfilename);return false;}  for(int i=0;i<vokfilename1.size();i++)  //通用固定写法{file.Fprintf("<filename>%s</filename><mtime>%s</mtime>\n",vokfilename1[i].filename,vokfilename1[i].mtime);}return true;
}//1111111111111111111111111111111111111111111111.5 ptype=1,把采集成功的文件追加到okfilename文件中
bool AppendToOKFileName(struct st_fileinfo *stfileinfo) //okfilename文件名是自己写进参数,一开始文件内容空
{CFile file;if(file.Open(starg.okfilename,"a") == false){logfile.Write("file.Open(%s) failed.\n",starg.okfilename);return false;}  // 如下写入一行,传入 *stfileinfo指针file.Fprintf("<filename>%s</filename><mtime>%s</mtime>\n",stfileinfo->filename,stfileinfo->mtime);return true;
}//111111111111111111111111111111111111111111111112.把xml参数读取到starg结构体中
bool _xmltoarg(char *strxmlbuffer)
{memset(&starg,0,sizeof(struct st_arg));GetXMLBuffer(strxmlbuffer,"host",starg.host);if(strlen(starg.host) == 0) { logfile.Write("host is null.\n"); return false;}GetXMLBuffer(strxmlbuffer,"mode",&starg.mode);if( (starg.mode != 1) && (starg.mode != 2) ) starg.mode = 1;GetXMLBuffer(strxmlbuffer,"username",starg.username);if(strlen(starg.username) == 0) { logfile.Write("username is null.\n"); return false;}GetXMLBuffer(strxmlbuffer,"password",starg.password);if(strlen(starg.password) == 0) { logfile.Write("password is null.\n"); return false;}GetXMLBuffer(strxmlbuffer,"localpath",starg.localpath);if(strlen(starg.localpath) == 0) { logfile.Write("localpath is null.\n"); return false;}GetXMLBuffer(strxmlbuffer,"remotepath",starg.remotepath);if(strlen(starg.remotepath) == 0) { logfile.Write("remotepath is null.\n"); return false;}GetXMLBuffer(strxmlbuffer,"matchname",starg.matchname);if(strlen(starg.matchname) == 0) { logfile.Write("matchname is null.\n"); return false;}GetXMLBuffer(strxmlbuffer,"ptype",&starg.ptype);if( (starg.ptype != 1) && (starg.ptype != 2) && (starg.ptype != 3)) { logfile.Write("ptype is error.\n"); return false;}GetXMLBuffer(strxmlbuffer,"remotepathbak",starg.remotepathbak);if( (starg.ptype == 3) && (strlen(starg.remotepathbak) == 0)) { logfile.Write("remotepathbak is null.\n"); return false;}GetXMLBuffer(strxmlbuffer,"listfilename",starg.listfilename);if(strlen(starg.listfilename) == 0) { logfile.Write("listfilename is null.\n"); return false;}GetXMLBuffer(strxmlbuffer,"okfilename",starg.okfilename);if( (starg.ptype == 1) && (strlen(starg.okfilename) == 0)) { logfile.Write("okfilename is null.\n"); return false;}GetXMLBuffer(strxmlbuffer,"timetvl",&starg.timetvl);if( (starg.timetvl) == 0) { logfile.Write("timetvl is null.\n"); return false;}return true;
}//111111111111111111111111111111111111111111111113.显示文件的帮助
void _help(char *argv[])
{printf("\n公共模块,从ftp服务器上采集文件。\n");printf("/root/qxidc/bin/ftpgetfile logfilename xmlbuffer \n\n");printf("例如:/root/qxidc/bin/ftpgetfile /root/qxidc/log/ftpgetfile.log \"<host>47.100.16.15:21</host><mode>1</mode><username>y</username><password>5199</password><localpath>/root/qxidc/data/surfdata</localpath><remotepath>/home/yu/surfdata</remotepath><matchname>SURF_*.txt,*.DAT</matchname><ptype>1</ptype><remotepathbak></remotepathbak><listfilename>/root/qxidc/ftplist/ftpgetfile_surfdata.list</listfilename><okfilename>/root/qxidc/ftplist/ftpgetfile_surfdata.xml</okfilename><timetvl>30</timetvl> \"\n\n");printf("logfilename  程序运行的日志文件名 \n");printf("xmlbuffer  文件传输的参数,如下: \n");printf("<host>118.31.74.14:21</host>  远程服务器的IP和端口 \n");printf("<mode>1</mode>  传输模式,1-pasv(被动),2-port(主动),缺省为1 \n");printf("<username>gu</username>  远程服务器的ftp用户名 \n");printf("<password>98</password>  远程服务器的ftp密码 \n");printf("<localpath>/home/gu/tmp/ftpget</localpath>  本地文件存放的目录 \n");printf("<remotepath>/home/y/</remotepath>  远程服务器文件存放的目录 \n");printf("<matchname>*.GIF</matchname>  待采集文件匹配的文件名,大写匹配,"\"不匹配的文件不会被采集,尽量精确,不允许采用*匹配全部文件\n");printf("<ptype>2</ptype>  采集之后,服务器处理方式:1-什么都不做(增量采集),2-删除,3-备份 \n");printf("<remotepathbak>/home/y/bak</remotepathbak>  远程服务器文件备份的目录,只有当ptype=3时才有效 \n");printf("<listfilename>/home/gu/qxidc/list/ftpgetfile_surfdata.list</listfilename> 采集器列出服务器文件名的清单 \n");printf("<okfilename>/home/gu/qxidc/list/ftpgetfile_surfdata.xml</okfilename> 已采集成功文件名清单 \n");printf("<timetvl>30</timetvl> 采集时间间隔 单位:s 一般大于 10 \n\n");//不能多个程序共用listfilename和okfilename
}

26.oracle安装:swap,dd

26.1 创建swap交换区:grep MemTotal /proc/meminfo (安装Oracle物理内存要求1024MB以上),grep SwapTotal /proc/meminfo


下面为添加交换区大小,此方法不限于centos,linux均适用,以下命令均需在root帐号下操作:
(1)先用free -m查看一下swap的大小。

(2)使用dd命令创建/home/swap这么一个分区文件。

(3)接着再把这个分区变成swap分区。

(4)再接着使用这个swap分区。使其成为有效状态。

(5)现在再用free -m命令查看一下内存和swap分区大小,就发现增加了2048M的空间了。创建的是2048,显示1999。

(6)修改/etc/fstab文件,让CentOS操作系统在每次重启时自动加载/home/swap交换区。上面执行错可以删除交换分区rm /home/swap,对了不用删。停止正在使用的swap分区:swapoff /home/swap。

26.2 安装依赖包及改系统核心参数:关闭一些系统对数据库的限制,pam.d,inittab

yum install -y binutils* compat-libstdc* elfutils-libelf* gcc* glibc* ksh* libaio* libgcc* libstdc* make* sysstat* libXp*  glibc-kernheaders
yum install -y ksh binutils compat-libstdc++-33 elfutils-libelf elfutils-libelf-devel gcc gcc-c++ glibc glibc-common glibc-devel libaio libaio-devel libgcc libstdc++ libstdc++-devel make numactl sysstat libXp unixODBC unixODBC-devel

1.vi /etc/sysctl.conf。
在文件最后增加以下行。
fs.file-max = 6815744
fs.aio-max-nr = 1048576
kernel.shmall = 2097152
kernel.shmmax= 2147483648
kernel.shmmni= 4096
kernel.sem = 250 32000100 128
net.ipv4.ip_local_port_range= 9000 65500
net.core.rmem_default= 262144
net.core.rmem_max= 4194304
net.core.wmem_default = 262144
net.core.wmem_max= 1048576
注意,kernel.shmmax参数的值为操作系统内存的一半,单位是字节。例如,装服务器的总物理内存如果是1024MB,那么kernel.shmmax的值应该是512乘1024乘1024=536870912,即kernel.shmmax = 536870912,其它的参数照抄。
2.vi /etc/security/limits.conf。
修改操作系统对oracle用户资源的限制。在该文件中添加如下行:
oracle soft nproc 2047
oracle hard nproc 16384
oracle soft nofile 1024
oracle hard nofile 65536
oracle hard stack 10240
3.vi /etc/pam.d/login。
session required /lib/security/pam_limits.so
4.vi /etc/profile。

if [ $USER = "oracle" ]; thenif [ $SHELL = "/bin/ksh" ]; thenulimit -p 16384ulimit -n 65536elseulimit -u 16384 -n 65536fi
fi

5.vi /etc/selinux/config,修改成 selinux=disabled。
6.关闭图形界面,vi /etc/inittab,把最后一行运行级别改为3,没有的话就不执行这一步。

7.重启服务器 #init 6 或 reboot。

26.3 创建oracle用户和组:.bash_profile。groupadd dba:增加dba组,相同等级的用户放一起就是一个组,只要对组设权限就行。groups 用户名:查看用户所在组。userdel(-r)用户名:删除用户或删除home文件夹里用户名文件夹

useradd -n oracle -g dba -d /oracle:增加oracle用户(这种方法创建oracle用户,会在根目录形成单独oracle文件夹与home和root文件夹平级,普通创建用户useradd y在根目录里的home目录里形成y文件夹)。
passwd oracle:修改oracle用户的密码。
cat /etc/passwd:查看所有用户除root,如下/etc/passwd中将用户名为qiang的用户赋予root权限。


// cat a.c
#include<stdio.h>
#include<pwd.h>
int main()
{struct passwd *pw; //定义指针pw记录返回值pw=getpwnam("root");printf("%s %s %d %d %s %s %s\n",pw->pw_name,pw->pw_passwd,pw->pw_uid,pw->pw_gid,pw->pw_gecos,pw->pw_dir,pw->pw_shell);  return 0;
}
// gcc a.c
// ./a.out
root x 0 0 root /root /bin/bash
// cat /etc/passwd
root:x:0:0:root:/root:/bin/bash

cat /etc/shadow:用户密码及相关属性。

// cat c.c
#include<stdio.h>
#include<shadow.h>
int main()
{struct spwd *spw;while((spw=getspent())!=NULL)printf("%s %s\n",spw->sp_namp,spw->sp_pwdp);endspent();return 0;
}
// gcc c.c
// ./a.out
root $1$GpBw2JM2$wdjExHPJ0qkM9lej7mzzq/
bin *
daemon *

cat /etc/group:查看所有用户组。

// cat b.c
#include<stdio.h>
#include<grp.h>
int main()
{struct group *grp;grp=getgrnam("root");printf("%s %s %d\n",grp->gr_name,grp->gr_passwd,grp->gr_gid);return 0;
}
// gcc b.c
// ./a.out
root x 0
// cat /etc/group
root:x:0:

从win本地上传oracle11g1.tgz压缩包到linux的tmp目录。用oracle用户登录,从根目录下开始解开压缩包,链接:https://pan.baidu.com/s/1Ywtv8zzRGzSCpwu9PyPobQ,提取码:ebk7 。

解压缩包后,一定要退出oracle用户exitctrl+d,否则oracle用户的环境变量不会生效。oracle11gR2.tgz解压后,会生成/oracle/.bash_profile文件,包括了Oracle数据库的安装参数,内容如下:

export ORACLE_BASE=/oracle/base
export ORACLE_HOME=/oracle/home
export ORACLE_SID=snorcl11g
export NLS_LANG='Simplified Chinese_China.ZHS16GBK'
export LD_LIBRARY_PATH=$ORACLE_HOME/lib:/usr/lib
export PATH=$PATH:$HOME/bin:$ORACLE_HOME/bin:.

26.4 数据库的启动和关闭:oracle用户登录,lsnrctl start/stop/status 启动/关闭网络监听服务,dbstart/dbshut 启动/关闭数据库

oracle数据库的启动和关闭配置成系统服务:在操作系统启动/关闭时自动启动/关闭Oracle实例和监听,以下都在root用户操作:
1.启动数据库实例的SQL脚本:vi /oracle/home/bin/dbstart,chmod +x

sqlplus / as sysdba <<EOF
startup;
EOF

2.vi /oracle/home/bin/dbrestart,chmod +x

sqlplus / as sysdba <<EOF
shutdown immediate;
startup;
EOF

3.vi /oracle/home/bin/dbshut,chmod +x

sqlplus / as sysdba <<EOF
shutdown immediate;
EOF

4.vi /usr/lib/systemd/system/oracle.service(/usr/lib文件夹里有一些.so文件,/systemd/system/oracle.service是自己创建的)

[Unit]
Description=Oracle RDBMS
After=network.target[Service]
Type=simple
ExecStart=/usr/bin/su - oracle -c "/oracle/home/bin/dbstart >> /tmp/oracle.log"
ExecReload=/usr/bin/su - oracle -c "/oracle/home/bin/dbrestart >> /tmp/oracle.log"
ExecStop=/usr/bin/su - oracle -c "/oracle/home/bin/dbshut >> /tmp/oracle.log"
RemainAfterExit=yes[Install]
WantedBy=multi-user.target

vi /usr/lib/systemd/system/lsnrctl.service

[Unit]
Description=Oracle RDBMS
After=network.target[Service]
Type=simple
ExecStart=/usr/bin/su - oracle -c "/oracle/home/bin/lsnrctl start >> /tmp/lsnrctl.log"
ExecReload=/usr/bin/su - oracle -c "/oracle/home/bin/lsnrctl reload >> /tmp/lsnrctl.log"
ExecStop=/usr/bin/su - oracle -c "/oracle/home/bin/lsnrctl stop >> /tmp/lsnrctl.log"
RemainAfterExit=yes[Install]
WantedBy=multi-user.target

5.如下命令可不执行。
systemctl daemon-reload # 重新加载服务配置文件
systemctl start oracle # 启动oracle服务。
systemctl restart oracle # 重启oracle服务。
systemctl stop oracle # 关闭oracle服务。
systemctl start lsnrctl # 启动lsnrctl服务。
systemctl restart lsnrctl # 重启lsnrctl服务。
systemctl stop lsnrctl # 关闭lsnrctl服务。
6.systemctl enable oracle # 把Oracle实例服务设置为开机自启动。
systemctl enable lsnrctl # 把Oracle监听服务设置为开机自启动。
7.Oracle实例启动的日志在/tmp/oracle.log文件中。监听的启动日成在/tmp/lsnrctl.log文件中。只有通过systemctl启动/关闭Oracle实例和监听才会写日志,手工执行脚本不写日志。

26.5 sqlplus命令行登录数据库:用oracle用户登录,执行sqlplus scott/tiger,以scott用户的身份登录

防火墙放开1521端口sqlplus登录数据库,centos7和centos6的防火墙设置不同,centos7采用以下方法:systemctl restart firewalld.service设完端口前后防火墙都重启下,firewall-cmd --list-ports查下放开的端口。
#firewall-cmd --zone=public --add-port=1521/tcp --permanent

启动监听:lsnrctl status,如下关闭防火墙:

26.6 plsql客户端登录数据库:远程连接需打开监听lsnrctl start

1.客户端环境:win64_11gR2_client链接:https://pan.baidu.com/s/1xLzrXAZm3xM-szds1IoQFw,提取码:mlo8 ,解压后点击set up应用程序进行安装。
2.客户端界面:链接:https://pan.baidu.com/s/1H9WIojcMbyqTBZe_goO-1Q 提取码:fp2u ,点击直接安装。
3.客户端参数配置文件
D:\app\w\product\11.2.0\client_1\network\admin\tnsnames.ora

snorcl11g_138 =(DESCRIPTION =(ADDRESS_LIST =(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.149.138)(PORT = 1521)))(CONNECT_DATA =(SID = snorcl11g)(SERVER = DEDICATED)))

如上是客户端,如下是服务端,参数内容与上相同,snorcl11g_138:是随便起的。

27.C语言操作oracle:trim,rc,库/头文件

27.1 makefile:-Wno-unused-variable变量未使用关闭警告(如int cc;下面未使用cc变量)

#oracle头文件路径
ORAINCL = -I$(ORACLE_HOME)/rdbms/public# oracle库文件路径  #-L指定库文件的搜索目录/oracle/home/lib
ORALIB =  -L$(ORACLE_HOME)/lib -L.# oracle的oci库  #-l指定链接库名libclntsh.so
ORALIBS = -lclntshCFLAGS = -g -Wno-write-strings -Wno-unused-variableall: createtable inserttable selecttable updatetable deletetable execplsqlcreatetable:createtable.cpp _ooci.h _ooci.cppg++ $(CFLAGS) -o createtable createtable.cpp $(ORAINCL) $(ORALIB) $(ORALIBS) _ooci.cppinserttable:inserttable.cpp _ooci.h _ooci.cppg++ $(CFLAGS) -o inserttable inserttable.cpp $(ORAINCL) $(ORALIB) $(ORALIBS) _ooci.cppselecttable:selecttable.cpp _ooci.h _ooci.cppg++ $(CFLAGS) -o selecttable selecttable.cpp $(ORAINCL) $(ORALIB) $(ORALIBS) _ooci.cppupdatetable:updatetable.cpp _ooci.h _ooci.cppg++ $(CFLAGS) -o updatetable updatetable.cpp $(ORAINCL) $(ORALIB) $(ORALIBS) _ooci.cppdeletetable:deletetable.cpp _ooci.h _ooci.cppg++ $(CFLAGS) -o deletetable deletetable.cpp $(ORAINCL) $(ORALIB) $(ORALIBS) _ooci.cppexecplsql:execplsql.cpp _ooci.h _ooci.cppg++ $(CFLAGS) -o execplsql execplsql.cpp $(ORAINCL) $(ORALIB) $(ORALIBS) _ooci.cpp

-Wno-write-string没有这行的话可将createtable.cpp中警告的字符串前加上(char*)就行。

还可以在声明时_ooci.h加const,_ooci.cpp中也要修改(不建议)。

如下-g是可调试。

如下全部采用标准写法:头路径,库路径+库名


如下采用原始不含头文件编译方法错误。


如下采用ORACLE_HOME换用户时只需要添加该用户.bash_profile中export…进env,不需要改makefile。



下面是文件:


如下程序只要链接了动态库就需要LD…。

27.2 createtable.cpp:setenv/putenv设置环境变量,char补空格,varchar不补空格。确定长度用char快,varchar2(4000)用到几个字符自动分配几个而不是一下给4000字符

#include "_ooci.h"
int main(int argc,char *argv[])
{//11111111111111111111111111111111111111111111111111111111.数据库连接池类connection conn;// 连接数据库,返回值0-成功,其它-失败// 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。if (conn.connecttodb("scott/tiger@snorcl11g_138","Simplified Chinese_China.ZHS16GBK") != 0){printf("connect database failed.\n%s\n",conn.m_cda.message); return -1;}//11111111111111111111111111111111111111111111111112.SQL语言操作类sqlstatement stmt(&conn);  //不需要判断返回值// 准备创建表的SQL,商品表:商品编号id,商品名称name,价格sal,入库时间btime,商品说明memo,商品图片pic// prepare方法不需要判断返回值stmt.prepare("\create table goods(id    number(10),\name  varchar2(30),\sal   number(10,2),\btime date,\memo  clob,\pic   blob,\primary key (id))");//111111111111111111111111111111111111111111111113.执行SQL语句,一定要判断返回值,0-成功,其它-失败if (stmt.execute() != 0){printf("stmt.execute() failed.\n%s\n%s\n",stmt.m_sql,stmt.m_cda.message); return -1;}printf("create table goods ok.\n");return 0;
}


如下trim(c1)去除c1的空格,最后一行不加trim是查不出下面表格。CLOB:结构化数据如文本信息。BLOB:非结构化数据。

27.3 inserttable.cpp:truncate删除表中全部数据,不产生事务

// 本程序演示向商品表中插入10条记录。
#include "_ooci.h"
// 定义用于操作数据的结构,与表中的字段对应
struct st_GOODS  //结构体值与数据库中表即与createtable.cpp中表对应
{long id;          // 商品编号,用long数据类型对应oracle无小数的numberchar name[31];    // 商品名称,用char对应oracle的varchar2,注意,表中字段的长度是30,char定义的长度是31,要留C语言的结束符double sal;       // 商品价格,用double数据类型对应oracle有小数的numberchar btime[20];   // 入库时间,用char对应oracle的date,格式可以在SQL语句中指定,本程序将指定为yyyy-mm-dd hh24:mi:ss
} stgoods;int main(int argc,char *argv[])
{//111111111111111111111111111111111111111111111111.数据库连接池connection conn;// 连接数据库,返回值0-成功,其它-失败// 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。if (conn.connecttodb("scott/tiger@snorcl11g_138","Simplified Chinese_China.ZHS16GBK") != 0){printf("connect database failed.\n%s\n",conn.m_cda.message); return -1;}//111111111111111111111111111111111111111111111112.SQL语言操作类sqlstatement stmt(&conn);// 准备插入数据的SQL,不需要判断返回值stmt.prepare("\insert into goods(id,name,sal,btime) \values(:1,:2,:3,to_date(:4,'yyyy-mm-dd hh24:mi:ss'))");//to_date(:4,'yyyy-mm-dd hh24:mi:ss')可为null//goods(id,name,sal,btime)字段在createtable.cpp已创建// 为SQL语句绑定输入变量的地址,行stmt.bindin(1,&stgoods.id);//bindin中的1对应上面:1stmt.bindin(2, stgoods.name,30); //30为长度,char是这样绑定stmt.bindin(3,&stgoods.sal);  //此时stgoods.id结构体变量们还未赋值stmt.bindin(4, stgoods.btime,19);//111111111111111111111111111111111111111111111113.模拟商品数据,向表中插入10条测试信息for (int ii=1;ii<=10;ii++){// 结构体变量初始化memset(&stgoods,0,sizeof(stgoods));// 为结构体的变量赋值stgoods.id=ii;sprintf(stgoods.name,"商品名称%02d",ii);stgoods.sal=ii*2.11;strcpy(stgoods.btime,"2018-03-01 12:25:31");// 每次指定变量的值后,执行SQL语句,一定要判断返回值,0-成功,其它-失败。if (stmt.execute() != 0) //每执行一次把变量值插入表,返回结构体m_cda.rc{printf("stmt.execute() failed.\n%s\n%s\n",stmt.m_sql,stmt.m_cda.message); return -1;}printf("insert ok(id=%d,rpc=%ld).\n",ii,stmt.m_cda.rpc); //rpc为行数}printf("insert table goods ok.\n");// 提交数据库事务,不提交的话程序退出,缺省回滚事务conn.commit();return 0;
}



注释//conn.commit();则insert后查询无记录,如下开启自动提交。

27.4 selecttable.cpp:stmt.m_cda.rpc变量保存了SQL执行后影响的记录数

//本程序演示从商品表中查询数据
#include "_ooci.h"
// 定义用于查询数据的结构,与表中的字段对应
struct st_GOODS  //和inserttable.cpp中结构体一样
{long id;          // 商品编号,用long数据类型对应oracle无小数的numberchar name[31];    // 商品名称,用char对应oracle的varchar2,注意,表中字段的长度是30,char定义的长度是31,要留C语言的结束符double sal;       // 商品价格,用double数据类型对应oracle有小数的numberchar btime[20];   // 入库时间,用char对应oracle的date,格式可以在SQL语句中指定,本程序将指定为yyyy-mm-dd hh24:mi:ss
} stgoods;int main(int argc,char *argv[])
{//111111111111111111111111111111111111111111111111.数据库连接池connection conn; // 连接数据库,返回值0-成功,其它-失败// 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中if (conn.connecttodb("scott/tiger@snorcl11g_138","Simplified Chinese_China.ZHS16GBK") != 0){printf("connect database failed.\n%s\n",conn.m_cda.message); return -1;}//111111111111111111111111111111111111111111111112.SQL语言操作类sqlstatement stmt(&conn);int iminid,imaxid;// 准备查询数据的SQL,不需要判断返回值stmt.prepare("\select id,name,sal,to_char(btime,'yyyy-mm-dd hh24:mi:ss') from goods where id>:1 and id<:2");//id,name,sal,to_char(btime,'yyyy-mm-dd hh24:mi:ss')是输出变量//id>:1 and id<:2是输入变量  // 为SQL语句绑定输入变量的地址stmt.bindin(1,&iminid);stmt.bindin(2,&imaxid);// 为SQL语句绑定输出变量的地址// stgoods.id这些变量在inserttable已经赋过值stmt.bindout(1,&stgoods.id); stmt.bindout(2, stgoods.name,30);stmt.bindout(3,&stgoods.sal);stmt.bindout(4, stgoods.btime,19);// 手工指定id的范围为1到5,执行一次查询 //inserttable已插入10条记录iminid=1;imaxid=5;//111111111111111111111111111111111111111111111113.执行SQL语句,一定要判断返回值,0-成功,其它-失败if (stmt.execute() != 0){printf("stmt.execute() failed.\n%s\n%s\n",stmt.m_sql,stmt.m_cda.message); return -1;}//上面执行完sql语句就得到一个结果集,怎么获取这结果集呢?用next方法while (1){// 先把结构体变量初始化,然后才获取记录memset(&stgoods,0,sizeof(stgoods));// 获取一条记录,一定要判断返回值,0-成功,1403-无记录,其它-失败// 在实际应用中,除了0和1403,其它的情况极少出现。if (stmt.next() !=0) break;//每next一次就从结果集里拿一条数据    // 把获取到的记录的值打印出来printf("id=%ld,name=%s,sal=%.02f,btime=%s\n",stgoods.id,stgoods.name,stgoods.sal,stgoods.btime);}printf("本次查询了goods表%ld条记录。\n",stmt.m_cda.rpc);  return 0;
}

27.5 updatetable.cpp:连接数据库,返回值0成功,其它失败,失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中

// 本程序演示更新商品表中数据
#include "_ooci.h"
int main(int argc,char *argv[])
{//111111111111111111111111111111111111111111111111.数据库连接池connection conn;  if (conn.connecttodb("scott/tiger@snorcl11g_138","Simplified Chinese_China.ZHS16GBK") != 0){printf("connect database failed.\n%s\n",conn.m_cda.message); return -1;}//111111111111111111111111111111111111111111111112.SQL语言操作类sqlstatement stmt(&conn);int iminid,imaxid;char strbtime[20];// 准备更新数据的SQL,不需要判断返回值stmt.prepare("\update goods set btime=to_date(:1,'yyyy-mm-dd hh24:mi:ss') where id>:2 and id<:3");// 为SQL语句绑定输入变量的地址stmt.bindin(1, strbtime,19);  //1对应上面:1stmt.bindin(2,&iminid);  //2对应上面:2stmt.bindin(3,&imaxid);// 手工指定id的范围为1到5,btime为2017-12-20 09:45:30,执行一次更新iminid=1;imaxid=5;memset(strbtime,0,sizeof(strbtime));strcpy(strbtime,"2017-12-20 09:45:30");//111111111111111111111111111111111111111111111113.执行SQL语句,一定要判断返回值,0-成功,其它-失败。if (stmt.execute() != 0){printf("stmt.execute() failed.\n%s\n%s\n",stmt.m_sql,stmt.m_cda.message); return -1;}// 请注意,stmt.m_cda.rpc变量非常重要,它保存了SQL被执行后影响的记录数。printf("本次更新了goods表%ld条记录。\n",stmt.m_cda.rpc);// 提交事务conn.commit();return 0;
}

27.6 deletetable.cpp:删除商品表中数据

#include "_ooci.h"
int main(int argc,char *argv[])
{//111111111111111111111111111111111111111111111111.数据库连接池connection conn;  if (conn.connecttodb("scott/tiger@snorcl11g_138","Simplified Chinese_China.ZHS16GBK") != 0){printf("connect database failed.\n%s\n",conn.m_cda.message); return -1;}//111111111111111111111111111111111111111111111112.SQL语言操作类sqlstatement stmt(&conn);int iminid,imaxid;  // 准备删除数据的SQL,不需要判断返回值stmt.prepare("delete from goods where id>:1 and id<:2");// 为SQL语句绑定输入变量的地址stmt.bindin(1,&iminid);stmt.bindin(2,&imaxid);// 手工指定id的范围为1到5iminid=1;imaxid=5;//111111111111111111111111111111111111111111113.执行SQL语句,一定要判断返回值,0-成功,其它-失败if (stmt.execute() != 0){printf("stmt.execute() failed.\n%s\n%s\n",stmt.m_sql,stmt.m_cda.message); return -1;}printf("本次从goods表中删除了%ld条记录。\n",stmt.m_cda.rpc); conn.commit();return 0;
}

27.7 execplsql.cpp:执行一个PL/SQL过程

// execplsql.cpp:其中sql:删除全部记录并插入一行
// 在这里说一下我个人的意见,我从不在Oracle数据库中创建PL/SQL过程,也很少使用触发器,原因如下:
// 1、在Oracle数据库中创建PL/SQL过程,程序的调试很麻烦;
// 2、维护工作很麻烦,因为程序员要花时间去了解数据库中的存储过程;
// 3、在封装的oci中,对oracle的操作已经是简单的事情,没必要去折腾存储过程;
// 在oci中也很少用PL/SQL语句,也是因为复杂的PL/SQL调试麻烦。
#include "_ooci.h"int main(int argc,char *argv[])
{//111111111111111111111111111111111111111111111111.数据库连接池connection conn;  // 连接数据库,返回值0-成功,其它-失败// 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。if (conn.connecttodb("scott/tiger@snorcl11g_138","Simplified Chinese_China.ZHS16GBK") != 0){printf("connect database failed.\n%s\n",conn.m_cda.message); return -1;}//111111111111111111111111111111111111111111111112.SQL语言操作类sqlstatement stmt(&conn);// 准备删除记录的PL/SQL,不需要判断返回值// 本PL/SQL先删除goods表中的全部记录,再插入一条记录stmt.prepare("\BEGIN\delete from goods;\insert into goods(id,name,sal,btime)\values(:1,'过程商品',55.65,to_date('2018-01-02 13:00:55','yyyy-mm-dd hh24:mi:ss'));\END;");//上面2条sql语句,:1是为了绑定变量,:1换成100就没有下面两行了int id=100;stmt.bindin(1,&id);// 注意,PL/SQL中的每条SQL需要用分号结束,END之后还有一个分号。//111111111111111111111111111111111111111111111113.执行SQL语句,一定要判断返回值,0-成功,其它-失败if (stmt.execute() != 0){printf("stmt.execute() failed.\n%s\n%s\n",stmt.m_sql,stmt.m_cda.message); return -1;}printf("exec PL/SQL ok.\n");conn.commit();return 0;
}

28.虚表/日期/序列:ntp&hwclock

oracle的虚表dual用来构成select的语法规则,不要当表来看,oracle保证dual里永远只有一条记录,可用它来做如下很多事。


如下若是c语言写1/86400就是=0(因为整数除),只有浮点数才能这样运算。oracle中是非常精确的,不用担心精度问题。

如下sysdate函数返回值可以当成值来用。






SQL>CREATE SEQUENCE ABC INCREMENT BY 1 START WITH 1 MAXVALUE 9999999999 NOCYCLE NOCACHE;

如上新建一个序列第一次取当前值是取不到的(提示该序列在此会话中未定义),必须.nextval再.currval取当前值。下面起步为100,步长为5。

下面给表增加两个字段crttime和keyid(导出数据时用到keyid),下面在crttable.sql中。

rowid指物理位置。rownum指序号(C语言很少用rownum)。如下第一行select时需要将rownum和rowid写出来查询才能看到,第二行用rowid作为查询或更新的条件最快(区别索引)

如上每条记录虽然存在数据库里(即表空间,表空间还是数据文件)最后还是存在磁盘上(在磁盘上就有位置),就是/oracle/base/oradata/snorcl11g里的.dbf文件。

29.索引/视图/链路/同义词:create index/view/link/synonym,distinct


ddatetime在数据库中是整数

视图就是访问数据的一个窗口,当成一个表来用,下面为先授权。

如上可以exit退出,也可直接切换connect scott用户,drop view …


视图不占用表空间,在emp将hiredate设置为系统时间,从v_emp查也有效果。








下面是同义词:数据库对象(表,视图,序列,存储过程,包)的一个别名。实际不可能给shqx和密码给客户,给普通用户如scott给客户并授权。


对表使用别名,如下内表是dept,外表是emp。

distinct关键字,取结果集中的唯一值,去重,如下有很多30,20,10重复。

30.系统高可用性:12个1T组合,rac


如下假如oracle数据库在一个服务器上运行,出问题了怎么办?(一般硬件故障)。Oracle三种高可用集群方案:1.RAC,2.Data Guard,3.OGG。如下是RAC技术,可以多个,但一般为两个。有的数据在内存,有的在缓存,2个要进行数据协调很麻烦,所以不是高性能,比单个服务器要慢。

如下为结合三种集群技术应用。

RAC有多个节点(上面为两个),应用程序只要做好客户端配置怎么连RAC数据库就行。

31.txt/xml文件入表:结构体内容

//psurfdata.cpp,txt文件入库封成类
#include "_public.h"
#include "_ooci.h"
#include "_shqx.h"
CLogFile logfile;
CDir Dir;
bool _psurfdata();
connection conn; //实例化对象,con也叫变量(称呼)
void EXIT(int sig);int main(int argc,char *argv[])
{if (argc!=5){printf("\n本程序用于处理全国气象站点观测的分钟数据,并保存到数据库的T_SURFDATA表中。\n");printf("这是完善后的程序,未完善的程序在psurfdata_old.cpp中。\n");printf("/htidc/shqx/bin/psurfdata 数据文件存放的目录 日志文件名 数据库连接参数 程序运行时间间隔\n");printf("例如:/htidc/shqx/bin/psurfdata /data/shqx/sdata/surfdata /log/shqx/psurfdata.log shqx/pwdidc@snorcl11g_198 10\n");return -1;}CloseIOAndSignal();signal(SIGINT,EXIT); signal(SIGTERM,EXIT);if (logfile.Open(argv[2],"a+")==false){printf("打开日志文件失败(%s)。\n",argv[2]); return -1;}logfile.Write("程序启动。\n");while (true){
//111111111111111111111111111111111111111111111扫描数据文件存放的目录,只匹配"SURF_ZH_*.txt"// logfile.Write("开始扫描目录。\n");if (Dir.OpenDir(argv[1],"SURF_ZH_*.txt",1000,true,true)==false){logfile.Write("Dir.OpenDir(%s) failed.\n",argv[1]); sleep(atoi(argv[4])); continue;}//11111111111111111111111111111111111111111111111连接数据库while (true){if (Dir.ReadDir()==false) break;  if (conn.m_state==0){if (conn.connecttodb(argv[3],"Simplified Chinese_China.ZHS16GBK")!=0){logfile.Write("connect database(%s) failed.\n%s\n",argv[3],conn.m_cda.message); break;}// logfile.Write("连接数据库成功。\n");}  logfile.Write("开始处理文件%s...",Dir.m_FileName);//111111111111111111111111111111111111111111111111处理入库if (_psurfdata()==false) {logfile.WriteEx("失败。\n"); break;}}if (conn.m_state==1) conn.disconnect(); // 断开与数据库的连接sleep(atoi(argv[4]));}return 0;
}
void EXIT(int sig)
{logfile.Write("程序退出,sig=%d\n\n",sig);exit(0);
}//11111111111111111111111111111111111111111111111处理入库
bool _psurfdata()
{CFile File;if (File.Open(Dir.m_FullFileName,"r")==false){logfile.Write("(File.Open(%s) failed.\n",Dir.m_FullFileName); return false;}//1111111111111111111111111111读取文件中的每一行记录,写入数据库的表中CSURFDATA SURFDATA(&conn,&logfile); //上行给m_conn和m_logfile两个指针成员变量赋值初始化也可写成如下两行://SURFDATA.m_conn=&conn;  //con是对象也是变量//SURFDATA.m_logfile=&logfile;char strBuffer[301];                                           while (true){memset(strBuffer,0,sizeof(strBuffer));if (File.Fgets(strBuffer,300,true)==false) break; //从文件中获取一行记录   if (SURFDATA.SplitBuffer(strBuffer)==false) { logfile.Write("%s\n",strBuffer); continue; }// 把用逗号分隔的记录拆分到结构体中long rc=SURFDATA.InsertTable(); //把结构体中的数据更新到T_SURFDATA表中,因为不知道返回哪个sql,所以用long rc =if ( (rc>=3113) && (rc<=3115) ) return false; //只要不是数据库session的错误,程序就继续。if (rc != 0) { logfile.Write("%s\n",strBuffer); continue; }}conn.commit(); //提交事务File.CloseAndRemove(); //关闭文件指针,并删除文件logfile.WriteEx("成功(total=%d,insert=%d,update=%d,invalid=%d)。\n",SURFDATA.totalcount,SURFDATA.insertcount,SURFDATA.updatecount,SURFDATA.invalidcount);return true;
}
//psurfdata1.cpp本程序只支持xml文件入库已封装成类,有keyid等字段。
//表加字段,结构体不用,在sqlplus中输入一行创建序列命令,stcode.ini不要最后留空行
#include "_public.h"
#include "_ooci.h"
#include "_shqx.h"
CLogFile logfile;
CDir Dir;
bool _psurfdata();
connection conn;
void EXIT(int sig);int main(int argc,char *argv[])
{if (argc!=5){printf("\n本程序用于处理全国气象站点观测的分钟数据,并保存到数据库的T_SURFDATA表中。\n");printf("与psurfdata.cpp不同,本程序只支持xml格式。\n");printf("/htidc/shqx/bin/psurfdata1 数据文件存放的目录 日志文件名 数据库连接参数 程序运行时间间隔\n");printf("例如:/htidc/shqx/bin/psurfdata1 /data/shqx/sdata/surfdata /log/shqx/psurfdata1.log shqx/pwdidc@snorcl11g_198 10\n");return -1;}CloseIOAndSignal();signal(SIGINT,EXIT); signal(SIGTERM,EXIT);if (logfile.Open(argv[2],"a+")==false){printf("打开日志文件失败(%s)。\n",argv[2]); return -1;}logfile.Write("程序启动。\n");while (true){
//1111111111111111111111111111111111111111111扫描数据文件存放的目录,匹配"SURF_ZH_*.xml"// logfile.Write("开始扫描目录。\n");if (Dir.OpenDir(argv[1],"SURF_ZH_*.xml",1000,true,true)==false){logfile.Write("Dir.OpenDir(%s) failed.\n",argv[1]); sleep(atoi(argv[4])); continue;}//111111111111111111111111111111111111111111111逐个处理目录中的数据文件while (true){if (Dir.ReadDir()==false) break;//111111111111111111111111111111111111111111111连接数据库if (conn.m_state==0){if (conn.connecttodb(argv[3],"Simplified Chinese_China.ZHS16GBK")!=0){logfile.Write("connect database(%s) failed.\n%s\n",argv[3],conn.m_cda.message); break;}// logfile.Write("连接数据库成功。\n");}  logfile.Write("开始处理文件%s...",Dir.m_FileName);//111111111111111111111111111111111111111111111处理入库if (_psurfdata()==false) {logfile.WriteEx("失败。\n"); break;}}if (conn.m_state==1) conn.disconnect(); sleep(atoi(argv[4]));}return 0;
}
void EXIT(int sig)
{logfile.Write("程序退出,sig=%d\n\n",sig);exit(0);
}//111111111111111111111111111111111111111111111处理入库
bool _psurfdata()
{CFile File;if (File.Open(Dir.m_FullFileName,"r")==false){logfile.Write("(File.Open(%s) failed.\n",Dir.m_FullFileName); return false;}CSURFDATA SURFDATA(&conn,&logfile);char strBuffer[301];while (true){memset(strBuffer,0,sizeof(strBuffer));if (File.FFGETS(strBuffer,300,"<endl/>")==false) break;// logfile.Write("str=%s=\n",strBuffer);// logfile.Write("%s\n",strBuffer);    if (SURFDATA.SplitBuffer1(strBuffer)==false) { logfile.Write("%s\n",strBuffer); continue; }long rc=SURFDATA.InsertTable();    if ( (rc>=3113) && (rc<=3115) ) return false; // 只要不是数据库session的错误,程序就继续。if (rc != 0) { logfile.Write("%s\n",strBuffer); continue; }}conn.commit();  File.CloseAndRemove();  //关闭文件指针,并删除文件logfile.WriteEx("成功(total=%d,insert=%d,update=%d,invalid=%d)。\n",SURFDATA.totalcount,SURFDATA.insertcount,SURFDATA.updatecount,SURFDATA.invalidcount);return true;
}
//_shqx.h
#ifndef _SHQX_H
#define _SHQX_H
#include "_public.h"
#include "_ooci.h"
struct st_stcode  //全国气象站点参数数据结构
{char provname[31];   // 省名称char obtid[11];      // 站点代码char cityname[31];   // 城市名double lat;          // 纬度double lon;          // 经度double height;       // 海拔高度
};
struct st_surfdata  //全国气象站点分钟观测数据结构
{char obtid[11];      // 站点代码char ddatetime[21];  // 数据时间:格式yyyy-mm-dd hh:mi:ss。int  t;              // 气温:单位,0.1摄氏度int  p;              // 气压:0.1百帕int  u;              // 相对湿度,0-100之间的值。int  wd;             // 风向,0-360之间的值。int  wf;             // 风速:单位0.1m/sint  r;              // 降雨量:0.1mmint  vis;            // 能见度:0.1米
};
struct st_signallog  //分区信号数据结构
{char obtid[11];char ddatetime[20];char signalname[2];char signalcolor[2];
};//111111111111111111111111111111111111111111111111CSURFDATA类
class CSURFDATA
{public:int totalcount,insertcount,updatecount,invalidcount;  // 记录总数据、插入数、更新数、无效记录数。struct st_surfdata m_stsurfdata;CSURFDATA(connection *conn,CLogFile *logfile); //在构造函数里传进参数~CSURFDATA();void initdata();  // 数据初始化connection *m_conn; //在类里操作数据库需要一个指针,m_conn,m_logfile这两个成员需要给它们赋值CLogFile   *m_logfile; //在类里写日志,m_logfile->,类里不能再类实例化,所以定义为指针int iccount; //不能定义到成员函数里值会变sqlstatement stmtsel,stmtins,stmtupt;// 把用逗号分隔的记录拆分到m_stsurfdata结构中。bool SplitBuffer(const char *strBuffer);  // 把xml格式的记录拆分到m_stsurfdata结构中。bool SplitBuffer1(const char *strBuffer);// 把m_stsurfdata结构中的值更新到T_SURFDATA表中。long InsertTable();
};//1111111111111111111111111111111111111111111111CSIGNALLOG类
class CSIGNALLOG
{public:int totalcount,insertcount,updatecount,invalidcount;  // 记录总数据、插入数、更新数、无效记录数。struct st_signallog m_stsignallog;vector<struct st_signallog> vsignallog;   // 容器存放一个文件的全部记录CSIGNALLOG(connection *conn,CLogFile *logfile);~CSIGNALLOG();void initdata();  // 数据初始化connection *m_conn;CLogFile   *m_logfile;int iccount;sqlstatement stmtsel,stmtins,stmtupt; bool SplitBuffer(const char *strBuffer);  // 把记录拆分到vsignallog容器中。  long InsertTable(); // 把vsignallog容器中的值更新到T_SIGNALDATA表中。
};//11111111111111111111111111111111111111111把非结构化数据文件写入oracle数据库的表中
int FileToTable(connection *in_conn,CLogFile *in_logfile,char *in_tname,char *in_filename,char *in_ddatetime);
#endif
//_shqx.cpp
#include "_shqx.h"
//11111111111111111111111111111111111111111111111111111CSURFDATA类
CSURFDATA::CSURFDATA(connection *conn,CLogFile *logfile)
{initdata();        // 构造函数里传入两个指针变量并赋初值m_conn=conn; m_logfile=logfile;  // 所以调用CSURFDATA(&conn,&logfile);就能完成初始化
}
void CSURFDATA::initdata()
{totalcount=insertcount=updatecount=invalidcount=0;m_conn=0; m_logfile=0;memset(&m_stsurfdata,0,sizeof(struct st_surfdata));
}
CSURFDATA::~CSURFDATA()
{}bool CSURFDATA::SplitBuffer(const char *strBuffer) //把用逗号分隔的记录拆分到m_stsurfdata结构中
{ totalcount++;memset(&m_stsurfdata,0,sizeof(struct st_surfdata));CCmdStr CmdStr;CmdStr.SplitToCmd(strBuffer,",",true);if (CmdStr.CmdCount()!=9) { invalidcount++; return false; }CmdStr.GetValue(0,m_stsurfdata.obtid,5);      // 站点代码CmdStr.GetValue(1,m_stsurfdata.ddatetime,19); // 数据时间:格式yyyy-mm-dd hh:mi:ss。double dtmp=0;CmdStr.GetValue(2,&dtmp); m_stsurfdata.t=(int)(dtmp*10);  // 气温:单位,0.1摄氏度CmdStr.GetValue(3,&dtmp); m_stsurfdata.p=(int)(dtmp*10);  // 气压:0.1百帕CmdStr.GetValue(4,&m_stsurfdata.u);  // 相对湿度,0-100之间的值。CmdStr.GetValue(5,&m_stsurfdata.wd); // 风向,0-360之间的值。CmdStr.GetValue(6,&dtmp); m_stsurfdata.wf=(int)(dtmp*10);  // 风速:单位0.1m/sCmdStr.GetValue(7,&dtmp); m_stsurfdata.r=(int)(dtmp*10);   // 降雨量:0.1mmCmdStr.GetValue(8,&dtmp); m_stsurfdata.vis=(int)(dtmp*10); // 能见度:0.1米return true;
}bool CSURFDATA::SplitBuffer1(const char *strBuffer) //把xml格式的记录拆分到m_stsurfdata结构中
{totalcount++;memset(&m_stsurfdata,0,sizeof(struct st_surfdata));GetXMLBuffer(strBuffer,"obtid",m_stsurfdata.obtid,5);      // 站点代码GetXMLBuffer(strBuffer,"ddatetime",m_stsurfdata.ddatetime,19); // 数据时间:格式yyyy-mm-dd hh:mi:ss。double dtmp=0;GetXMLBuffer(strBuffer,"t",&dtmp); m_stsurfdata.t=(int)(dtmp*10);  // 气温:单位,0.1摄氏度GetXMLBuffer(strBuffer,"p",&dtmp); m_stsurfdata.p=(int)(dtmp*10);  // 气压:0.1百帕GetXMLBuffer(strBuffer,"u",&m_stsurfdata.u);  // 相对湿度,0-100之间的值。GetXMLBuffer(strBuffer,"wd",&m_stsurfdata.wd);  // 风向,0-360之间的值。GetXMLBuffer(strBuffer,"wf",&dtmp); m_stsurfdata.wf=(int)(dtmp*10);  // 风速:单位0.1m/sGetXMLBuffer(strBuffer,"r",&dtmp); m_stsurfdata.r=(int)(dtmp*10);   // 降雨量:0.1mmGetXMLBuffer(strBuffer,"vis",&dtmp);  m_stsurfdata.vis=(int)(dtmp*10); // 能见度:0.1米return true;
}long CSURFDATA::InsertTable() //把m_stsurfdata结构中的值更新到T_SURFDATA表中
{if (stmtsel.m_state==0){stmtsel.connect(m_conn);stmtsel.prepare("select count(*) from T_SURFDATA where obtid=:1 and ddatetime=to_date(:2,'yyyy-mm-dd hh24:mi:ss')");stmtsel.bindin( 1, m_stsurfdata.obtid,5);stmtsel.bindin( 2, m_stsurfdata.ddatetime,19);stmtsel.bindout(1,&iccount);}if (stmtins.m_state==0){stmtins.connect(m_conn);stmtins.prepare("insert into T_SURFDATA(obtid,ddatetime,t,p,u,wd,wf,r,vis,crttime,keyid) values(:1,to_date(:2,'yyyy-mm-dd hh24:mi:ss'),:3,:4,:5,:6,:7,:8,:9,sysdate,SEQ_SURFDATA.nextval)");stmtins.bindin( 1, m_stsurfdata.obtid,5);stmtins.bindin( 2, m_stsurfdata.ddatetime,19);stmtins.bindin( 3,&m_stsurfdata.t);stmtins.bindin( 4,&m_stsurfdata.p);stmtins.bindin( 5,&m_stsurfdata.u);stmtins.bindin( 6,&m_stsurfdata.wd);stmtins.bindin( 7,&m_stsurfdata.wf);stmtins.bindin( 8,&m_stsurfdata.r);stmtins.bindin( 9,&m_stsurfdata.vis);}if (stmtupt.m_state==0){stmtupt.connect(m_conn);stmtupt.prepare("update T_SURFDATA set t=:1,p=:2,u=:3,wd=:4,wf=:5,r=:6,vis=:7 where obtid=:8 and ddatetime=to_date(:2,'yyyy-mm-dd hh24:mi:ss')");stmtupt.bindin( 1,&m_stsurfdata.t);stmtupt.bindin( 2,&m_stsurfdata.p);stmtupt.bindin( 3,&m_stsurfdata.u);stmtupt.bindin( 4,&m_stsurfdata.wd);stmtupt.bindin( 5,&m_stsurfdata.wf);stmtupt.bindin( 6,&m_stsurfdata.r);stmtupt.bindin( 7,&m_stsurfdata.vis);stmtupt.bindin( 8, m_stsurfdata.obtid,5);stmtupt.bindin( 9, m_stsurfdata.ddatetime,19);}if (stmtsel.execute() != 0){invalidcount++; m_logfile->Write("stmtsel.execute() failed.\n%s\n%s\n",stmtsel.m_sql,stmtsel.m_cda.message); return stmtsel.m_cda.rc;}  iccount=0;stmtsel.next();if (iccount>0) {if (stmtupt.execute() != 0)   //执行更新的SQL语句,一定要判断返回值,0-成功,其它-失败。{invalidcount++; m_logfile->Write("stmtupt.execute() failed.\n%s\n%s\n",stmtupt.m_sql,stmtupt.m_cda.message);return stmtupt.m_cda.rc;}updatecount++;}else{   if (stmtins.execute() != 0)  // 执行插入的SQL语句,一定要判断返回值,0-成功,其它-失败。{invalidcount++; m_logfile->Write("stmtins.execute() failed.\n%s\n%s\n",stmtins.m_sql,stmtins.m_cda.message);return stmtins.m_cda.rc;}insertcount++;}return 0;
}//1111111111111111111111111111111111111111111111111111111111111CSIGNALLOG类
CSIGNALLOG::CSIGNALLOG(connection *conn,CLogFile *logfile)
{initdata();m_conn=conn; m_logfile=logfile;
}
void CSIGNALLOG::initdata()
{totalcount=insertcount=updatecount=invalidcount=0;m_conn=0; m_logfile=0;memset(&m_stsignallog,0,sizeof(struct st_signallog));vsignallog.clear();
}
CSIGNALLOG::~CSIGNALLOG()
{}bool CSIGNALLOG::SplitBuffer(const char *strBuffer) //把记录拆分到vsignallog容器中
{vsignallog.clear();memset(&m_stsignallog,0,sizeof(struct st_signallog));CCmdStr CmdStr;  CmdStr.SplitToCmd(strBuffer," ",true);if (CmdStr.CmdCount()<3) { invalidcount++; return false; }CmdStr.GetValue(0,m_stsignallog.ddatetime,12); //数据时间:格式yyyymmddhh24mistrcat(m_stsignallog.ddatetime,"00");AddTime(m_stsignallog.ddatetime,m_stsignallog.ddatetime,8*60*60,"yyyy-mm-dd hh24:mi:ss");CmdStr.GetValue(1,m_stsignallog.obtid,4);   //站点代码char strtemp[11];for (int ii=3;ii<=CmdStr.CmdCount();ii++){ // 201809142353 GWTE A3000 ....= memset(strtemp,0,sizeof(strtemp));CmdStr.GetValue(ii-1,strtemp,5);  // m_stsignallog.signalname[0]表示字符串中第一个字符m_stsignallog.signalname[0]=strtemp[0]; //strtemp[0]就是Am_stsignallog.signalcolor[0]=strtemp[1]; //strtemp[1]就是3vsignallog.push_back(m_stsignallog);totalcount++;}return true;
}long CSIGNALLOG::InsertTable()  //把vsignallog容器中的值更新到T_SIGNALDATA表中
{ //可能会返回stmtupt.m_cda.rc,所以用longif (stmtsel.m_state==0){stmtsel.connect(m_conn);// 如下这个表的主键有三个字段stmtsel.prepare("select count(*) from T_SIGNALLOG where obtid=:1 and ddatetime=to_date(:2,'yyyy-mm-dd hh24:mi:ss') and signalname=:3");stmtsel.bindin( 1, m_stsignallog.obtid,4);stmtsel.bindin( 2, m_stsignallog.ddatetime,19);stmtsel.bindin( 3, m_stsignallog.signalname,1);stmtsel.bindout(1,&iccount);}if (stmtins.m_state==0){stmtins.connect(m_conn);stmtins.prepare("insert into T_SIGNALLOG(obtid,ddatetime,signalname,signalcolor,crttime,keyid) values(:1,to_date(:2,'yyyy-mm-dd hh24:mi:ss'),:3,:4,sysdate,SEQ_SIGNALLOG.nextval)");stmtins.bindin( 1, m_stsignallog.obtid,4);stmtins.bindin( 2, m_stsignallog.ddatetime,19);stmtins.bindin( 3, m_stsignallog.signalname,1);stmtins.bindin( 4, m_stsignallog.signalcolor,1);}if (stmtupt.m_state==0){stmtupt.connect(m_conn);stmtupt.prepare("update T_SIGNALLOG set signalcolor=:1 where obtid=:2 and ddatetime=to_date(:3,'yyyy-mm-dd hh24:mi:ss') and signalname=:4");stmtupt.bindin( 1, m_stsignallog.signalcolor,1);stmtupt.bindin( 2, m_stsignallog.obtid,4);stmtupt.bindin( 3, m_stsignallog.ddatetime,19);stmtupt.bindin( 4, m_stsignallog.signalname,1);}for (int ii=0;ii<vsignallog.size();ii++){ //把容器里的值拷出来memcpy(&m_stsignallog,&vsignallog[ii],sizeof(struct st_signallog));m_logfile->Write("%s,%s,%s,%s\n",m_stsignallog.obtid,m_stsignallog.ddatetime,m_stsignallog.signalname,m_stsignallog.signalcolor);if (stmtsel.execute() != 0){invalidcount++; m_logfile->Write("stmtsel.execute() failed.\n%s\n%s\n",stmtsel.m_sql,stmtsel.m_cda.message); return stmtsel.m_cda.rc;}  iccount=0;stmtsel.next();  if (iccount>0) {    if (stmtupt.execute() != 0)   //执行更新的SQL语句,一定要判断返回值,0-成功,其它-失败。{invalidcount++; m_logfile->Write("stmtupt.execute() failed.\n%s\n%s\n",stmtupt.m_sql,stmtupt.m_cda.message);return stmtupt.m_cda.rc;}updatecount++;}else{      if (stmtins.execute() != 0)  //执行插入的SQL语句,一定要判断返回值,0-成功,其它-失败。{invalidcount++; m_logfile->Write("stmtins.execute() failed.\n%s\n%s\n",stmtins.m_sql,stmtins.m_cda.message);return stmtins.m_cda.rc;}insertcount++;}}return 0;
}//11111111111111111111111111111111111111111111111把非结构化数据文件写入oracle数据库的表中
int FileToTable(connection *in_conn,CLogFile *in_logfile,char *in_tname,char *in_filename,char *in_ddatetime)
{sqlstatement stmt(in_conn);  int icount=0;  //判断文件记录在表中是否已存在stmt.prepare("select count(*) from %s where filename=:1",in_tname);stmt.bindin(1,in_filename,300);stmt.bindout(1,&icount);if (stmt.execute() != 0){in_logfile->Write("FileToTable() failed.%s\n%s\n",stmt.m_sql,stmt.m_cda.message); return stmt.m_cda.rc;}stmt.next();  if (icount>0) return 0;  //如果记录已存在,直接返回0-成功。  //11111111111111111111111111111111111111111111111111111111111111111int ifilesize=FileSize(in_filename);  //把文件信息插入表中。stmt.prepare("\insert into %s(filename,ddatetime,filesize,filecontent,crttime,keyid)\values(:1,to_date(:2,'yyyymmddhh24miss'),:3,empty_blob(),sysdate,SEQ_%s.nextval)",\in_tname,in_tname+2);  stmt.bindin(1,in_filename,300); //empty_blob()可以换成null试试,文件内容可以不弄到blob字段stmt.bindin(2,in_ddatetime,14);stmt.bindin(3,&ifilesize);  if (stmt.execute() != 0){in_logfile->Write("FileToTable() failed.%s\n%s\n",stmt.m_sql,stmt.m_cda.message); return stmt.m_cda.rc;}    //111111111111111111111111111111111111111111111111111111111111111stmt.prepare("select filecontent from %s where filename=:1 for update",in_tname);stmt.bindin(1,in_filename,300);   //把文件内容更新到BLOB字段中。stmt.bindblob();if (stmt.execute() != 0){in_logfile->Write("FileToTable() failed.%s\n%s\n",stmt.m_sql,stmt.m_cda.message); return stmt.m_cda.rc;}    if (stmt.next() != 0) return -1; //获取一条记录,一定要判断返回值,0-成功,1403-无记录,其它-失败  if (stmt.filetolob((char *)in_filename) != 0) { //把磁盘文件pic_in.jpg的内容写入BLOB字段,一定要判断返回值,0-成功,其它-失败。in_logfile->Write("FileToTable() stmt.filetolob() failed.\n%s\n",stmt.m_cda.message); return -1;} in_conn->commit(); //图片数据大,一个文件提交一次return 0;
}

创建新用户并指定该用户缺省表空间为users,vi creuser.sql。

--把数据库用户允许错误重试的次数改为不限制
alter profile DEFAULT limit FAILED_LOGIN_ATTEMPTS UNLIMITED;
alter profile DEFAULT limit PASSWORD_LIFE_TIME  UNLIMITED;-- 上海气象数据中心的主用户,shqx为用户名,pwdidc为密码,default tablespace users默认user表空间
-- drop user shqx cascade;
create user shqx profile default identified by pwdidc default tablespace users account unlock;
grant connect to shqx;
grant dba to shqx;
exit;

下面是以超级用户登录,/ 指不用任何密码,不能远程登录只能登录本机且装有数据库。

32.站点参数建表入表/PowerDesigner/主外键:多表查询

vi crttable.sql,T_OBTCODE这个表没必要创建索引,因为数据量很少。

drop table T_OBTCODE;
create table T_OBTCODE
(obtid      char(5),cityname   varchar2(30),provname   varchar2(30),lat        number(5,2),lon        number(5,2),height     number(8,2),rsts       number(1),    --状态:1-启用,2-禁用,3-故障primary key(obtid)
);




如下图整列(列操作)插入',1);,技巧:alt+shift+鼠标拖动右边滚条到最低+鼠标点击最低。如下取消勾选全词匹配。


vi T_OBTCODE.sql将上面列操作实现的sql全部复制进去。




vi T_OBTCODE.sql改完后。

PowerDesigner安装链接:https://pan.baidu.com/s/1TR5tT6qh7G4CVPDFxZ7_wg 提取码:vx9m 。

将上面汉化…文件夹里文件全复制替换到下面安装目录中(可改变安装目录)。

上海气象.pdm文件:链接:https://pan.baidu.com/s/1zkzGDQuggwZhd1oNobeVqQ 提取码:0ad6 。

P:主键,F:外键,M:勾上不允许为空。

1.下面为主键命名,主键在Columns里p字段已指定。

如下是主键的另一种写法。

2.下面为创建索引


3.下面为创建表空间,如下将表存入名为USERS的表空间。

如下将索引存入名为INDEXS表空间。

如下主键就是索引也有这些参数也扔入INDEXS表空间。

4.字段值附上约束条件,如果复制其他表记得删除字段值约束条件

5.如下U是unique index,记录序号即keyid是唯一约束,第三行是数据时间。

下面是创建序列。

ctrl+G生成.sql文件,注意主键名不要重复,双引号替换为空。复制刚生成的.sql文件内容到crttable.sql中,执行如下命令必须其他窗口SQL>exit。

33.数据交换:数据导出为文件,数据文件推送

// exptables.cpp
#include "_public.h"
#include "_ooci.h"
struct st_arg // 主程序的参数
{char connstr[101];char charset[51];char tname[51];char cols[1001];char fieldname[1001];char fieldlen[501];int  exptype;char andstr[501];char bname[51];char ename[51];char taskname[51];char exppath[301];int  timetvl;
} starg;
CLogFile logfile;
connection conn;
bool _exptables();  // 本程序的业务流程主函数
void EXIT(int sig);
vector<string> vfieldname; // 存放拆分fieldname后的容器
vector<int>    vfieldlen;  // 存放拆分fieldlen后的容器
int maxfieldlen;           // 存放fieldlen的最大值
void SplitFields();        // 拆分fieldname和fieldlen
void _help(char *argv[]); // 显示程序的帮助
long maxkeyid;   // 已导出数据的最大的keyid
bool LoadMaxKeyid(); // 从系统参数T_SYSARG表中加载已导出数据的最大的keyid
bool UptMaxKeyid();  // 更新系统参数T_SYSARG表中已导出数据的最大的keyid
bool _xmltoarg(char *strxmlbuffer); // 把xml解析到参数starg结构中int main(int argc,char *argv[])
{if (argc!=3) { _help(argv); return -1; }CloseIOAndSignal();signal(SIGINT,EXIT); signal(SIGTERM,EXIT);if (logfile.Open(argv[1],"a+")==false){printf("打开日志文件失败(%s)。\n",argv[1]); return -1;}  if (_xmltoarg(argv[2])==false) return -1; // 把xml解析到参数starg结构中while (true){ // 连接数据库 //放while (true)外面连耗资源    if (conn.connecttodb(starg.connstr,starg.charset) != 0){logfile.Write("connect database %s failed.\n",starg.connstr); sleep(starg.timetvl); continue;}// logfile.Write("export table %s.\n",starg.tname);if (_exptables() == false) logfile.Write("export tables failed.\n"); //导出数据的主函数conn.disconnect();   // 断开与数据库的连接sleep(starg.timetvl);}return 0;
}
void EXIT(int sig)
{logfile.Write("程序退出,sig=%d\n\n",sig);exit(0);
}//1111111111111111111111111111111111111111111111111111111111111111111111
void _help(char *argv[])
{printf("\n");printf("Using:/htidc/public/bin/exptables logfilename xmlbuffer\n\n");printf("增量导出示例:\n");printf("/htidc/public/bin/exptables /log/shqx/exptables_surfdata_for_hb.log \"<connstr>shqx/pwdidc@snorcl11g_198</connstr><charset>Simplified Chinese_China.ZHS16GBK</charset><tname>T_SURFDATA</tname><cols>obtid,to_char(ddatetime,'yyyymmddhh24miss'),t,p,u,wd,wf,r,vis</cols><fieldname>obtid,ddatetime,t,p,u,wd,wf,r,vis</fieldname><fieldlen>5,14,8,8,8,8,8,8,8</fieldlen><exptype>1</exptype><andstr> and obtid in ('59293','50745')</andstr><bname>SURFDATA_</bname><ename>_for_hb</ename><taskname>SURFDATA_FOR_HB</taskname><exppath>/data/shqx/exp/tohb</exppath><timetvl>30</timetvl>\"\n\n");printf("全量导出示例:\n");printf("/htidc/public/bin/exptables /log/shqx/exptables_obtcode_for_hb.log \"<connstr>shqx/pwdidc@snorcl11g_198</connstr><charset>Simplified Chinese_China.ZHS16GBK</charset><tname>T_OBTCODE</tname><cols>obtid,obtname,provname,lat,lon,height</cols><fieldname>obtid,obtname,provname,lat,lon,height</fieldname><fieldlen>5,30,30,8,8,8</fieldlen><exptype>2</exptype><andstr> and rsts=1 and obtid in ('59293','50745')</andstr><bname>OBTCODE_</bname><ename>_for_hb</ename><exppath>/data/shqx/exp/tohb</exppath><timetvl>300</timetvl>\"\n\n");printf("本程序是数据中心的公共功能模块,从数据库的表中导出数据生成xml文件,用于数据交换。\n");printf("logfilename是本程序运行的日志文件。\n");printf("xmlbuffer为文件传输的参数,如下:\n");printf("数据库的连接参数 <connstr>shqx/pwdidc@snorcl11g_198</connstr>\n");printf("数据库的字符集 <charset>Simplified Chinese_China.ZHS16GBK</charset> 这个参数要与数据源数据库保持>一致,否则会出现中文乱码的情况。\n");printf("待导出数据的表名 <tname>T_SURFDATA</tname>\n");printf("需要导出字段的列表 <cols>obtid,to_char(ddatetime,'yyyymmddhh24miss'),t,p,u,wd,wf,r,vis</cols> 可以采用函数。\n");printf("导出字段的别名列表 <fieldname>obtid,ddatetime,t,p,u,wd,wf,r,vis</fieldname> 必须与cols一一对应。\n");printf("导出字段的长度列表 <fieldlen>5,14,8,8,8,8,8,8,8</fieldlen> 必须与cols一一对应。\n");printf("导出数据的方式 <exptype>1</exptype> 1-增量导出;2-全量导出,如果是增量导出,要求表一定要有keyid字段。\n");printf("导出数据的附加条件 <andstr> and obtid in ('59293','50745')</andstr> 注意,关键字and不能少。\n");printf("导出文件的命名的前部分 <bname>SURFDATA_</bname>\n");printf("导出文件的命名的后部分 <ename>_for_hb</ename>\n");printf("导出任务的命名 <taskname>SURFDATA_FOR_HB</taskname> 当exptype=1时该参数有效。\n");printf("导出文件存放的目录 <exppath>/data/shqx/exp/tohb</exppath>\n");printf("导出数据的时间间隔 <timetvl>30</timetvl> 单位:秒,建议大于10。\n");printf("以上参数,除了taskname和andstr,其它字段都不允许为空。\n\n\n");
}bool _xmltoarg(char *strxmlbuffer)
{memset(&starg,0,sizeof(struct st_arg));GetXMLBuffer(strxmlbuffer,"connstr",starg.connstr);if (strlen(starg.connstr)==0) { logfile.Write("connstr is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"charset",starg.charset);if (strlen(starg.charset)==0) { logfile.Write("charset is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"tname",starg.tname);if (strlen(starg.tname)==0) { logfile.Write("tname is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"cols",starg.cols);if (strlen(starg.cols)==0) { logfile.Write("cols is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"fieldname",starg.fieldname);if (strlen(starg.fieldname)==0) { logfile.Write("fieldname is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"fieldlen",starg.fieldlen);if (strlen(starg.fieldlen)==0) { logfile.Write("fieldlen is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"exptype",&starg.exptype);if ( (starg.exptype!=1) && (starg.exptype!=2) ) { logfile.Write("exptype is not in (1,2).\n"); return false; }GetXMLBuffer(strxmlbuffer,"andstr",starg.andstr);if (strlen(starg.andstr)==0) { logfile.Write("andstr is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"bname",starg.bname);if (strlen(starg.bname)==0) { logfile.Write("bname is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"ename",starg.ename);if (strlen(starg.ename)==0) { logfile.Write("ename is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"taskname",starg.taskname);if ( (starg.exptype==1) && (strlen(starg.taskname)==0) ) { logfile.Write("taskname is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"exppath",starg.exppath);if (strlen(starg.exppath)==0) { logfile.Write("exppath is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"timetvl",&starg.timetvl);if (starg.timetvl==0) { logfile.Write("timetvl is null.\n"); return false; }// 拆分fieldname和fieldlenSplitFields();// 判断fieldname和fieldlen中元素的个数一定要相同if (vfieldname.size() != vfieldlen.size() ) { logfile.Write("fieldname和fieldlen的元素个数不同。.\n"); return false; }return true;
}//111111111111111111111111111111111111111本程序的业务流程主函数
bool _exptables()
{// 从系统参数T_SYSARG表中加载已导出数据的最大的keyidif (LoadMaxKeyid()==false) { logfile.Write("LoadMaxKeyid() failed.\n"); return false; } // 生成导出数据的SQL语句char strsql[4096]; char fieldvalue[vfieldname.size()][maxfieldlen+1]; // 输出变量定义为一个二维数组//第一维vfieldname.size()字段个数(限制fieldvalue外个数),第二维maxfieldlen+1字段长度(限制fieldvalue内个数),+1是最后一个空字符结尾符//导出数据的结果不管是字符串,整数还是浮点数都用字符串存放memset(strsql,0,sizeof(strsql));if (starg.exptype==1)  //增量导出, order by keyid排完序后数据好导入sprintf(strsql,"select %s,keyid from %s where 1=1 and keyid>%ld %s order by keyid",starg.cols,starg.tname,maxkeyid,starg.andstr);else //全量导出不需要keyidsprintf(strsql,"select %s from %s where 1=1 %s",starg.cols,starg.tname,starg.andstr);sqlstatement stmt(&conn);stmt.prepare(strsql);  for (int ii=0;ii<vfieldname.size();ii++){stmt.bindout(ii+1,fieldvalue[ii],vfieldlen[ii]); //绑定变量从1开始算}// 如果是增量导出,还要绑定keyid字段if (starg.exptype==1) stmt.bindout(vfieldname.size()+1,&maxkeyid);      if (stmt.execute() != 0)  // 执行导出数据的SQL{logfile.Write("select %s failed.\n%s\n%s\n",starg.tname,stmt.m_cda.message,stmt.m_sql); return false;}int  iFileSeq=1;   // 待生成文件的序号,临时变量,文件名不会重复了char strFileName[301],strLocalTime[21];CFile File;while (true){   // 如果在循环外面打开文件,stmt.next若是没记录又要删除文件memset(fieldvalue,0,sizeof(fieldvalue));   if (stmt.next() !=0) break;    if (File.IsOpened()==false)  // 把数据写入文件{memset(strLocalTime,0,sizeof(strLocalTime));LocalTime(strLocalTime,"yyyymmddhh24miss");memset(strFileName,0,sizeof(strFileName));sprintf(strFileName,"%s/%s%s%s_%d.xml",starg.exppath,starg.bname,strLocalTime,starg.ename,iFileSeq++);if (File.OpenForRename(strFileName,"w")==false){logfile.Write("File.OpenForRename(%s) failed.\n",strFileName); return false;}File.Fprintf("<data>\n");}for (int ii=0;ii<vfieldname.size();ii++){ //数据一个字段一个字段写入xml文件中File.Fprintf("<%s>%s</%s>",vfieldname[ii].c_str(),fieldvalue[ii],vfieldname[ii].c_str());}File.Fprintf("<endl/>\n");//111111111111111111111111111111111111111111111111111111111111if (stmt.m_cda.rpc%1000==0)  //每写入1000行关闭文件{File.Fprintf("</data>\n");if (File.CloseAndRename()==false){logfile.Write("File.CloseAndRename(%s) failed.\n",strFileName); return false;}// 更新系统参数T_SYSARG表中已导出数据的最大的keyidif (UptMaxKeyid()==false) { logfile.Write("UptMaxKeyid() failed.\n"); return false; }logfile.Write("create file %s ok.\n",strFileName);}}//1111111111111111111111111111111111111111111111111111111111if (File.IsOpened()==true) //不够1000条的写入一个文件{File.Fprintf("</data>\n");if (File.CloseAndRename()==false){logfile.Write("File.CloseAndRename(%s) failed.\n",strFileName); return false;}// 更新系统参数T_SYSARG表中已导出数据的最大的keyidif (UptMaxKeyid()==false) { logfile.Write("UptMaxKeyid() failed.\n"); return false; }logfile.Write("create file %s ok.\n",strFileName);}if (stmt.m_cda.rpc>0) logfile.Write("本次导出了%d条记录。\n",stmt.m_cda.rpc);return true;
}//1111111111111111111111111111111111111111111111111111
void SplitFields() //拆分fieldname和fieldlen
{vfieldname.clear(); vfieldlen.clear(); maxfieldlen=0;  CCmdStr CmdStr;CmdStr.SplitToCmd(starg.fieldname,",");vfieldname.swap(CmdStr.m_vCmdStr);int ifieldlen=0;CmdStr.SplitToCmd(starg.fieldlen,",");for (int ii=0;ii<CmdStr.CmdCount();ii++){  CmdStr.GetValue(ii,&ifieldlen); //maxfieldlen一开始为0if (ifieldlen>maxfieldlen) maxfieldlen=ifieldlen; //得到fieldlen的最大值maxfieldlenvfieldlen.push_back(ifieldlen);}
}//111111111111111111111111111111111从系统参数T_SYSARG表中加载已导出数据的最大的keyid
bool LoadMaxKeyid() //实现增量采集必须把每次导出数据的keyid保存起来,所以采用一个T_SYSARG参数表
{   //taskname作为参数代码argcodeif (starg.exptype!=1) return true; //只有增量导出才需要加载/更新系统参数表sqlstatement stmt(&conn);stmt.prepare("select argvalue from T_SYSARG where argcode=:1");stmt.bindin(1,starg.taskname,50);stmt.bindout(1,&maxkeyid);if (stmt.execute() != 0){logfile.Write("select T_SYSARG failed.\n%s\n%s\n",stmt.m_cda.message,stmt.m_sql); return false;}// 如果记录不存在,插入一新记录。if (stmt.next() != 0){ //一直只有一条记录:SURFDATA_FOR_HB,SURFDATA_FOR_HB,0stmt.prepare("insert into T_SYSARG(argcode,argname,argvalue) values(:1,:2,0)");stmt.bindin(1,starg.taskname,50);stmt.bindin(2,starg.taskname,50);stmt.execute();conn.commit();}// logfile.Write("maxkeyid=%d\n",maxkeyid);return true;
}//1111111111111111111111111111111111更新系统参数T_SYSARG表中已导出数据的最大的keyid
bool UptMaxKeyid() //导出前加载,导出后更新
{if (starg.exptype!=1) return true;sqlstatement stmt(&conn);stmt.prepare("update T_SYSARG set argvalue=:1 where argcode=:2");stmt.bindin(1,&maxkeyid);stmt.bindin(2,starg.taskname,50);if (stmt.execute() != 0){logfile.Write("select T_SYSARG failed.\n%s\n%s\n",stmt.m_cda.message,stmt.m_sql); return false;}conn.commit();return true;
}

下面为全量导出站点参数表。


vi …1.xml,只导出了2条记录一个文件。

程序在后台跑,有新图就拿下来,wgetclient将网页内容全弄下来,搞清图片命名规律进行解析。

//wgetclient.cpp
#include "_public.h"
void EXIT(int sig);
CLogFile       logfile;
int main(int argc, char *argv[])
{if(argc!=6){printf("Usage:%s weburl tmpfilename outputfilename logfilename charset\n",argv[0]); printf("本程序用于获取WEB网页的内容。\n");printf("weburl 网页WEB的地址。\n");printf("tmpfilename 获取到的网页的内容存放的全路径的临时文件名,该文件可能是utf-8或其它编码。\n");printf("outputfilename 最终的输出文件全路径文件名,该文件是gb18030编码,注意tmpfilename被转换为outputfilename后,tmpfilename文件被自动删除。\n");printf("logfilename 本程序的运行产生的日志文件名。\n");printf("charset 网页的字符集,如utf-8\n\n");exit(1);}// 关闭全部的信号和输入输出// 设置信号,在shell状态下可用 "kill + 进程号" 正常终止些进程// 但请不要用 "kill -9 +进程号" 强行终止CloseIOAndSignal(); signal(SIGINT,EXIT); signal(SIGTERM,EXIT);if (logfile.Open(argv[4],"a+") == false){printf("logfile.Open(%s) failed.\n",argv[4]); return -1;}MKDIR(argv[2],true); MKDIR(argv[3],true);char strweburl[3001];memset(strweburl,0,sizeof(strweburl));strncpy(strweburl,argv[1],3000);char strcmd[3024];memset(strcmd,0,sizeof(strcmd));snprintf(strcmd,3000,"/usr/bin/wget -c -q -O %s \"%s\" 1>>/dev/null 2>>/dev/null",argv[2],strweburl);system(strcmd);logfile.Write("%s\n",strcmd);char strfilenametmp[301];memset(strfilenametmp,0,sizeof(strfilenametmp));snprintf(strfilenametmp,300,"%s.tmp",argv[3]);// 把获取到的网页转换为中文memset(strcmd,0,sizeof(strcmd));snprintf(strcmd,256,"iconv -c -f %s -t gb18030 %s -o %s",argv[5],argv[2],strfilenametmp);system(strcmd);logfile.Write("%s\n",strcmd);REMOVE(argv[2]);   // 删除临时文件 RENAME(strfilenametmp,argv[3]);return 0;
}void EXIT(int sig)
{if (sig > 0) signal(sig,SIG_IGN);logfile.Write("catching the signal(%d).\n",sig);logfile.Write("wgetclient exit.\n");exit(0);
}

//wgetrain24.cpp
#include "_public.h"
void EXIT(int sig);
CLogFile       logfile;
bool GetURL(char *strBuffer,char *strURL,char *strFileName);
int main(int argc, char *argv[])
{if(argc!=4){printf("Usage:%s logfilename tmpfilename outputfilename\n",argv[0]); printf("Sample:./wgetrain24 /log/shqx/wgetrain24.log /data/wgettmp /data/wfile/zhrain24\n\n");printf("本程序用于从中国天气网获取逐小时降雨量实况图。\n");printf("中国天气网的url是http://products.weather.com.cn/product/Index/index/procode/JC_JSL_ZH.shtml\n");printf("如果中国天气网的url改变,程序也在做改动。\n");printf("logfilename 本程序的运行产生的日志文件名。\n");printf("tmpfilename 本程序运行产生的临时文件存放的目录。\n");printf("获取逐小时降雨量实况图存放的目录。\n\n");exit(1);}CloseIOAndSignal(); signal(SIGINT,EXIT); signal(SIGTERM,EXIT);if (logfile.Open(argv[1],"a+") == false){printf("logfile.Open(%s) failed.\n",argv[1]); return -1;}MKDIR(argv[2],false); MKDIR(argv[3],false);while (true){    char strwgetclient[2001];  // 调用wgetclient获取网页内容memset(strwgetclient,0,sizeof(strwgetclient));snprintf(strwgetclient,2000,"/htidc/public/bin/wgetclient \"http://products.weather.com.cn/product/Index/index/procode/JC_JSL_ZH.shtml\" %s/wgetclient_%d.tmp  %s/wgetclient_%d.html %s/wgetclient.log utf-8",argv[2],getpid(),argv[2],getpid(),argv[2]);system(strwgetclient);// logfile.Write("%s\n",strwgetclient);// 打开网页内容文件char stroutputfile[301];memset(stroutputfile,0,sizeof(stroutputfile));snprintf(stroutputfile,300,"%s/wgetclient_%d.html",argv[2],getpid());CFile File;if (File.Open(stroutputfile,"r")==false){logfile.Write("File.Open(%s) failed.\n",stroutputfile); sleep(60); continue;}    char strBuffer[1001],strURL[501],strFullFileName[301],strFileName[101];  // 得到全部的图片文件名while (true){memset(strBuffer,0,sizeof(strBuffer));memset(strURL,0,sizeof(strURL));memset(strFullFileName,0,sizeof(strFullFileName));memset(strFileName,0,sizeof(strFileName));  if (File.Fgets(strBuffer,1000)==false) break;  if (MatchFileName(strBuffer,"*PWCP_TWC_WEAP_SFER_ER1_TWC_L88_P9_20*.JPG*")==false) continue;  // logfile.Write("%s",strBuffer);        if (GetURL(strBuffer,strURL,strFileName)==false) continue; //解析出url和文件名       snprintf(strFullFileName,300,"%s/%s",argv[3],strFileName); //文件已存在,不采集if (access(strFullFileName,F_OK)==0) continue;  logfile.Write("download %s ",strFileName); //调用wget获取文件memset(strwgetclient,0,sizeof(strwgetclient));snprintf(strwgetclient,500,"wget \"%s\" -o %s/wgetrain24.log -O %s",strURL,argv[2],strFullFileName);system(strwgetclient);  if (access(strFullFileName,F_OK)==0) logfile.WriteEx("ok.\n");else logfile.WriteEx("failed.\n");}  File.CloseAndRemove();sleep(60);}return 0;
}bool GetURL(char *strBuffer,char *strURL,char *strFileName)
{char *start,*end;start=end=0;if ((start=strstr(strBuffer,"http"))==0) return false;if ((end=strstr(start,"\""))==0) return false; //找双引号strncpy(strURL,start,end-start);strcpy(strFileName,strstr(strURL,"PWCP"));return true;
}
void EXIT(int sig)
{if (sig > 0) signal(sig,SIG_IGN);logfile.Write("catching the signal(%d).\n",sig);logfile.Write("wgetclient exit.\n");exit(0);
}

34.非结构化数据存储:blob,pzhrain24file

// 本程序演示如何把磁盘文件的文本文件写入Oracle的BLOB字段中。
//filetoblob.cpp,实时生成的不要存oracle的blob字段
#include "_ooci.h"
int main(int argc,char *argv[])
{connection conn;  // 连接数据库,返回值0-成功,其它-失败// 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。if (conn.connecttodb("scott/tiger@snorcl11g_198","Simplified Chinese_China.ZHS16GBK") != 0){printf("connect database %s failed.\n%s\n","scott/tiger@orcl",conn.m_cda.message); return -1;}    sqlstatement stmt(&conn);  // SQL语言操作类 // 为了方便演示,把goods表中的记录全删掉,再插入一条用于测试的数据。// 不需要判断返回值stmt.prepare("\BEGIN\delete from goods;\insert into goods(id,name,pic) values(1,'商品名称',empty_blob());\END;");  // 执行SQL语句,一定要判断返回值,0-成功,其它-失败。if (stmt.execute() != 0){printf("stmt.execute() failed.\n%s\n%s\n",stmt.m_sql,stmt.m_cda.message); return -1;}// 使用游标从goods表中提取id为1的pic字段// 注意了,同一个sqlstatement可以多次使用// 但是,如果它的sql改变了,就要重新prepare和bindin或bindout变量stmt.prepare("select pic from goods where id=1 for update");stmt.bindblob();// 执行SQL语句,一定要判断返回值,0-成功,其它-失败。if (stmt.execute() != 0){printf("stmt.execute() failed.\n%s\n%s\n",stmt.m_sql,stmt.m_cda.message); return -1;}// 获取一条记录,一定要判断返回值,0-成功,1403-无记录,其它-失败。if (stmt.next() != 0) return 0;  // 把磁盘文件pic_in.jpg的内容写入BLOB字段,一定要判断返回值,0-成功,其它-失败。if (stmt.filetolob((char *)"pic_in.jpg") != 0){printf("stmt.filetolob() failed.\n%s\n",stmt.m_cda.message); return -1;}  conn.commit(); // 提交事务return 0;
}


如下文件信息放入表中。

// pzhrain24file.cpp
#include "_public.h"
#include "_ooci.h"
#include "_shqx.h"
CLogFile logfile;
CDir Dir;
// 处理数据文件
bool _pzhrain24file(char *strargv2,char *strargv4,char *strargv5);
connection conn;
void EXIT(int sig);int main(int argc,char *argv[])
{if (argc!=7){printf("\n本程序用于处理全国逐小时雨量实况图片文件。\n\n");printf("/htidc/shqx/bin/pzhrain24file logfilename connstr srcpathname dstpathname tname timetvl\n");printf("例如:/htidc/shqx/bin/pzhrain24file /log/shqx/pzhrain24file.log shqx/pwdidc@snorcl11g_198 /data/wfile/zhrain24 /qxfile/zhrain24 T_ZHRAIN24 30\n");printf("logfilename 本程序运行的日志文件名。\n");printf("connstr 数据库的连接参数。\n");printf("srcpathname 原始文件存放的目录,文件命名如PWCP_TWC_WEAP_SFER_ER1_TWC_L88_P9_20191101070000000.JPG。\n");printf("dstpathname 目标文件存放的目录,文件按yyyy/mm/dd组织目录,重命名为zhrain24_yyyymmddhh24miss.jpg。\n");printf("tname 数据存放的表名。\n");printf("timetvl 本程序运行的时间间隔,单位:秒。\n");return -1;}CloseIOAndSignal();signal(SIGINT,EXIT); signal(SIGTERM,EXIT);if (logfile.Open(argv[1],"a+")==false){printf("打开日志文件失败(%s)。\n",argv[1]); return -1;}logfile.Write("程序启动。\n");while (true){// logfile.Write("开始扫描目录。\n");// 扫描数据文件存放的目录,只匹配"PWCP_TWC_WEAP_SFER_ER1_TWC_L88_P9_20*.JPG"if (Dir.OpenDir(argv[3],"PWCP_TWC_WEAP_SFER_ER1_TWC_L88_P9_20*.JPG",1000,true,true)==false){logfile.Write("Dir.OpenDir(%s) failed.\n",argv[3]); sleep(atoi(argv[6])); continue;}    while (true) // 逐个处理目录中的数据文件{if (Dir.ReadDir()==false) break;if (_pzhrain24file(argv[2],argv[4],argv[5])==false) {logfile.WriteEx("失败。\n"); continue;}}if (conn.m_state==1) conn.disconnect(); sleep(atoi(argv[6]));}return 0;
}
void EXIT(int sig)
{logfile.Write("程序退出,sig=%d\n\n",sig);exit(0);
}//1111111111111111111111111111111111111111111处理数据文件
bool _pzhrain24file(char *strargv2,char *strargv4,char *strargv5)
{char strddatetime[21];   // 文件的数据时间,格式yyyymmddhh24missmemset(strddatetime,0,sizeof(strddatetime));strncpy(strddatetime,strstr(Dir.m_FileName,"20"),14);//搜索文件名PWCP_TWC…中20,后面取14位,重命名为zhrain24_%s.jpgchar strdstfilename[301];  // 目标文件名,不带路径memset(strdstfilename,0,sizeof(strdstfilename));snprintf(strdstfilename,300,"zhrain24_%s.jpg",strddatetime);char strdstfilepath[301];  // 目标文件存放的目录memset(strdstfilepath,0,sizeof(strdstfilepath));snprintf(strdstfilepath,300,"%s/",strargv4);strncat(strdstfilepath,strddatetime,4);     strcat(strdstfilepath,"/");  // 年的子目录strncat(strdstfilepath,strddatetime+4,2);   strcat(strdstfilepath,"/");  // 月的子目录strncat(strdstfilepath,strddatetime+6,2);   strcat(strdstfilepath,"/");  // 日的子目录char strfulldstfilename[301]; // 目标文件名,全路径memset(strfulldstfilename,0,sizeof(strfulldstfilename));snprintf(strfulldstfilename,300,"%s%s",strdstfilepath,strdstfilename);// 如果文件已处理(目标文件已存在),直接返回成功。if (access(strfulldstfilename,F_OK) == 0) return true;if (conn.m_state==0){if (conn.connecttodb(strargv2,"Simplified Chinese_China.ZHS16GBK")!=0){logfile.Write("connect database(%s) failed.\n%s\n",strargv2,conn.m_cda.message); return false;}// logfile.Write("连接数据库成功。\n");}// 把源文件复制到目标文件if (COPY(Dir.m_FullFileName,strfulldstfilename)==false) {logfile.Write("复制文件COPY(%s,%s)...failed.\n",Dir.m_FullFileName,strfulldstfilename); return false;}// 把非结构化数据文件写入oracle数据库的表中if (FileToTable(&conn,&logfile,strargv5,strfulldstfilename,strddatetime)!=0){logfile.Write("把文件%s存入%s...failed.\n",strfulldstfilename,strargv5);  return false;}logfile.Write("把文件%s存入%s...ok.\n",strfulldstfilename,strargv5);  return true;
}


35.磁盘/cpu信息收集:内存信息free -m,和top命令查看的内存是一样的,也在系统文件/proc/meminfo

//diskinfo.cpp,写入xml文件中再入库
#include "_public.h"
void EXIT(int sig);
CLogFile logfile;int main(int argc,char *argv[])
{if (argc != 4){printf("\n");printf("Using:./diskinfo hostname logfilename outputpath\n");printf("Example:/htidc/public/bin/diskinfo 118.89.50.198 /tmp/htidc/log/diskinfo.log /tmp/htidc/monclient\n\n");printf("此程序调用df命名,把本服务器的磁盘使用率信息写入xml文件。\n");printf("hostname是本服务器的主机名,为了方便识别,也可以用IP。\n");printf("logfilename是本程序的日志文件名。\n");printf("outputpath是输出的xml文件存放的目录。\n");printf("此程序运行在需要监控的服务器上(本程序只适用Linux系统),采集后的xml文件由文件传输程序发送给数据处理服务程序入库。\n\n\n");return -1;}CloseIOAndSignal(); signal(SIGINT,EXIT); signal(SIGTERM,EXIT);if (logfile.Open(argv[2],"a+") == false){printf("logfile.Open(%s) failed.\n",argv[2]); return -1;}FILE *fp=0;if ( (fp=popen("df -k --block-size=1M","r")) == NULL ){logfile.Write("popen(df -k --block-size=1M) failed.\n"); return false;}char strXMLFileName[301],strLocalTime[21];memset(strXMLFileName,0,sizeof(strXMLFileName));memset(strLocalTime,0,sizeof(strLocalTime));LocalTime(strLocalTime,"yyyymmddhh24miss");snprintf(strXMLFileName,300,"%s/diskinfo_%s_%s.xml",argv[3],strLocalTime,argv[1]);CFile XMLFile;if (XMLFile.OpenForRename(strXMLFileName,"w+") == false ){logfile.Write("XMLFile.OpenForRename(%s) failed.\n",strXMLFileName); pclose(fp); return -1;}XMLFile.Fprintf("<data>\n");CCmdStr CmdStr;char strBuffer[1024],strLine[500];while (true){memset(strBuffer,0,sizeof(strBuffer));if (FGETS(fp,strBuffer,500) == false) break;// 如果没有找到“%”,就再读取一行,与strBuffer拼起来if (strstr(strBuffer,"%") == 0){memset(strLine,0,sizeof(strLine));if (FGETS(fp,strLine,500) == false) break;strcat(strBuffer," "); strcat(strBuffer,strLine);}// 删除字符串前后的空格和换行符DeleteLRChar(strBuffer,' '); DeleteLRChar(strBuffer,'\n');// 把字符串中间的多个空格全部转换为一个空格UpdateStr(strBuffer,"  "," ");// 把全内容全部转换为小写ToLower(strBuffer);// 除了磁盘信息,还有可能是内存,SMB等其它文件,都丢弃掉if (strncmp(strBuffer,"/dev",4) != 0) continue;CmdStr.SplitToCmd(strBuffer," ");if (CmdStr.CmdCount() != 6) continue;char strusep[21];memset(strusep,0,sizeof(strusep));strcpy(strusep,CmdStr.m_vCmdStr[4].c_str());UpdateStr(strusep,"%","");char strLocalTime[21];memset(strLocalTime,0,sizeof(strLocalTime));LocalTime(strLocalTime,"yyyymmddhh24miss");XMLFile.Fprintf(\"<nodip>%s</nodip>"\"<crttime>%s</crttime>"\"<filesystem>%s</filesystem>"\"<total>%0.02f</total>"\"<used>%0.02f</used>"\"<available>%0.02f</available>"\"<usep>%0.02f</usep>"\"<mount>%s</mount><endl/>\n",argv[1],strLocalTime,CmdStr.m_vCmdStr[0].c_str(),atof(CmdStr.m_vCmdStr[1].c_str())/1024.0,atof(CmdStr.m_vCmdStr[2].c_str())/1024.0,atof(CmdStr.m_vCmdStr[3].c_str())/1024.0,(atof(CmdStr.m_vCmdStr[2].c_str())/atof(CmdStr.m_vCmdStr[1].c_str()))*100.0,CmdStr.m_vCmdStr[5].c_str());}XMLFile.Fprintf("</data>\n");pclose(fp);XMLFile.CloseAndRename();logfile.Write("create %s ok.\n",strXMLFileName);exit(0);
}void EXIT(int sig)
{if (sig > 0) signal(sig,SIG_IGN);logfile.Write("catching the signal(%d).\n",sig);logfile.Write("diskinfo exit.\n");exit(0);
}

cpuinfo.cpp思路是定义三个结构体变量,加载cpu信息到结构体里,睡60s,再继续加载cpu信息到结构体里,再将两结构体成员相减,就可以知道一分钟内cpu情况,采用的是一分钟信息。vi /proc/stat如下。

//cpuinfo.cpp
#include "_public.h"
void EXIT(int sig);
CLogFile logfile;
struct st_cpuinfo
{double user;double sys;double wait;double nice;double idle;double irq;double softirq;double total;
};
struct st_cpuinfo stcpuinfo1,stcpuinfo2,stcpuinfo3;
bool LoadCPUInfo(struct st_cpuinfo &stcpuinfo);int main(int argc,char *argv[])
{if (argc != 4){printf("\n");printf("Using:./cpuinfo hostname logfilename outputpath\n");printf("Example:/htidc/public/bin/cpuinfo 118.89.50.198 /tmp/htidc/log/cpuinfo.log /tmp/htidc/monclient\n\n");printf("此程序读取/proc/stat文件,把本服务器的CPU使用率信息写入xml文件。\n");printf("hostname是本服务器的主机名,为了方便识别,也可以用IP。\n");printf("logfilename是本程序的日志文件名。\n");printf("outputpath是输出的xml文件存放的目录。\n");printf("此程序运行在需要监控的服务器上(本程序只适用Linux系统),采集后的xml文件由文件传输程序发送给数据处理服务程序入库。\n\n\n");return -1;}//memset(strHostName,0,sizeof(strHostName));//strncpy(strHostName,argv[2],20);CloseIOAndSignal(); signal(SIGINT,EXIT); signal(SIGTERM,EXIT);if (logfile.Open(argv[2],"a+") == false){printf("logfile.Open(%s) failed.\n",argv[2]); return -1;}memset(&stcpuinfo1,0,sizeof(struct st_cpuinfo));memset(&stcpuinfo2,0,sizeof(struct st_cpuinfo));memset(&stcpuinfo3,0,sizeof(struct st_cpuinfo));if (LoadCPUInfo(stcpuinfo1) ==false) return -1;  sleep(60);if (LoadCPUInfo(stcpuinfo2) ==false) return -1;stcpuinfo3.user=stcpuinfo2.user-stcpuinfo1.user;stcpuinfo3.sys=stcpuinfo2.sys-stcpuinfo1.sys;stcpuinfo3.wait=stcpuinfo2.wait-stcpuinfo1.wait;stcpuinfo3.nice=stcpuinfo2.nice-stcpuinfo1.nice;stcpuinfo3.idle=stcpuinfo2.idle-stcpuinfo1.idle;stcpuinfo3.irq=stcpuinfo2.irq-stcpuinfo1.irq;stcpuinfo3.softirq=stcpuinfo2.softirq-stcpuinfo1.softirq;stcpuinfo3.total=stcpuinfo3.user+stcpuinfo3.sys+stcpuinfo3.wait+stcpuinfo3.nice+stcpuinfo3.idle+stcpuinfo3.irq+stcpuinfo3.softirq;char strLocalTime[21];memset(strLocalTime,0,sizeof(strLocalTime));LocalTime(strLocalTime,"yyyymmddhh24miss");char strXMLFileName[301];memset(strXMLFileName,0,sizeof(strXMLFileName));snprintf(strXMLFileName,300,"%s/cpuinfo_%s_%s.xml",argv[3],strLocalTime,argv[1]);CFile XMLFile;if (XMLFile.OpenForRename(strXMLFileName,"w+") == false ){logfile.Write("XMLFile.OpenForRename(%s) failed.\n",strXMLFileName); return -1;}XMLFile.Fprintf("<data>\n");XMLFile.Fprintf("<nodip>%s</nodip><crttime>%s</crttime><user>%0.02f</user><sys>%0.02f</sys><wait>%0.02f</wait><nice>%0.02f</nice><idle>%0.02f</idle><usep>%0.02f</usep><endl/>\n",argv[1],strLocalTime,stcpuinfo3.user/stcpuinfo3.total*100.0,stcpuinfo3.sys/stcpuinfo3.total*100.0,stcpuinfo3.wait/stcpuinfo3.total*100.0,stcpuinfo3.nice/stcpuinfo3.total*100.0,stcpuinfo3.idle/stcpuinfo3.total*100.0,100.0-stcpuinfo3.nice/stcpuinfo3.total*100.0);XMLFile.Fprintf("</data>\n");XMLFile.CloseAndRename();logfile.Write("create %s ok.\n",strXMLFileName);exit(0);
}
void EXIT(int sig)
{if (sig > 0) signal(sig,SIG_IGN);logfile.Write("catching the signal(%d).\n",sig);logfile.Write("cpuinfo exit.\n");exit(0);
}bool LoadCPUInfo(struct st_cpuinfo &stcpuinfo)
{CFile CPUFile;if (CPUFile.Open("/proc/stat","r") == false ){logfile.Write("CPUFile.OpenForRead(/proc/stat) failed.\n"); return false;}CCmdStr CmdStr;char strBuffer[1024];while (true){memset(strBuffer,0,sizeof(strBuffer));if (CPUFile.FFGETS(strBuffer,500) == false) break;// 删除字符串前后的空格DeleteLRChar(strBuffer,' ');// 把字符串中间的多个空格全部转换为一个空格UpdateStr(strBuffer,"  "," ");ToLower(strBuffer);CmdStr.SplitToCmd(strBuffer," ");if (strcmp(CmdStr.m_vCmdStr[0].c_str(),"cpu")==0) {stcpuinfo.user=atof(CmdStr.m_vCmdStr[1].c_str());stcpuinfo.sys=atof(CmdStr.m_vCmdStr[2].c_str());stcpuinfo.wait=atof(CmdStr.m_vCmdStr[3].c_str());stcpuinfo.nice=atof(CmdStr.m_vCmdStr[4].c_str());stcpuinfo.idle=atof(CmdStr.m_vCmdStr[5].c_str());stcpuinfo.irq=atof(CmdStr.m_vCmdStr[6].c_str());stcpuinfo.softirq=atof(CmdStr.m_vCmdStr[7].c_str());return true;}}logfile.Write("Read /proc/stat failed.\n"); return false;
}


vi /tmp/htdic/monclient/cpu*,如下是收集到的信息。

【数据库1】mysql,DDL/DML,DQL,外键约束,多表/子查询,事务,登陆,连接池,jdbc,redis,crontab,ftp,oracle,数据交换/存储/收集相关推荐

  1. oracle删除库里的所有表,清空mysql指定库里全部表数据-自动删除所有表,有外键约束的表优先删除...

    清空mysql指定库里所有表数据-自动删除所有表,有外键约束的表优先删除 由于要清空数据库数据 ,手动非常麻烦.网上找了一下,有一个Oracle的,参照它,在其上修改一下用于Mysql,把代码奉献如下 ...

  2. mysql的常见的外键约束

    一  mysql的常见的外键约束策略 设计外键时,mysql提供了4种外键关联策略 1.RESTRICT 限制(默认) 如果有外键关联了tb_class 的id,则tb_class 不能删除被关联的记 ...

  3. PHPAdmin添加外键约束(建立表-表之间的关系)

    在使用PHPAdmin建立数据库之后,要添加外键约束,即建立表与表之间(字段之间的联系) 首先在建立表的过程中也写需要注意的点 1.设置主键 2.有可能会用到的关联字段要记得设置唯一性 3.将所有要建 ...

  4. SQL外键约束多表查询级联删除子查询

    文章目录 一.外键 1.创建外键约束 2.删除外键约束 3.外键约束提示 二.级联删除 三.多表查询 1.笛卡尔积 2.内连接查询 1.隐式内连接 2.显示内连接 3.外连接查询 1.左外连接 2.右 ...

  5. mysql左连接代码,mysql多表子查询与左连接的代码

    摘要 腾兴网为您分享:mysql多表子查询与左连接的代码,字体配置,邮币在线,英语字典,携程抢票等软件知识,以及cad弱电,adobe全套2018,万能格式转换器,360dns优选,音频制作app,上 ...

  6. 【原创】数据库中为什么不推荐使用外键约束

    引言 其实这个话题是老生常谈,很多人在工作中确实也不会使用外键.包括在阿里的JAVA规范中也有下面这一条 [强制]不得使用外键与级联,一切外键概念必须在应用层解决. 但是呢,询问他们原因,大多是这么回 ...

  7. 在mysql中如何添加外键约束_如何在MySQL中设置外键约束

    (1) 外键的使用: 外键的作用,主要有两个: 一个是让数据库自己通过外键来保证数据的完整性和一致性 一个就是能够增加ER图的可读性 有些人认为外键的建立会给开发时操作数据库带来很大的麻烦.因为数据库 ...

  8. mysql启动和关闭外键约束的方法(FOREIGN_KEY_CHECKS)

    在MySQL中删除一张表或一条数据的时候,出现 [Err] 1451 -Cannot delete or update a parent row: a foreign key constraint f ...

  9. 【MYSQL】foreign key 外键约束(详解)

    外键是关联不同表之间的一个联接,比如我们现在有两张表: 那么如何设置外键呢? mysql> create table class(classId int primary key auto_inc ...

最新文章

  1. css实现元素居中的常见方法
  2. 给函数传递不定关键字的参数 和
  3. css 倒三角_改善CSS的10种最佳做法,帮助你从样式中获得最大的收益。
  4. POJ 2828 Buy Tickets(单点更新) 详细题解和思路
  5. [2018.07.10 T2]不回文
  6. 项目经理是有责无权的“领导” 要会借力
  7. threejs学习之透视相机与正交相机
  8. (webapp)微信和safri 对于html5 部分功能不兼容,多选或单选下拉框去除边框无效果。...
  9. 腾讯加盟:Kafka-on-Pulsar 项目迎来 2 位腾讯 Maintainer!
  10. [转] 同期群分析Cohort Analysis
  11. EDM邮件营销推广方法分享
  12. 大学物理第一章 质点运动学详解
  13. PHP 验证码图片无法正常显示
  14. 谷歌浏览器中英文切换
  15. 浙里办APP的系统架构分析
  16. Red Hat Enterprise Linux 8(简称RHEL 8,中文名红帽企业Linux 8)的分享链接
  17. Mysql添加用户设置权限
  18. 基于虹软的人脸识别功能
  19. Compose-jb图片加载库load-the-image,适用于KMM Compose desktop桌面端(Windows,Linux,MacOs)
  20. 研究涉密计算机国产化会议,涉密领域国产化替代工作方案

热门文章

  1. Compose搭档 — Flow、Room
  2. 要跑步,选对鞋!给新手的跑步鞋指南
  3. 我国最高山峰是珠穆朗玛峰:8848m, 我现在有一张足够大的纸张,厚度为:0.01m。 请问,我折叠多少次,就可以保证厚度不低于珠穆朗玛峰的高度?(不明确次数使用while)
  4. 云开发坤坤鸡乐盒微信小程序源码
  5. 快消品企业如何进行销售管理?
  6. tongyongwenbenlian
  7. 网易有道 | REDIS 云原生实战
  8. 334. 递增的三元子序列 increasingTriplet
  9. matplotlib 中深色颜色选取
  10. python 异步文件读写_Python中使用asyncio 封装文件读写