C#中SQLite的使用及工具类
SQLite简介
SQLite是一款轻型的数据库,一个数据库就是一个文件,详细介绍参考官网:https://www.sqlite.org/index.html
SQLite 数据类型是一个用来指定任何对象的数据类型的属性。SQLite 中的每一列,每个变量和表达式都有相关的数据类型。
您可以在创建表的同时使用这些数据类型。SQLite 使用一个更普遍的动态类型系统。在 SQLite 中,值的数据类型与值本身是相关的,而不是与它的容器相关。
SQLite数据与常见的MySQL、SQL等的数据库不一样,它是动态类型数据库,每个值在数据库占的存储空间根据值的大小确定,使用时需要注意数据类型的问题。
存储类
每个存储在 SQLite 数据库中的值都具有以下存储类之一:
| 存储类 | 描述 |
|---|---|
| NULL | 值是一个 NULL 值。 |
| INTEGER | 值是一个带符号的整数,根据值的大小存储在 1、2、3、4、6 或 8 字节中。 |
| REAL | 值是一个浮点值,存储为 8 字节的 IEEE 浮点数字。 |
| TEXT | 值是一个文本字符串,使用数据库编码(UTF-8、UTF-16BE 或 UTF-16LE)存储。 |
| BLOB | 值是一个 blob 数据,完全根据它的输入存储。 |
亲和类型
SQLite支持列的亲和类型概念,任何列仍然可以存储任何类型的数据,当数据插入时该字段的数据将会优先采用亲和类型作为该值的存储方式。
创建 SQLite3 表时可使用的各种数据类型名称及相应的亲和类型,如下:
| 数据类型 | 亲和类型 |
|---|---|
| INT INTEGER TINYINT SMALLINT MEDIUMINT BIGINT UNSIGNED BIG INT INT2 INT8 |
INTEGER:对于亲缘类型为INTEGER的字段,其规则等同于NUMERIC,唯一差别是在执行CAST表达式时。 |
| CHARACTER(20) VARCHAR(255) VARYING CHARACTER(255) NCHAR(55) NATIVE CHARACTER(70) NVARCHAR(100) TEXT CLOB |
TEXT:数值型数据在被插入之前,需要先被转换为文本格式,之后再插入到目标字段中。 |
| BLOB no datatype specified |
NONE:不做任何的转换,直接以该数据所属的数据类型进行存储。 |
| REAL DOUBLE DOUBLE PRECISION FLOAT |
REAL:其规则基本等同于NUMERIC,唯一的差别是不会将"30000.0"这样的文本数据转换为INTEGER存储方式。 |
| NUMERIC DECIMAL(10,5) BOOLEAN DATE DATETIME |
NUMERIC 当文本数据被插入到亲缘性为NUMERIC的字段中时: 如果转换操作不会导致数据信息丢失以及完全可逆,那么SQLite就会将该文本数据转换为INTEGER或REAL类型的数据; 如果转换失败,SQLite仍会以TEXT方式存储该数据。 对于NULL或BLOB类型的新数据,SQLite将不做任何转换,直接以NULL或BLOB的方式存储该数据。 注:对于浮点格式的常量文本,如"30000.0",如果该值可以转换为INTEGER同时又不会丢失数值信息,那么SQLite就会将其转换为INTEGER的存储方式。 |
引用System.Data.SQLite.dll
在C#中使用SQLite数据库需要引用System.Data.SQLite.dll,下载链接:http://system.data.sqlite.org/index.html/doc/trunk/www/downloads.wiki
注:System.Data.SQLite是SQLite的ADO.NET提供程序,两者是两个不同的开源项目,现在System.Data.SQLite的开发和维护工作大部分由SQLite开发团队执行。
System.Data.SQLite的下载页面选项太多,一般人进来都不清楚要下载那些内容,下面对下载界面中的软件包做一个简单介绍。
软件包分类
下载内容按类型分为安装包、非静态连接的二进制包和静态连接的二进制包三种,区别如下:
- 安装程序包仅用于在开发人员计算机上安装,然后仅在需要VisualStudio的设计时组件时安装,不建议安装在客户机器上(理论上也可以)。
- 安装包会安装相关的动态库到系统内,并注册到GAC(Global Assembly Cache)。
- 二进制软件包旨在供开发人员使用,以便获得开发所需的汇编二进制文件,并通过XCOPY部署将其应用程序部署到客户机上。
- 两种二进制包的区别在于非托管部分的连接方式不同,非静态连接的二进制包在使用时需要VC运行时库的支持。
注:如果所有目标机器已经安装了VisualC++运行时,或者可以容易地部署,则应该避免“静态”包。
每个类型都按.NET版本分成了若干小组,每个.NET版本又分为32位和64位两组:
- 支持的.NET版本有 2.0 SP2 、 3.5 SP2 、4.0 、 4.5 、 4.5.1 、4.6 。
- 选用32位还是64位是根据使用系统来决定的,如开发时是64位(使用64位dll)系统而发布后运行在32位(使用32位dll)系统上。
注:虽然.NET高版本兼容低版本,但强烈建议选择与目标.NET Framework版本匹配的包。
在每个.NET版本-位数分组中都有2个文件包,一个带有“bundle”字样,另一个没有:
- 带有“bundle”字样的表示动态库是按混合模式编译的,在使用的时候只需要System.Data.SQLite.dll就可以了。
- 不带“bundle”的则是将非托管部分和托管部分分别编译,System.Data.SQLite.dll不能独立使用,还需要有SQLite.Interop.dll才能使用。
注:除非认为绝对必要,否则应避免使用“bundle”包。
根据上面的介绍,如果开发机器和客户机器可能具有不同的处理器体系结构,则可能需要一个以上的二进制程序包。
使用本机库预加载
本机库预加载功能从1.0.80.0版本开始可用,并且默认情况下已启用,能够自动适应当前系统的位数。为了利用此功能,必须将单独的托管程序集和互操作程序集与XCOPY部署一起使用(混合模式程序集、安装软件包部署不支持此功能)。
使用本机库预加载功能时,应用程序部署看起来如下( bin 表示将在目标计算机上部署应用程序二进制文件的目录):
- bin \ App.exe(可选,仅受管应用程序可执行程序集)
- bin \ App.dll(可选,仅托管应用程序库程序集)
- bin \ System.Data.SQLite.dll(必需,仅受管核心程序集)
- bin \ System.Data.SQLite.Linq.dll(可选,仅托管LINQ程序集)
- bin \ System.Data.SQLite.EF6.dll(可选,仅托管EF6程序集)
- bin \ x86 \ SQLite.Interop.dll(必需,x86本机互操作程序集)
- bin \ x64 \ SQLite.Interop.dll(必需,x64本机互操作程序集)
启用本机库预加载功能并显示上面的应用程序部署后,System.Data.SQLite仅限托管程序集将尝试自动检测当前进程的处理器体系结构并预加载适当的本机库,此时不用考虑客户机器的是64位还是32位。
常用部署包
我把.NET的4.0 、4.5版本对应的软件包按本机库预加载功能的要求重新组装,使用时直接复制到Debug目录下即可:
- sqlite-netFx45-binary-Win32-x64-2012-1.0.113.0.zip 提取码: r4yy
- sqlite-netFx40-binary-Win32-x64-2010-1.0.113.0.zip 提取码: j88h
- sqlite-netFx45-static-binary-Win32-x64-2012-1.0.113.0.zip 提取码: 33kp
- sqlite-netFx40-static-binary-Win32-x64-2010-1.0.113.0.zip 提取码: iqr2
注:官方建议不使用静态的二进制包,我个人则喜欢用静态的二进制包,这样就不用考虑客户机器上是否安装有对应的VC运行时库了。
工具类
工具类大部分内容来自c# Sqlite帮助类,考虑到SQLite是一个数据库一个文件、一个项目可能需要多个数据库,我将工具类改为通过对象实例操作数据库并提供一个静态的对象实例字典。
工具类代码如下:
public class SQLiteHelper
{
/// <summary>
/// 数据库列表
/// </summary>
public static Dictionary<string, SQLiteHelper> DataBaceList = new Dictionary<string, SQLiteHelper>();
/// <summary>
/// 构造函数
/// </summary>
/// <param name="filename">数据库文件名</param>
public SQLiteHelper(string filename=null)
{
DataSource = filename;
}
/// <summary>
/// 数据库地址
/// </summary>
public string DataSource { get; set; }
/// <summary>
/// 创建数据库,如果数据库文件存在则忽略此操作
/// </summary>
public void CreateDataBase()
{
string path = Path.GetDirectoryName(DataSource);
if ((!string.IsNullOrWhiteSpace(path)) && (!Directory.Exists(path))) Directory.CreateDirectory(path);
if (!File.Exists(DataSource)) SQLiteConnection.CreateFile(DataSource);
}
/// <summary>
/// 获得连接对象
/// </summary>
/// <returns>SQLiteConnection</returns>
public SQLiteConnection GetSQLiteConnection()
{
string connStr =string.Format("Data Source={0}", DataSource);
var con = new SQLiteConnection(connStr);
return con;
}
/// <summary>
/// 准备操作命令参数
/// </summary>
/// <param name="cmd">SQLiteCommand</param>
/// <param name="conn">SQLiteConnection</param>
/// <param name="cmdText">Sql命令文本</param>
/// <param name="data">参数数组</param>
private static void PrepareCommand(SQLiteCommand cmd, SQLiteConnection conn, string cmdText, Dictionary<String, String> data)
{
if (conn.State != ConnectionState.Open)
conn.Open();
cmd.Parameters.Clear();
cmd.Connection = conn;
cmd.CommandText = cmdText;
cmd.CommandType = CommandType.Text;
cmd.CommandTimeout = 30;
if (data != null && data.Count >= 1)
{
foreach (KeyValuePair<String, String> val in data)
{
cmd.Parameters.AddWithValue(val.Key, val.Value);
}
}
}
/// <summary>
/// 查询,返回DataSet
/// </summary>
/// <param name="cmdText">Sql命令文本</param>
/// <param name="data">参数数组</param>
/// <returns>DataSet</returns>
public DataSet ExecuteDataset(string cmdText, Dictionary<string, string> data = null)
{
var ds = new DataSet();
using (SQLiteConnection connection = GetSQLiteConnection())
{
var command = new SQLiteCommand();
PrepareCommand(command, connection, cmdText, data);
var da = new SQLiteDataAdapter(command);
da.Fill(ds);
}
return ds;
}
/// <summary>
/// 查询,返回DataTable
/// </summary>
/// <param name="cmdText">Sql命令文本</param>
/// <param name="data">参数数组</param>
/// <returns>DataTable</returns>
public DataTable ExecuteDataTable(string cmdText, Dictionary<string, string> data = null)
{
var dt = new DataTable();
using (SQLiteConnection connection = GetSQLiteConnection())
{
var command = new SQLiteCommand();
PrepareCommand(command, connection, cmdText, data);
SQLiteDataReader reader = command.ExecuteReader();
dt.Load(reader);
}
return dt;
}
/// <summary>
/// 返回一行数据
/// </summary>
/// <param name="cmdText">Sql命令文本</param>
/// <param name="data">参数数组</param>
/// <returns>DataRow</returns>
public DataRow ExecuteDataRow(string cmdText, Dictionary<string, string> data = null)
{
DataSet ds = ExecuteDataset(cmdText, data);
if (ds != null && ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0)
return ds.Tables[0].Rows[0];
return null;
}
/// <summary>
/// 执行数据库操作
/// </summary>
/// <param name="cmdText">Sql命令文本</param>
/// <param name="data">传入的参数</param>
/// <returns>返回受影响的行数</returns>
public int ExecuteNonQuery(string cmdText, Dictionary<string, string> data=null)
{
using (SQLiteConnection connection = GetSQLiteConnection())
{
var command = new SQLiteCommand();
PrepareCommand(command, connection, cmdText, data);
return command.ExecuteNonQuery();
}
}
/// <summary>
/// 返回SqlDataReader对象
/// </summary>
/// <param name="cmdText">Sql命令文本</param>
/// <param name="data">传入的参数</param>
/// <returns>SQLiteDataReader</returns>
public SQLiteDataReader ExecuteReader(string cmdText, Dictionary<string, string> data = null)
{
var command = new SQLiteCommand();
SQLiteConnection connection = GetSQLiteConnection();
try
{
PrepareCommand(command, connection, cmdText, data);
SQLiteDataReader reader = command.ExecuteReader(CommandBehavior.CloseConnection);
return reader;
}
catch
{
connection.Close();
command.Dispose();
throw;
}
}
/// <summary>
/// 返回结果集中的第一行第一列,忽略其他行或列
/// </summary>
/// <param name="cmdText">Sql命令文本</param>
/// <param name="data">传入的参数</param>
/// <returns>object</returns>
public object ExecuteScalar(string cmdText, Dictionary<string, string> data = null)
{
using (SQLiteConnection connection = GetSQLiteConnection())
{
var cmd = new SQLiteCommand();
PrepareCommand(cmd, connection, cmdText, data);
return cmd.ExecuteScalar();
}
}
/// <summary>
/// 分页查询
/// </summary>
/// <param name="recordCount">总记录数</param>
/// <param name="pageIndex">页牵引</param>
/// <param name="pageSize">页大小</param>
/// <param name="cmdText">Sql命令文本</param>
/// <param name="countText">查询总记录数的Sql文本</param>
/// <param name="data">命令参数</param>
/// <returns>DataSet</returns>
public DataSet ExecutePager(ref int recordCount, int pageIndex, int pageSize, string cmdText, string countText, Dictionary<string, string> data = null)
{
if (recordCount < 0)
recordCount = int.Parse(ExecuteScalar(countText, data).ToString());
var ds = new DataSet();
using (SQLiteConnection connection = GetSQLiteConnection())
{
var command = new SQLiteCommand();
PrepareCommand(command, connection, cmdText, data);
var da = new SQLiteDataAdapter(command);
da.Fill(ds, (pageIndex - 1) * pageSize, pageSize, "result");
}
return ds;
}
/// <summary>
/// 重新组织数据库:VACUUM 将会从头重新组织数据库
/// </summary>
public void ResetDataBass()
{
using (SQLiteConnection conn = GetSQLiteConnection())
{
var cmd = new SQLiteCommand();
if (conn.State != ConnectionState.Open)
conn.Open();
cmd.Parameters.Clear();
cmd.Connection = conn;
cmd.CommandText = "vacuum";
cmd.CommandType = CommandType.Text;
cmd.CommandTimeout = 30;
cmd.ExecuteNonQuery();
}
}
}
工具类使用方法如下:
static void Main(string[] args)
{
SQLiteHelper testDb = new SQLiteHelper("test.db");
SQLiteHelper.DataBaceList.Add("TEST", testDb);
//建库
testDb.CreateDataBase();
//建表
StringBuilder sbr = new StringBuilder();
sbr.AppendLine("CREATE TABLE IF NOT EXISTS `test_table`(");
sbr.AppendLine("`id` INTEGER PRIMARY KEY AUTOINCREMENT,");//自增id主键
sbr.AppendLine("`name` VARCHAR(100) NOT NULL,");
sbr.AppendLine("`password` VARCHAR(40) NOT NULL,");
sbr.AppendLine("`create_time` datetime DEFAULT CURRENT_TIMESTAMP,");
sbr.AppendLine("`update_time` datetime DEFAULT CURRENT_TIMESTAMP );");
sbr.AppendLine();
sbr.AppendLine("CREATE TRIGGER IF NOT EXISTS `trigger_test_table_update_time` ");//触发器-自动更新update_time
sbr.AppendLine("AFTER UPDATE ON `test_table` ");
sbr.AppendLine("FOR EACH ROW ");
sbr.AppendLine("BEGIN ");
sbr.AppendLine("UPDATE `test_table` SET `update_time` = CURRENT_TIMESTAMP WHERE id = old.id; ");
sbr.AppendLine("END;");
string cmdText = sbr.ToString();
int val = testDb.ExecuteNonQuery(cmdText);
Console.WriteLine("影响行数:" + val);
//增
sbr.Clear();
sbr.Append("INSERT INTO test_table (name,password) VALUES ");
sbr.Append("(11,111), ");
sbr.Append("(12,222); ");
cmdText = sbr.ToString();
val = testDb.ExecuteNonQuery(cmdText);
Console.WriteLine("影响行数:" + val);
//删
sbr.Clear();
sbr.Append("DELETE FROM test_table ");
sbr.Append("WHERE id=1;");
cmdText = sbr.ToString();
val = testDb.ExecuteNonQuery(cmdText);
Console.WriteLine("影响行数:" + val);
//改
sbr.Clear();
sbr.Append("UPDATE test_table SET ");
sbr.Append("name='13', ");
sbr.Append("password='333' ");
sbr.Append("WHERE id=@id;");
cmdText = sbr.ToString();
Dictionary<string, string> data = new Dictionary<string, string>();
data.Add("@id", "2");
val = testDb.ExecuteNonQuery(cmdText, data);
Console.WriteLine("影响行数:" + val);
//查
sbr.Clear();
sbr.Append("SELECT name,password FROM test_table ");
sbr.Append("WHERE id=@id;");
cmdText = sbr.ToString();
DataTable dt = testDb.ExecuteDataTable(cmdText, data);
Console.WriteLine("结果行数:" + dt.Rows.Count);
//删除表
sbr.Clear();
sbr.Append("DROP TABLE test_table;");
cmdText = sbr.ToString();
val = SQLiteHelper.DataBaceList["TEST"].ExecuteNonQuery(cmdText);
Console.WriteLine("影响行数:" + val);
//重组数据库
SQLiteHelper.DataBaceList["TEST"].ResetDataBass();
Console.ReadKey();
}
参考资料
- c# Sqlite帮助类
- 让使用SQLite的.NET应用自适应32位/64位系统
- System.Data.SQLite
- sqlite3自增key设定(创建自增字段)
- Sqlite如何自动更新时间字段
C#中SQLite的使用及工具类的更多相关文章
- Nutz中那些好用的工具类
Nutz 是国产的精品开源框架,它全无依赖,只以1兆多的身材,就可以实现SSH全部功能的90%以上.内容主要涵盖了:Ioc.Aop.MVC.Dao.Json等WEB开发的方方面面. 它不仅轻巧,而且 ...
- Java中的4个并发工具类 CountDownLatch CyclicBarrier Semaphore Exchanger
在 java.util.concurrent 包中提供了 4 个有用的并发工具类 CountDownLatch 允许一个或多个线程等待其他线程完成操作,课题点 Thread 类的 join() 方法 ...
- Java中的AES加解密工具类:AESUtils
本人手写已测试,大家可以参考使用 package com.mirana.frame.utils.encrypt; import com.mirana.frame.constants.SysConsta ...
- 【深度精讲】JFinal中的Ret和Kv工具类的区别,你用对了吗?
在JFinal中有两个类Map的工具类,一个是有状态的Ret,一个是无状态的Kv,各种自己的应用场景,你用对了吗? 下面我们从多个方面来探究一下,JFinal针对这两个类的设计: 一.位置-com.j ...
- java中redis的分布式锁工具类
使用方式 try { if(PublicLock.getLock(lockKey)){ //这里写代码逻辑,执行完后需要释放锁 PublicLock.freeLock(lockKey); } } ca ...
- java高并发系列 - 第16天:JUC中等待多线程完成的工具类CountDownLatch,必备技能
这是java高并发系列第16篇文章. 本篇内容 介绍CountDownLatch及使用场景 提供几个示例介绍CountDownLatch的使用 手写一个并行处理任务的工具类 假如有这样一个需求,当我们 ...
- java.util.concurrent中的几种同步工具类
java.util.concurrent并发包中提供了一系列的的同步工具类,这些基础类不管是否能在项目中使用到,了解一下使用方法和原理对java程序员来说都是有必要的.博主在看<java并发编程 ...
- java中使用反射做一个工具类,来为指定类中的成员变量进行赋值操作,使用与多个类对象的成员变量的赋值。
//------------------------------------------------我是代码的分割线 // 首选是一个工具类,在该工具类里面,定义了一个方法,public void s ...
- Spring中内置的一些工具类
学习Java的人,或者开发很多项目,都需要使用到Spring 这个框架,这个框架对于java程序员来说.学好spring 就不怕找不到工作.我们时常会写一些工具类,但是有些时候 我们不清楚,我们些的工 ...
随机推荐
- Java并发包源码学习系列:同步组件CyclicBarrier源码解析
目录 CyclicBarrier概述 案例学习 类图结构及重要字段 内部类Generation及相关方法 void reset() void breakBarrier() void nextGener ...
- java中是否存在i+1<i?
存在! 首先我们知道int的取值范围是: -2147483648~2147483647,最高位为符号位 2147483647的二进制为:01111111 11111111 11111111 11111 ...
- SpringBoot读取配置文件的内容
1.@Value读取 在springboot项目中,如果要读取配置文件application.properties或application.yml文件的内容,可以使用自带的注解@Value.以prop ...
- python使用requests模块下载文件并获取进度提示
一.概述 使用python3写了一个获取某网站文件的小脚本,使用了requests模块的get方法得到内容,然后通过文件读写的方式保存到硬盘同时需要实现下载进度的显示 二.代码实现 安装模块 pip3 ...
- hexo 报错 use_date_for_updated is deprecated...
hexo 报错 use_date_for_updated is deprecated... WARN Deprecated config detected: "use_date_for_up ...
- jar下载慢,maven配置国内仓库
使用 maven 下载 jar 包速度会很慢,原因是 maven 默认的仓库地址是国外的,所以速度很慢,解决这个问题我们只需要修改 maven 仓库地址即可 maven 下载 jar 包时会优先去 ~ ...
- linux 几种传输文件的方式
SimpleHTTPServer + wget 如果线上可以直连线下的话,在线上使用wget访问线下的文件服务器: web的方法,比较灵活,使用完要尽快关闭这个服务: cd temp temp$ py ...
- 用go实现常见的数据结构
目录 1 golang常见数据结构实现 1.1 链表 1.2 可变数组 1.3 栈和队列 1.3.1 原生切片实现栈和队列 1.3.1.1 切片原生栈实现 1.3.1.2 切片原生队列实现 1.3.2 ...
- 微信小程序自定义Tabber,附详细源码
目录 1,前言 2,说明 3,核心代码 1,前言 分享一个完整的微信小程序自定义Tabber,tabber按钮可以设置为跳转页面,也可以设置为功能按钮.懒得看文字的可以直接去底部,博主分享了小程序代码 ...
- Springboot 轻量替代框架 Solon 1.3.10 发布
Solon 是一个微型的Java开发框架.项目从2018年启动以来,参考过大量前人作品:历时两年,4000多次的commit:内核保持0.1m的身材,超高的跑分,良好的使用体验.支持:RPC.REST ...