Android初学之十七:使用LitePal操作数据库

Posted Sick.BOY

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android初学之十七:使用LitePal操作数据库相关的知识,希望对你有一定的参考价值。

一、LitePal简介

LitePal是一款开源的android数据库框架,它采用了对象关系映射(ORM)的模式,并将我们平时开发最常用到的一些数据库功能进行了封装,使得不用编写一行SQL语句就可以完成各种表的建设和增删改查的操作。LitePal的项目主页上也有详细的使用文档,地址是:https://github.com/LitePalFramework/LitePal

二、配置LitePal

1、编辑app/build.gradle文件,在dependencies闭包中添加:

1.4.1是版本号的意思,最新版本号可以到LitePal官网上查看
2、还需要配置litepal.xml文件,右击app/src/main 目录 -NEW -Directory,创建一个assets目录,接着在assets目录下再新建一个litepal.xml文件,接着编辑该文件中的内容:

其中标签用于指定数据库名,标签用于指定数据库版本号,标签用于指定所有的映射模型
3、最后还需要配置一下LitePalApplication,修改清单文件的代码

这里我们将项目的application配置为org.litepal.LitePalApplication,这样才能让LitePal的所有功能都可以正常工作。
现在LitePal的配置工作已经完成。

三、创建和升级数据库

对象关系映射(ORM)模式:我们使用的编程语言是面向对象语言,而使用数据库则是关系型数据库,那么将面向对象的语言和面向关系的数据库之间建立一种映射关系,这就是对象关系映射。可以用面向对象的思维来操作数据库,而不用再和SQL语句打交道了。

1、定义一个Book类

2、将Book类添加到映射模型中,修改litepal.xml代码

这里使用标签来声明我们要配置的映射模型,注意一定要使用完整的类名,不管有多少模型类需要映射,都是使用同样的方式配置在标签下即可
3、现在只要执行一次数据库的操作,BookStore.db数据库就会自动创建出来,添加一个创建数据库的按钮:

比如我们想要想Book表添加一个press(出版社)列,直接修改Book类中的代码,添加一个press字段即可,

与此同时,我们还想要添加一张Category表,那么只需要新建一个Category类就可以了:

改完了所有我们想改的东西,只需要将版本号加1就行了,由于这里还添加了一个新的模型,因此也需要将它添加到映射模型列表当中,修改litepal.xml代码:

这样就完成啦!

四、使用LitePal添加数据

1、LitePal进行表管理操作时不需要模型类有任何的继承结构,但是进行CRUD操作时就不行了,必须要继承自DataSupprt类才行,因此我们需要先把继承结构给加上,修改Book类中的代码:

2、接着我们向Book表中添加数据,新建一个添加数据按钮:

五、使用LitePal更新数据

1、最简单的一种更新方式就是对已存储的对象重新设置,然后重新调用save()方法即可。
2、对于LitePal来说,对象是否存储就是根据调用model.isSaved()方法的结果来判断的,返回true就表示已存储,返回false就表示未存储,那么接下来的问题就是,什么情况下会返回true,什么情况会返回false呢?

有两种情况下model.isSaved()方法才返回true,一种情况是已经调用过model.save()方法去添加数据了,此时model会被认为是已存储的对象,另一种情况是model对象是通过LitePal提供的查询API查出来的,由于是从数据库中查到的对象,因此也会被认为是已存储的对象。

示例:更新书名的价格,新建一个更新按钮:

不过,在使用updateAll()方法时,还有一 个非常重要的知识点是你需要知晓的, 就是当你想把一个字段的值更新成默认值时,是不可以使用上面的方式来set数据的。我们都知道,在Java中任何一 种数据类型的字段都会有认值,例如int类型的默认值是0,boolean类型的默认值是false, String 类型的默认值是null。那么当new出一个Book对象时,其实所有字段都已经被初识化成默认值了,比如说pages字段的值就是0。因此,如果我们想把数据库表中的pages列更新成0,直接调用book. setPages (0)是不可以的,因为即使不调用这行代码,pages字段本身也是0, LitePal 此时是不会对这个列进行更新的。
对于所有想要将为数据更新成默认值的操作,LitePal 统一提供了个setToDefault()方法, 然后传人相应的列名就可以实现了。 比如我们可以这样写:
Book book = new Book();
book. setToDefault(“pages”);
book. updateAll();
这段代码的意思是,将所有书的页数都更新为0.因为updateAll()方法中没有指定约束条件,因此更新操作对所有数据都生效了。

六、使用LitePal删除数据

使用LiePal 删除数据的方式主要有两种,第一种比较简单,
就是直接调用已存储对象的delete()方法就可以了,对于已存储对象的概念,我们在上一一小节中已经学习过了。也就是说,调用过save()方法的对象,或者是通过LitePal提供的查询API查出来的对象,都是可以直接使用delete()方法来删除数据的。这种方式比较简单,我们就不进行代码演示了,下 面直接来看另外种删除数据的方式。

示例:删除Book表中价格低于200块的数据,新建一个按钮:

七、使用LitePal查询数据

示例:查询Book表中的所有信息

除了findAl()方法之外,LitePal还提供了很多其他非常有用的查询API。
1、比如我们想要查询Book表中的第一 条数据就可以这样写:
Book firstBook = DataSupport. findFirst (Book.class);
2、查询Book表中的最后一条数据 就可以这样写:
Book lastBook = DataSupport. findLast (Book.class);
3、我们还可以通过连缀查询来定制更多的查询功能。
(1)、select()方法用于指定查询哪几列的数据,对应了SQL当中的select关键字。比如只查name和author这两列的数据,就可以这样写:
ListeBook> books = Datasupport. select(“name”, “author” ).find(Book.class);
(2)、where方法用于指定在询的约束条件,对应了sQL当中的where关键字。比如只查百
数大于400的数据,就可以这样写:
List-oo bs = Dasupprt .where( "ages> ?”, “00no.k.chas
(3)、order()方法用于指定结果的排序方式,对应了sQL当中的order by关键字。比如将查询结果按照书价从高到低排序,就可以这样写:
List books = DataSupport . order(“price desc”). find(Book.class);
其中desc表示降序排列,asc 或者不写表示升序排列。
(4)、limit()方法用于指定查询结果的数量,比如只查表中的前3条数据,就可以这样写:
List books = DataSupport. limit(3). find(Book. class);
(5)、offet()方法用于指定查询结果的偏移量,比如查询表中的第2条、第3条、第4条数据,就可以这样写:
List books = Datasupport.
由于limit(3)查询到的是前3条数据,这里我们再加上offset(1)进行一个位置的偏移,就能实现查询第2条、第3条、第4条数据的功能了。Limit()和ffset()方法共同对应了SQL当中的limit关键字。
(6)、当然,你还可以对这5个方法进行任意的连缀组合,来完成一个比较复杂的查询操作:

List<Book> books = Datasupport. select("name", "author", "pages")
.where("pages > ?",“400")
,order(" pages")
.limit(10)
.offset(10)find ( Book.class);

这段代码就表示,查询Book表中第11~20 条满足页数大于400这个条件的name、author和pages这3列数据,并将查询结果按照页数升序排列。

当前,如果你实在有一些特殊需求,上述的API都满足不了你的时候,LitePal 仍然 支持使用原生的SQL来进行查询:
Cursor c = DataSupport. findBySQL(“select * from Book where pages > ? and price < ?” “400”,“20”) ;
调用DataSupport.findBySQL()方法来进行原生查询,其中第一个参数用于指定SQL语句,后面的参数用于指定占位符的值。注意findBySQL()方法返回的是一个Cursor对象,接下
来你还需要通过之前所学的老方式将数据一取出才行。

根据郭霖大神的第一行代码学习,敬礼!

转: Java并发编程之十七:深入Java内存模型—内存操作规则总结

转载请注明出处:http://blog.csdn.net/ns_code/article/details/17377197


主内存与工作内存

    Java内存模型的主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节。此处的变量主要是指共享变量,存在竞争问题的变量。Java内存模型规定所有的变量都存储在主内存中,而每条线程还有自己的工作内存,线程的工作内存中保存了该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量(根据Java虚拟机规范的规定,volatile变量依然有共享内存的拷贝,但是由于它特殊的操作顺序性规定——从工作内存中读写数据前,必须先将主内存中的数据同步到工作内存中,所有看起来如同直接在主内存中读写访问一般,因此这里的描述对于volatile也不例外)。不同线程之间也无法直接访问对方工作内存中的变量,线程间变量值得传递均需要通过主内存来完成。


 

内存间交互操作


    Java内存模型中定义了以下8中操作来完成主内存与工作内存之间交互的实现细节:


    1、luck(锁定):作用于主内存的变量,它把一个变量标示为一条线程独占的状态。

    2、unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。

    3、read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到工作内存中,以便随后的load动作使用。

    4、load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。

    5、use(使用):作用于工作内存的变量,它把工作内存中的一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值得字节码指令时将会执行这个操作。

    6、assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。

    7、store(存储):作用于工作内存的变量,它把工作内存中的一个变量的值传递到主内存中,以便随后的write操作使用。

    8、write(写入):作用于主内存的变量,它把store操作从工作内存中得到的变量值放入主内存的变量中。


   Java内存模型还规定了执行上述8种基本操作时必须满足如下规则:


    1、不允许read和load、store和write操作之一单独出现,以上两个操作必须按顺序执行,但没有保证必须连续执行,也就是说,read与load之间、store与write之间是可插入其他指令的。

    2、不允许一个线程丢弃它的最近的assign操作,即变量在工作内存中改变了之后必须把该变化同步回主内存。

    3、不允许一个线程无原因地(没有发生过任何assign操作)把数据从线程的工作内存同步回主内存中。

    4、一个新的变量只能从主内存中“诞生”,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量,换句话说就是对一个变量实施use和store操作之前,必须先执行过了assign和load操作。

    5、一个变量在同一个时刻只允许一条线程对其执行lock操作,但lock操作可以被同一个条线程重复执行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才会被解锁。

    6、如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新执行load或assign操作初始化变量的值。

    7、如果一个变量实现没有被lock操作锁定,则不允许对它执行unlock操作,也不允许去unlock一个被其他线程锁定的变量。

    8、对一个变量执行unlock操作之前,必须先把此变量同步回主内存(执行store和write操作)。


 

volatile型变量的特殊规则


    Java内存模型对volatile专门定义了一些特殊的访问规则,当一个变量被定义成volatile之后,他将具备两种特性:

    1、保证此变量对所有线程的可见性。这里不具体解释了。需要注意,volatile变量的写操作除了对它本身的读操作可见外,volatile写操作之前的所有共享变量均对volatile读操作之后的操作可见,另外注意其适用场景,详见http://blog.csdn.net/ns_code/article/details/17290021http://blog.csdn.net/ns_code/article/details/17101369这两篇博文。

    2、禁止指令重排序优化。普通的变量仅仅会保证在该方法的执行过程中所有依赖赋值结果的地方都能获得正确的结果,而不能保证变量赋值操作的顺序与程序中的执行顺序一致,在单线程中,我们是无法感知这一点的。


    补充:Java语言规范规定了JVM线程内部维持顺序化语义,也就是说只要程序的最终结果等同于它在严格的顺序化环境下的结果,那么指令的执行顺序就可能与代码的顺序不一致,这个过程通过叫做指令的重排序。指令重排序存在的意义在于:JVM能够根据处理器的特性(CPU的多级缓存系统、多核处理器等)适当的重新排序机器指令,使机器指令更符合CPU的执行特点,最大限度的发挥机器的性能。在没有同步的情况下,编译器、处理器以及运行时等都可能对操作的执行顺序进行一些意想不到的调整


 

final域

    final类型的域是不能修改的,除了这一点外,在Java内存模型中,final域还有着特殊的语义,final域能确保初始化过程的安全性,从而可以不受限制地访问不可变对象,并在共享这些对象时无须同步。具体而言,就是被final修饰的字段在构造器中一旦被初始化完成,并且构造器没有把“this”的引用传递出去(this引用逃逸是一件很危险的事情,其他线程有可能通过这个引用访问到“初始化了一半”的对象),那么在其他线程中就能看到final字段的值,而且其外、外部可见状态永远也不会改变。它所带来的安全性是最简单最纯粹的。

 


long和double型变量的特殊规则


    Java内存模型要求lock、unlock、read、load、assign、use、store和write这8个操作都具有原子性,但是对于64位的数据类型long和double,在模型中特别定义了一条宽松的规定:允许虚拟机将没有被volatile修饰的64位数据的读写操作划分为两次32位的操作来进行。这样,如果有多个线程共享一个未被声明为volatile的long或double类型的变量,并且同时对它们进行读取和修改操作,那么某些线程可能会读到一个既非原值,也非其他线程修改值得代表了“半个变量”的数值。不过这种读取到“半个变量”的情况非常罕见,因为Java内存模型虽然允许虚拟机不把long和double变量的读写实现成原子操作,但允许迅疾选择把这些操作实现为具有原子性的操作,而且还“强烈建议”虚拟机这样实现。目前各种平台下的商用虚拟机几乎都选择吧64位数据的读写操作作为原子操作来对待,因此在编码时,不需要将long和double变量专门声明为volatile。


参考资料:http://blog.csdn.net/vking_wang/article/details/8574376#t2

                   《深入理解Java虚拟机——JVM高级特性与最佳实践》第12章


















以上是关于Android初学之十七:使用LitePal操作数据库的主要内容,如果未能解决你的问题,请参考以下文章

Android:日常学习笔记(10)———使用LitePal操作数据库

android如何使用LitePal操作数据库

android如何使用LitePal操作数据库

android如何使用LitePal操作数据库

android如何使用LitePal操作数据库

转: Java并发编程之十七:深入Java内存模型—内存操作规则总结