最近遇到了一个XX的需求,就是我用java从后端拿到最近数据同步到前台给PD用,PD只要GBK格式的。操作系统是win7,数据库是sqlite3。看起来很简单,分分钟的事情。
1.编码格式转换
prepareStatement.setString(1,newString("什么东西".getBytes("gbk"),"utf-8"));
publicbyte[]getBytes(Charsetcharset)使用给定的charset将此String编码到byte序列,并将结果存储到新的byte数组。
publicString(byte[]bytes,Charsetcharset)通过使用指定的charset解码指定的byte数组,构造一个新的String。
我发现在这里无论是如何进行转码,入库到sqlite,无一例外的全是乱码。
在控制台打印的是这么一堆东西。
库里面是这么一堆东西,结果没有一个是正确的。命令行的默认编码是936(gbk的),这样放到PB里面才是正确的。
我怀疑是不是编译环境编码不对,换成gbk,utf-8。都不好用。
我在本机搭了一个简易的PD环境,连库,检索。全乱码。
第一种方式不行。
2.修改jdbc驱动
刚开始是这个版本。看了下源码。
在prepareStatement.setString()这一步并不进行转码。
在excuteUpdate()才做了处理
也就是说无论你setString里面传的什么东西,通通不灵,全给你用utf-8转换到byte数组里面。
问题找到,修改重新打包。在自己运行,不行,乱码。放在别人机器上,解决。然后开始比对环境我32位,他64。好的。
到这里应该解决了吧。没有,线上说必须用32位的。
在改,下一步是不是就该改jdk的东西了。感觉蛋疼。这个路先不走了。换条路。
3.使用odbc驱动
也可以,不论jdk是64的还是32的,也不用关心jdbc驱动,而且现在做的项目是部在win7上。
但问题又来了,我这里面使用的是多线程读写操作。一直会报sqlLOGICERROR,然后就卡着不动了。在网上找了篇文章,才发现。sqlite的锁机制。
在sqlite中,锁和事务是紧密联系的。为了有效地使用事务,需要了解一些关于如何加锁的知识。
sqlite采用粗放型的锁。当一个连接要写数据库,所有其它的连接被锁住,直到写连接结束了它的事务。sqlite有一个加锁表,来帮助不同的写数据库都能够在最后一刻再加锁,以保证最大的并发性。sqlite使用锁逐步上升机制,为了写数据库,连接需要逐级地获得排它锁。sqlite有5个不同的锁状态:未加锁(UNLOCKED)、共享(SHARED)、保留(RESERVED)、未决(PENDING)和排它(EXCLUSIVE)。每个数据库连接在同一时刻只能处于其中一个状态。每种状态(未加锁状态除外)都有一种锁与之对应。最初的状态是未加锁状态,在此状态下,连接还没有存取数据库。当连接到了一个数据库,甚至已经用BEGIN开始了一个事务时,连接都还处于未加锁状态。未加锁状态的下一个状态是共享状态。为了能够从数据库中读(不写)数据,连接必须首先进入共享状态,也就是说首先要获得一个共享锁。多个连接可以同时获得并保持共享锁,也就是说多个连接可以同时从同一个数据库中读数据。但哪怕只有一个共享锁还没有释放,也不允许任何连接写数据库。
如果一个连接想要写数据库,它必须首先获得一个保留锁。一个数据库上同时只能有一个保留锁。保留锁可以与共享锁共存,保留锁是写数据库的第1阶段。保留锁即不阻止其它拥有共享锁的连接继续读数据库,也不阻止其它连接获得新的共享锁。一旦一个连接获得了保留锁,它就可以开始处理数据库修改操作了,尽管这些修改只能在缓冲区中进行,而不是实际地写到磁盘。对读出内容所做的修改保存在内存缓冲区中。当连接想要提交修改(或事务)时,需要将保留锁提升为排它锁。为了得到排它锁,还必须首先将保留锁提升为未决锁。获得未决锁之后,其它连接就不能再获得新的共享锁了,但已经拥有共享锁的连接仍然可以继续正常读数据库。此时,拥有未决锁的连接等待其它拥有共享锁的连接完成工作并释放其共享锁。一旦所有其它共享锁都被释放,拥有未决锁的连接就可以将其锁提升至排它锁,此时就可以自由地对数据库进行修改了。所有以前对缓冲区所做的修改都会被写到数据库文件。
事务的种类
sqlite有三种不同的事务,使用不同的锁状态。事务可以开始于:DEFERRED、MMEDIATE或EXCLUSIVE。事务类型在BEGIN命令中指定:BEGIN[DEFERRED|IMMEDIATE|EXCLUSIVE]TRANSACTION;一个DEFERRED事务不获取任何锁(直到它需要锁的时候),BEGIN语句本身也不会做什么事情——它开始于UNLOCK状态。默认情况下就是这样的,如果仅仅用BEGIN开始一个事务,那么事务就是DEFERRED的,同时它不会获取任何锁;当对数据库进行第一次读操作时,它会获取SHARED锁;同样,当进行第一次写操作时,它会获取RESERVED锁。由BEGIN开始的IMMEDIATE事务会尝试获取RESERVED锁。如果成功,BEGINIMMEDIATE保证没有别的连接可以写数据库。但是,别的连接可以对数据库进行读操作;但是,RESERVED锁会阻止其它连接的BEGINIMMEDIATE或者BEGINEXCLUSIVE命令,当其它连接执行上述命令时,会返回sqlITE_BUSY错误。这时你就可以对数据库进行修改操作了,但是你还不能提交,当你COMMIT时,会返回sqlITE_BUSY错误,这意味着还有其它的读事务没有完成,得等它们执行完后才能提交事务。EXCLUSIVE事务会试着获取对数据库的EXCLUSIVE锁。这与IMMEDIATE类似,但是一旦成功,EXCLUSIVE事务保证没有其它的连接,所以就可对数据库进行读写操作了。上节那个例子的问题在于两个连接最终都想写数据库,但是它们都没有放弃各自原来的锁最终,SHARED锁导致了问题的出现。如果两个连接都以BEGINIMMEDIATE开始事务,那么死锁就不会发生。在这种情况下,在同一时刻只能有一个连接进入BEGINIMMEDIATE,其它的连接就得等待。BEGINIMMEDIATE和BEGINEXCLUSIVE通常被写事务使用。就像同步机制一样,它防止了死锁的产生。基本的准则是:如果你正在使用的数据库没有其它的连接,用BEGIN就足够了。但是,如果你使用的数据库有其它的连接也会对数据库进行写操作,就得使用BEGINIMMEDIATE或BEGINEXCLUSIVE开始你的事务。事务的生命周期有一些关于代码和事务的问题需要关注。首先需要知道哪个对象运行在哪个事务之下。另一个问题是持续时间——一个事务何时开始,何时结束,从哪一点开始影响其它连接?第一个问题与API直接关联,第二个与sql的一般概念及sqlite的特殊实现关联。一个连接(connection)可以包含多个语句(statement),而且每个连接有一个与数据库关联的B-tree和一个pager。Pager在连接中起着很重要的作用,因为它管理事务、锁、内存缓冲以及负责崩溃恢复(crashrecovery)。当你进行数据库写操作时,记住最重要的一件事:在任何时候,只在一个事务下执行一个连接。这回答了第一个问题。关于第二个问题,一般来说,一个事务的生命周期和语句差不多,你也可以手动结束它。默认情况下,事务自动提交,当然你也可以通过BEGIN..COMMIT手动提交。接下来的问题是事务如何与锁关联。锁的状态大多数情况下,锁的生命周期在事务的生命周期之中。它们不一定同时开始,但总时同时结束。当你结束一个事务时,也会释放它相关的锁。或者说,锁直到事务结束或系统崩溃时才会释放。如果系统在事务没有结束的情况下崩溃,那么下一个访问数据库的连接会处理这种情况。读事务我们先来看看SELECT语句执行时锁的状态变化过程,非常简单:一个连接执行SELECT语句,触发一个事务,从UNLOCKED到SHARED,当事务COMMIT时,又回到UNLOCKED,就这么简单。那么,当你运行两个语句时会发生什么呢?这时如何加锁呢?这依赖于你是否运行在自动提交状态。考虑下面的例子(为了简单,这里用了伪码):db=open('foods.db')db.exec('BEGIN')db.exec('SELECT*FROMepisodes')db.exec('COMMIT')db.close()由于显式地使用了BEGIN和COMMIT,两个SELECT命令在一个事务下执行。第一个exec()执行时,连接处于SHARED,然后第二个exec()执行。当事务提交时,连接又从SHARED回到UNLOCKED状态,状态变化如下:
UNLOCKED→PENDING→SHARED→UNLOCKED如果没有BEGIN和COMMIT两行,两个SELECT都运行于自动提交状态,状态变化如下:
UNLOCKED→PENDING→SHARED→UNLOCKED→PENDING→SHARED→UNLOCKED仅仅是读数据,但在自动提交模式下,却会经历两个加解锁的循环,太麻烦。而且,一个写进程可能插到两个SELECT中间对数据库进行修改,这样,你就不能保证第二次能够读到同样的数据了,而使用BEGIN..COMMIT就可以有此保证。写事务下面我们来考虑写数据库,比如UPDATE。和读事务一样,它也会经历UNLOCKED→PENDING→SHARED的变化过程,但接下来就会看到PENDING锁是如何起到关口作用的了。保留(RESERVED)状态当一个连接(connection)要向数据库写数据时,从SHARED状态变为RESERVED状态。如果它得到RESERVED锁,也就意味着它已经准备好进行写操作了。即使它没有把修改写入数据库,也可以把修改保存到位于pager的缓冲区中(pagecache)了。当一个连接进入RESERVED状态,pager就开始初始化回卷日志(rollbackjournal)。回卷日志是一个文件,用于回卷和崩溃恢复,见图5-1。在RESERVED状态下,pager管理着三种页:(1)已修改的页:包含被B-tree修改的记录,位于pagecache中。(2)未修改的页:包含没有被B-tree修改的记录。(3)日志页:这是修改页以前的版本,日志页并不存储在pagecache中,而是在B-tree修改页之前写入日志。Pagecache非常重要,正是因为它的存在,一个处于RESERVED状态的连接可以真正的开始工作,而不会干扰其它的(读)连接。所以,sqlite可以高效地处理在同一时刻的多个读连接和一个写连接。未决(UNPENDING)状态当一个连接完成修改,需要真正开始提交事务时,执行该过程的pager进入EXCLUSIVE状态。从RESERVED状态开始,pager试着获取PENDING锁,一旦得到,就独占它,不允许任何其它连接获得PENDING锁。既然写操作持有PENDING锁,其它任何连接都不能从UNLOCKED状态进入SHARED状态,即不会再有新的读进程,也不会再有新的写进程。只有那些已经处于SHARED状态的连接可以继续工作。而处于PENDING状态的写进程会一直等到所有这些连接释放它们的锁,然后对数据库加EXCUSIVE锁,进入EXCLUSIVE状态,独占数据库。排它(EXCLUSIVE)状态在EXCLUSIVE状态下,主要的工作是把修改的页从pagecache写入数据库文件,这是真正进行写操作的地方。在pager将修改页写到文件之前,还必须先处理日志。它检查是否所有的日志都写入了磁盘,因为它们可能还位于操作系统的缓冲区中。所以pager得告诉OS把所有的文件写入磁盘,这与synchronouspragma所做的工作相同,如第4章所述。日志是数据库进行恢复的惟一方法,所以日志对于DBMS非常重要。如果日志页没有完全写入磁盘而发生崩溃,数据库就不能恢复到它原来的状态,此时数据库就处于不一致状态。日志写盘完成后,pager就把所有的修改页写入数据库文件。接下来做什么取决于事务提交的模式,如果是自动提交,那么pager清理日志、pagecache,然后由EXCLUSIVE进入UNLOCKED。如果是手动提交,那么pager继续持有EXCLUSIVE锁和回卷日志,直至遇到COMMIT或者ROLLBACK。总之,出于性能方面的考虑,进程占有排它锁的时间应该尽可能的短,所以DBMS通常都是在真正写文件时才会占有排它锁,这样能大大提高并发性能。
这篇翻译自:http://www.cnblogs.com/cxjchen/p/3153553.html 摘过来的
然后我默默的改成了单线程。就这样。
原文链接:https://www.f2er.com/sqlite/199317.html