金沙棋牌app手机下载

当前位置:金沙棋牌 > 金沙棋牌app手机下载 > iOS数据库升级,使用Python开发SQLite代理服务器

iOS数据库升级,使用Python开发SQLite代理服务器

来源:http://www.logblo.com 作者:金沙棋牌 时间:2019-10-08 00:27

在运行程序之前需要在mysql数据库中创建test数据库,如下图所示:图片 1

最近重新处理了一下项目中数据库升级的部分,把思路和实现稍微记录一下。

C# SQL数据库间歇性异常 A network-related or instance-specific error
求助!原本稳定运行一段时间的程序突然出现SQL数据库连接异常
程序异常信息 如下

  转载:

下面是具体是实现程序:

关于数据库升级,首先我们需要一个常量,来标识我的数据库版本号应该是多少

A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server)

  

  

//2017年7月1号第1版 (指定的一个数据库的版本号)
static int kDBVersion = 2017070101;
  1. 可以ping到远程数据库服务器的ip
  2. 使用SQL Server 2014 Management Studio无法连接到远程数据库 (异常提示与程序异常一致),但本地的数据库可以连接上
  3. 重启本机后可以正常连接远程数据库
  4. 程序是定时请求数据库视图 大概是间隔10分钟请求一次
  5. 目前只是这台电脑存在问题,有运行本程序更久的电脑 没有出现过类似情况
  6. 运行程序的电脑系统是Server 2008、本地数据库是 Server 2008 R2、远程数据库的版本不清楚
  7. 远程数据库应该没问题 因为异常期间局域网内其他电脑可以正常访问

  SQLite数据库使用单个磁盘文件,并且不需要像Oracle、MSSQL、MySQL等数据库管理系统那样启动服务,使用非常灵活方便。但是SQLite也有个很严重的问题,就是没有相应的服务,也没有监听任何端口,因此相应的程序只能访问本地数据库。也就是说,无

  1 package News;
  2 
  3 import java.sql.Connection;
  4 import java.sql.DriverManager;
  5 import java.sql.ResultSet;
  6 import java.sql.Statement;
  7 import java.util.Scanner;
  8 
  9 public class News {
 10     public static void main(String[] args) {
 11         System.out.println("< < < < < < 使用ArrayList实现新闻管理系统 > > > > > >");
 12 
 13         while (true) {
 14             Scanner scanner = new Scanner(System.in);
 15             System.out
 16                     .println("1. 查询全部新闻n2. 添加新闻n3. 删除新闻n4. 按标题查询新闻n5. 按标题修改新闻 n0.退出n请选择功能(1,2,3,4,5,0):");
 17             int selected = scanner.nextInt();
 18 
 19             switch (selected) {
 20             case 1:
 21                 reads();
 22                 break;
 23 
 24             case 2:
 25                 System.out.println("请输入新闻标题:");
 26                 String title = scanner.next();
 27                 System.out.println("请输入新闻内容:");
 28                 String content = scanner.next();
 29                 System.out.println("请输入新闻备注:");
 30                 String remark = scanner.next();
 31 
 32                 add(title, content,remark);
 33                 break;
 34 
 35             case 3:
 36                 System.out.println("请输入新闻标题:");
 37                 String title2 = scanner.next();
 38                 delete(title2);
 39                 break;
 40 
 41             case 4:
 42                 System.out.println("请输入新闻标题:");
 43                 String title1 = scanner.next();
 44                 read(title1);
 45                 break;
 46 
 47             case 5:
 48                 System.out.println("请输入新闻标题:");
 49                 String title3 = scanner.next();
 50                 update(title3);
 51                 break;
 52 
 53             case 0:
 54                 scanner.close();
 55                 System.exit(0);
 56                 break;
 57 
 58             default:
 59                 System.out.println("输入错误,请重新输入:");
 60                 break;
 61             }
 62         }
 63     }
 64 
 65     /**
 66      * 定义添加新闻方法
 67      * 
 68      * @param title
 69      *            标题
 70      * @param content
 71      *            内容
 72      */
 73     public static void add(String title, String content,String remark) {
 74         try {
 75             Class.forName("com.mysql.jdbc.Driver");
 76             String url = "jdbc:mysql://localhost:3306/test";
 77             String user = "root";
 78             String password = "root";
 79             Connection conn = DriverManager.getConnection(url, user, password);
 80             Statement st = conn.createStatement();
 81             String sql = "insert into news values(null,'" + title + "','"
 82                     + content + "','"+ remark + "')";
 83             int row = st.executeUpdate(sql);
 84             System.out.println(row > 0 ? "添加成功" : "添加失败");
 85         } catch (Exception e) {
 86             e.printStackTrace();
 87         }
 88     }
 89 
 90     /**
 91      * 定义查询全部方法
 92      */
 93     public static void reads() {
 94         try {
 95             Class.forName("com.mysql.jdbc.Driver");
 96             String url = "jdbc:mysql://localhost:3306/test";
 97             String user = "root";
 98             String password = "root";
 99             Connection conn = DriverManager.getConnection(url, user, password);
100             Statement st = conn.createStatement();
101             String sql = "select * from news";
102             ResultSet list = st.executeQuery(sql);
103             if (list != null) {
104                 while (list.next()) {
105                     int id = list.getInt(1);
106                     String title = list.getString(2);
107                     String content = list.getString(3);
108                     String remark = list.getString(4);
109                     System.out.println(id + "t" + title + "t" + content
110                             + "t" + remark);
111                 }
112             }
113         } catch (Exception e) {
114             e.printStackTrace();
115         }
116     }
117 
118     /**
119      * 定义根据标题查询新闻的方法
120      * 
121      * @param title1
122      *            新闻标题
123      */
124     public static void read(String title1) {
125         try {
126             Class.forName("com.mysql.jdbc.Driver");
127             String url = "jdbc:mysql://localhost/test";
128             String user = "root";
129             String password = "root";
130             Connection conn = DriverManager.getConnection(url, user, password);
131             Statement st = conn.createStatement();
132             String sql = "select * from news where title='" + title1 + "'";
133             ResultSet row = st.executeQuery(sql);
134             if (row.next()) {
135                 int id = row.getInt(1);
136                 String title = row.getString(2);
137                 String content = row.getString(3);
138                 String remark = row.getString(4);
139                 System.out.println(id + "t" + title + "t" + content + "t"
140                         + remark);
141             }
142         } catch (Exception e) {
143             e.printStackTrace();
144         }
145     }
146 
147     /**
148      * 定义按标题删除新闻的方法
149      * 
150      * @param title
151      *            新闻方法
152      */
153     public static void delete(String title) {
154         try {
155             Class.forName("com.mysql.jdbc.Driver");
156             String url = "jdbc:mysql://localhost:3306/test";
157             String user = "root";
158             String password = "root";
159             Connection conn = DriverManager.getConnection(url, user, password);
160             Statement st = conn.createStatement();
161             String sql = "delete from news where title='" + title + "'";
162             int row = st.executeUpdate(sql);
163             System.out.println(row > 0 ? "删除成功" : "删除失败");
164         } catch (Exception e) {
165             e.printStackTrace();
166         }
167     }
168 
169     /**
170      * 定义根据标题修改新闻的方法
171      * 
172      * @param title
173      *            新闻标题
174      */
175     public static void update(String title) {
176 
177         try {
178             Class.forName("com.mysql.jdbc.Driver");
179             String url = "jdbc:mysql://localhost:3306/test";
180             String user = "root";
181             String password = "root";
182             Connection conn = DriverManager.getConnection(url, user, password);
183             Statement st = conn.createStatement();
184             String sql = "select * from news where title='" + title + "'";
185             ResultSet list = st.executeQuery(sql);
186             if (list.next()) {
187                 Scanner sca = new Scanner(System.in);
188                 System.out.println("请输入新的新闻标题:");
189                 String title4 = sca.next();
190                 System.out.println("请输入新闻内容:");
191                 String content = sca.next();
192                 System.out.println("请输入备注:");
193                 String remark = sca.next();
194                 
195                 String sql1 = "update news set title='" + title4
196                         + "',content='" + content + "',remark='" + remark + "' where title='"+title+"'";
197                 System.out.println(sql1);
198                 int u = st.executeUpdate(sql1);
199                 System.out.println(u > 0 ? "修改成功" : "修改失败");
200                 sca.close();
201             }
202         } catch (Exception e) {
203             e.printStackTrace();
204         }
205     }
206 }

好了,程序第一次运行,程序会复制数据库,并把2017070101这个版本号写入到数据库中。

尝试了多种百度类似题主的解决方法 都没效果(注!没有对程序做任何修改),最后没法重启了一次电脑,程序数据库可以正常访问,原本以为没问题了, 可是后面又出现了一次异常,但是程序日志显示 出现异常后一段时间 又可以正常访问数据库了

法分离程序和数据库,只能把程序和数据库放在同一台计算机上。

 

- (void)initializeFXDB {
    NSString *path = [NSString stringWithFormat:@"%@fenxiao.db",[GlobalVariables getCustomResourcePath]];
    if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
        [self copyFile2Documents:@"fenxiao.db"];
    }
}

网上一样异常信息的有不少,但是实际和我的情况是不一样,有没有大佬遇到过 求教

  本文使用Python开发了一个SQLite数据库的服务程序,可以完美地分离程序和数据库。技术要点是Socket编程,在数据库服务器上运行服务程序,该服务程序监听特定端口、执行代理程序发来的SQL语句并返回结果;代理程序负责接收客户端的SQL语句并转发

如果你的程序版本号未进行升级,仍然是当前版本的话,其实就没有其他逻辑了。

给服务器,然后再把服务器返回的结果转发给客户端。在具体使用时可以在本文代码基础上进行简化和扩展。

如果我的程序有数据库更新,这时就需要升级我程序中的数据库版本号:

  服务程序:

指定到需要升级到的数据库版本
static int kDBVersion = 2018010101;

  

新的程序执行依然去检查数据库,没有就复制目录中的数据库到沙盒中,并写入2018010101版本号;要是沙盒中有数据库文件,就取出数据库的版本号,来比较大小。
如果发现数据的版本号小于当前程序的版本,将数据库升级到当前版本,把程序的数据库版本号2018010101 写入数据库。

##
# 服务器程序,接收代理服务器转发来的SQL指令,并返回结果
#
import sqlite3
import socket
import struct

def getData(sql):
    '''通过给定的SQL SELECT语句返回结果'''
    with sqlite3.connect(r'database.db') as conn:
        cur = conn.cursor()
        cur.execute(sql)
        result = cur.fetchall()
    return result

def doSql(sql):
    '''适用于DELETE/UPDATE/INSERT INTO语句,返回影响的记录条数'''
    with sqlite3.connect(r'database.db') as conn:
        cur = conn.cursor()
        result = cur.execute(sql)
    return result.rowcount

# 创建socket对象,默认使用IPV4+TCP
sockServer = socket.socket()
sockServer.bind(('', 3030))
sockServer.listen(1)
while True:
    # 接收客户端连接
    try:
        conn, addr = sockServer.accept()
    except:
        continue

    sql = conn.recv(1024).decode('gbk').lower()

    if sql.startswith(('update','delete','insert')):
        try:
            # 首先发送要发送的字节总数量
            # 然后再发送真实数据
            result = str(doSql(sql)).encode('gbk')
            conn.send(struct.pack('i', len(result)))
            conn.send(result)
        except:
            message = b'error'
            conn.send(struct.pack('i', len(message)))
            conn.send(message)
    elif sql.startswith('select'):
        try:
            result = str(getData(sql)).encode('gbk')
            conn.send(struct.pack('i', len(result)))
            conn.send(result)
        except:
            message = b'error'
            conn.send(struct.pack('i', len(message)))
            conn.send(message)
- (void)initializeFXDB {
    NSString *path = [NSString stringWithFormat:@"%@fenxiao.db",[GlobalVariables getCustomResourcePath]];
    if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
        [self copyFile2Documents:@"fenxiao.db"];
    }
    if ([self getDatabaseVersion] == 0) {
        [self setDatabaseVersion:kDBVersion];
    } else {
        //读取数据中的版本号,与程序中的VERSION_CODE 进行比较,如果相等,就什么都不做,如果小于,也不做,直接把数据版本号写入数据库。
        if ([self getDatabaseVersion] <= kDBVersion) {
            [self onUpgradeWithOldVesion:[self getDatabaseVersion] newVersion:kDBVersion];
            kDBVersion = [self getDatabaseVersion];
        } else {
            [self setDatabaseVersion:kDBVersion];
        }
    }
}

  代理程序:

升级的逻辑非常简单,现在来写一下具体的升级的方法。

##
# 代理服务器,在SQLite数据库服务器和客户端之间进行指令和数据的转发
# 这样可以把数据库和程序放到两个服务器上进行分离
##
import socket
from threading import Thread
import struct

sockServer = socket.socket()
sockServer.bind(('',5050))
sockServer.listen(50)

def agent(conn):
    # 接收客户端发来的指令,并进行过滤
    sql = conn.recv(1024)
    if not sql.decode('gbk').startswith(('select', 'delete', 'insert','update')):
        message = b'not a sql statement'
        conn.send(struct.pack('i', len(message)))
        conn.send(message)
        return
    else:
        sockClient = socket.socket()
        # 尝试连接服务器
        try:
            sockClient.connect(('10.2.1.3', 3030))
        except:
            message = b'Server not alive'
            conn.send(struct.pack('i', len(message)))
            conn.send(message)
            return

        # 向服务程序转发SQL语句
        sockClient.send(sql)
        # 数据量大小,使用sturct序列化一个整数需要4个字节
        size = sockClient.recv(4)
        conn.send(size)
        size = struct.unpack('i', size)[0]
        while True:
            if size == 0:
                break
            elif size > 4096:
                data = sockClient.recv(4096)
                conn.send(data)
                size -= len(data)
            else:
                data = sockClient.recv(size)
                conn.send(data)
                size -= len(data)
        sockClient.close()
    conn.close()

while True:
    conn, _ = sockServer.accept()
    Thread(target=agent, args=(conn,)).start()

首先看一下脚本文件的目录

  模拟客户端程序:

图片 2

##
# 模拟客户端,向SQLite代理服务器发送指令并接收数据
#
import sqlite3
import socket
import struct

while True:
    sql = input('输入一个要执行的SQL语句:n')
    # 没有输入,进入下一次循环
    if sql.strip() == '':
        continue

    # 输入exit或quit,退出客户端
    if sql in ('exit', 'quit'):
        break
    # 建立socket,尝试连接
    sockClient = socket.socket()
    try:
        sockClient.connect(('10.2.1.3', 5050))        
    except:
        print('服务器异常,请检查')
    else:
        # 发送远程SQL语句
        sockClient.send(sql.encode('gbk'))
        size = sockClient.recv(4)
        size = struct.unpack('i', size)[0]

        data = b''
        while True:
            if size == 0:
                break
            elif size > 4096:
                # 注意断包和粘包
                # 虽然设置了4096,但是不一定能够接收4096字节
                #即使缓冲区的数据远多于4096
                t = sockClient.recv(4096)
                data += t
                size -= len(t)
            else:
                t = sockClient.recv(size)
                data += t
                size -= len(t)
        data = data.decode('gbk')
        try:
            data = eval(data)
        except:
            pass
        sockClient.close()
        print(data)

image.png

 

我们需要读取所有的升级脚本文件,按照名称进行排序,把文件名称转换成数字,大于数据版本号的升级脚本都执行一遍。

//升级数据库版本方法
- (void)onUpgradeWithOldVesion:(int)oldVersion newVersion:(int)newVersion{
    [self executeMigrationsWithOldVesion:oldVersion newVersion:newVersion];
}

//顺序执行每个脚本文件
- (BOOL)executeMigrationsWithOldVesion:(int)oldVersion newVersion:(int)newVersion{
    BOOL migrationExecuted = false;
    @try {
        NSString *path = [[NSBundle mainBundle] bundlePath];
        NSFileManager *fileManger = [NSFileManager defaultManager];
        NSArray *files = [fileManger contentsOfDirectoryAtPath:[NSString stringWithFormat:@"%@/Migrations",path] error:nil];

        for (NSString *file in files) {
            @try {
                int version = [[file replace:@".sql" withStr:@""] intValue];
                if (version > oldVersion && version <= newVersion) {
                    [self executeSqlScript:db file:file];
                    migrationExecuted = true;
                }
            } @catch (NSException *exception) {
                FXLog(@"%@",exception);
            }
        }
    } @catch (NSException *exception) {
        FXLog(@"%@",exception);
    }
    return migrationExecuted;
}

//逐行读取sql语句,拼接后运行每个脚本文件的sql语句
- (void)executeSqlScript:(FMDatabase *)db file:(NSString *)file{
    @try {
        NSString *path = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"Migrations/%@",file] ofType:nil];
        NSFileHandle *fh = [NSFileHandle fileHandleForReadingAtPath:path];
        NSData *data = [fh readDataToEndOfFile];
        NSString *string = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];

        NSArray *array = [string componentsSeparatedByString:@"n"];
        NSString *line = @"";
        NSString *sb = @"";
        for (int i = 0; i<array.count; i++) {
            line = array[i];
            if (line.length > 0) {
                if ([[line substringFromIndex:(line.length -1)] isEqualToString:@";"]) {
                    sb = [sb stringByAppendingString:line];
                } else {
                    sb = [sb stringByAppendingString:[NSString stringWithFormat:@"%@ ",line]];
                }
            }
        }
        //使用FMDB的话,此处需要使用executeStatements:方法,因为更新语句中可能存在重复语句,应当跳过去继续执行,弱否则语句不能继续执行下去。
        [[FXFMDB_DBTool dbTool].dbQueue inTransaction:^(FMDatabase * _Nonnull db, BOOL * _Nonnull rollback) {
            [db executeStatements:sb];
        }];
        //每次升级完成脚本后 设置数据库版本号
        [self setDatabaseVersion:[[file replace:@".sql" withStr:@""] intValue]];
    } @catch (NSException *exception) {
        FXLog(@"%@",exception);
        @throw exception;
    }
}

整个数据库升级过程就完成了,现在将上述过程中获取数据库版本等方法补充上,根据实现功能自行百度也可。

//将数据库文件从资源库剪切到Documents目录,由于在沙盒里只有Documents目录是可读写的
- (NSString *)copyFile2Documents:(NSString*)fileName
{
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSError *error;
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];

    NSString *destPath =[documentsDirectory stringByAppendingPathComponent:fileName];
//    NSString *path = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"Migrations/%@",fileName] ofType:nil];
    //  如果目标目录也就是(Documents)目录没有数据库文件的时候,才会复制一份,否则不复制
    if(![fileManager fileExistsAtPath:destPath]){
        NSString *sourcePath = [[NSBundle mainBundle] pathForResource:fileName ofType:nil];
        if (!sourcePath) {
            FXLog(@"sourcepath 为空!");
            @throw [NSException exceptionWithName:@"sourcepath 为空" reason:@"sourcepath 为空!" userInfo:nil];
            return @"";
        }
        [fileManager copyItemAtPath:sourcePath toPath:destPath error:&error];
    }
    return destPath;
}
//获取数据库版本号
- (int)getDatabaseVersion {
    __block int r = 0;
    NSArray<GXLinkedDictionary *> *arr = [[FXFMDB_DBTool dbTool] executeQuery:@"pragma user_version ; "];
    if (!arr || arr.count == 0) {
        return 0;
    }    
    GXLinkedDictionary *dict = arr[0];
    if (!dict || dict.count == 0) {
        return 0;
    }  
    [dict gxEnumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        r = (int)[obj longLongValue];
    }];
    return r;
}
//设置数据库版本号
- (void)setDatabaseVersion:(int)version {
    NSString *query = [NSString stringWithFormat:@"pragma user_version = %d", version];
    NSArray<GXLinkedDictionary *> *arr = [[FXFMDB_DBTool dbTool] executeQuery:query];
    if (arr.count) {
        FXLog(@"设置版本号为%d",version);
    }
}

本文由金沙棋牌发布于金沙棋牌app手机下载,转载请注明出处:iOS数据库升级,使用Python开发SQLite代理服务器

关键词:

上一篇:没有了

下一篇:没有了