博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
kotlin数据库_如何在Kotlin应用程序中使用Xodus数据库
阅读量:2519 次
发布时间:2019-05-11

本文共 14749 字,大约阅读时间需要 49 分钟。

kotlin数据库

I want to show you how to use one of my favorite database choices for applications. Namely, . Why do I like using Xodus for Kotlin applications? Well, here are a couple of its selling points:

我想向您展示如何在应用程序中使用我最喜欢的数据库选择之一。 即 。 为什么我喜欢在Kotlin应用程序中使用Xodus? 好吧,这里有几个卖点:

  • Transactional

    交易性

  • Embedded

    嵌入式的

  • Schema-less

    无模式

  • Pure JVM-based

    基于纯JVM

  • Has an additional Kotlin DSL — .

    还有一个额外的Kotlin DSL — 。

What does this mean to you?

这对您意味着什么?

  • ACID on-board — all database operations are atomic, consistent, isolated, and durable.

    板载ACID —所有数据库操作都是原子的,一致的,隔离的且持久的。
  • No need to manage an external database — everything is inside your application.

    无需管理外部数据库-一切都在应用程序内部。
  • Painless refactorings — if you need to add a couple of properties you won’t have to then rebuild the tables.

    无痛重构–如果您需要添加几个属性,则无需重新构建表。
  • Cross-platform database — Xodus can run on any platform that can run a Java virtual machine.

    跨平台数据库-Xodus可以在可以运行Java虚拟机的任何平台上运行。
  • Kotlin language benefits — take the best from using types, nullable values and delegates for properties declaration and constraints description.

    Kotlin语言的好处-充分利用类型,可空值和委托进行属性声明和约束描述。

is an open-source product from . Originally it was developed for internal use, but it was subsequently released to the public back in July 2016. and use it as their data storage. If you are curious about the performance, you can check out the . As for the real-life example, take a look at the : which at the time of writing has over 1,6 million issues, and that is not even taking into account all the comments and time tracking entries all stored there.

是的开源产品。 它最初是为内部使用而开发的,但后来于2016年7月发布给公众和将其用作数据存储。 如果您对性能感到好奇,可以查看 。 对于真实的示例,请看一下 :在撰写本文时,它已发行了超过万个问题,并且甚至没有考虑所有存储在其中的注释和时间跟踪条目。

is a Kotlin library that contains the data definition language and queries for Xodus. It was also developed first as a part of the product and then later released publicly. YouTrack and Hub both use it for persistent layer definition.

是Kotlin库,其中包含数据定义语言和Xodus查询。 它也首先作为产品的一部分进行开发,然后再公开发布。 YouTrack和Hub都将其用于持久层定义。

建立 (Setup)

Let’s write a small application which stores books and their authors.

让我们编写一个存储书及其作者的小应用程序。

I will use Gradle as a build tool, as it helps simplify all the dependencies management and project compilation stuff. If you have never worked with Gradle, I recommend taking a look at the official guides they have on and .

我将Gradle用作构建工具,因为它有助于简化所有依赖项管理和项目编译的工作。 如果您从未使用过Gradle,建议您阅读他们在和的官方指南。

So first, we need to start by creating a new directory for our example, and then run gradle init there. This will initialize the project structure and add some directories and build scripts.

因此,首先,我们需要为示例创建一个新目录,然后在gradle init运行gradle init 。 这将初始化项目结构,并添加一些目录和构建脚本。

Now, create a bookstore.kt file in src/main/kotlin directory. Fill it with the never-going-out-of-fashion classics:

现在,在src/main/kotlin目录中创建一个bookstore.kt文件。 用永不过时的经典装满它:

fun main() {  println("Hello World")}

Then, update the build.gradle file using code similar to this:

然后,使用类似于以下代码的代码更新build.gradle文件:

plugins {  id 'application'  id 'org.jetbrains.kotlin.jvm' version '1.3.21'}group 'mariyadavydova'version '1.0-SNAPSHOT'sourceCompatibility = 1.8targetCompatibility = 1.8tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {  kotlinOptions {    jvmTarget = "1.8"  }}repositories {  mavenCentral()}dependencies {  implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.21'  implementation 'org.jetbrains.xodus:dnq:1.2.420'}mainClassName = 'BookstoreKt'

There are a few things that are happening here:

这里发生了一些事情:

  1. We add the Kotlin plugin and claim that the compilation output is targeted for JVM 1.8.

    我们添加了Kotlin插件,并声称编译输出针对JVM 1.8。
  2. We add dependencies to the Kotlin standard library and Xodus-DNQ.

    我们向Kotlin标准库和Xodus-DNQ添加依赖项。
  3. We also add the application plugin and define the main class. In the case of the Kotlin application, we do not have a class with a static method main, like in Java. Instead, we have to define a standalone function main. However, under the hood, Kotlin still makes a class containing this function, and the name of the class is generated from the name of the file. For example, ‘bookstore.kt’ makes ‘BookstoreKt’.

    我们还添加了应用程序插件并定义了主类。 在Kotlin应用程序的情况下,我们没有像Java这样的带有静态方法main的类。 相反,我们必须定义一个独立的函数main 。 但是,在幕后,Kotlin仍然制作了一个包含此功能的类,并且该类的名称是从文件名生成的。 例如, 'bookstore.kt'成为'BookstoreKt'

We can actually safely remove settings.gradle, as we don’t need it in this example.

实际上,我们可以安全地删除settings.gradle ,因为在此示例中不需要它。

Now, execute ./gradlew run; you should see “Hello World” in your console:

现在,执行./gradlew run ; 您应该在控制台中看到“ Hello World”:

> Task :runHello World

资料定义 (Data definition)

Xodus provides three different ways to deal with data, namely , and the . However, Xodus-DNQ supports only the Entity Stores, which describe a data model as a set of typed entities with named properties (attributes) and named entity links (relations). It is similar to rows in the SQL database table.

Xodus提供了三种不同的数据处理方式,即 , 和 。 但是,Xodus-DNQ仅支持实体存储,它们将数据模型描述为一组具有命名属性(属性)和命名实体链接(关系)的类型化实体。 它类似于SQL数据库表中的行。

As my goal is to demonstrate how simple it is to operate Xodus via Kotlin DSL, I’ll stick to the entity types API for this story.

因为我的目标是演示通过Kotlin DSL操作Xodus多么简单,所以我将坚持本故事的实体类型API。

Let’s start with an XdAuthor:

让我们从XdAuthor开始:

class XdAuthor(entity: Entity) : XdEntity(entity) {  companion object : XdNaturalEntityType
()var name by xdRequiredStringProp() var countryOfBirth by xdStringProp() var yearOfBirth by xdRequiredIntProp() var yearOfDeath by xdNullableIntProp() val books by xdLink0_N(XdBook::authors)}

From my point of view, this declaration looks pretty natural: we say that our authors always have names and year of birth, may have country of birth and year of death (the latter is irrelevant for the currently living authors); also, there could be any number of books from each author in our bookstore.

从我的角度来看,这种说法看起来很自然:我们说我们的作者总是有名字和出生年份,可能有出生国家和死亡年份(后者与当前在世的作者无关); 此外,我们书店中的每位作者都有多少本书籍。

There are several things worth mentioning in this code snippet:

此代码段中有几件事值得一提:

  • The companion object declares the entityType property for each class (which is used by the database engine).

    companion对象为每个类声明entityType属性(数据库引擎使用该属性)。

  • The data fields are declared with the help of the delegates, which encapsulate the types, properties, and constraints for these fields.

    数据字段是在委托的帮助下声明的,这些委托封装了这些字段的类型,属性和约束。
  • Links are values, not variables; that is, you don’t set them with =, but access them as a collection. (Pay attention to val books versus var name; I spent quite a bit of time trying to figure out why the compilation with var books kept failing.)

    链接是值,而不是变量; 也就是说,您不必将其设置为= ,而是将其作为集合进行访问。 (请注意val books而不是var name ;我花了很多时间试图弄清楚为什么使用var books的编译总是失败。)

The second type is an XdBook:

第二种是XdBook

class XdBook(entity: Entity) : XdEntity(entity) {  companion object : XdNaturalEntityType
()var title by xdRequiredStringProp() var year by xdNullableIntProp() val genres by xdLink1_N(XdGenre) val authors : XdMutableQuery
by xdLink1_N(XdAuthor::books)}

The thing to pay attention to here is the declaration of the authors’ field:

这里要注意的是authors字段的声明:

  • Notice that we write down the type explicitly (XdMutableQuery<XdAuthor>). For the bidirectional link, we have to help the compiler to resolve the types by leaving a hint on one of the link ends.

    请注意,我们明确记录了类型( XdMutableQuery<XdAuth或>)。 对于双向链接,我们必须通过在链接端之一上留下提示来帮助编译器解析类型。

  • Also, notice that XdAuthor::books references XdBook::authors and vice versa. We have to add these references if we want the link to be bidirectional; so if you add an author to the book, the book will appear in the list of the books of this author, and vice versa.

    另外,请注意XdAuthor::books引用了XdBook::authors ,反之亦然。 如果我们希望链接是双向的,则必须添加这些引用。 因此,如果您将作者添加到书中,则该书将出现在该作者的书列表中,反之亦然。

The third entity type is an XdGenre enumeration, which is pretty trivial:

第三种实体类型是XdGenre枚举,这很简单:

class XdGenre(entity: Entity) : XdEnumEntity(entity) { companion object : XdEnumEntityType
() { val FANTASY by enumField {} val ROMANCE by enumField {} }}

数据库初始化 (Database initialization)

Now, when we have declared the entity types, we have to initialize the database:

现在,当我们声明实体类型时,我们必须初始化数据库:

fun initXodus(): TransientEntityStore {  XdModel.registerNodes(      XdAuthor,      XdBook,      XdGenre  )  val databaseHome = File(System.getProperty("user.home"), "bookstore")  val store = StaticStoreContainer.init(      dbFolder = databaseHome,      environmentName = "db"  )  initMetaData(XdModel.hierarchy, store)  return store}fun main() {  val store = initXodus()}

This code shows the most basic setup:

此代码显示了最基本的设置:

  • We define the data model. Here we list all entity types manually, but it is possible to as well.

    我们定义数据模型。 在这里,我们手动列出了所有实体类型,但是也可以 。

  • We initialize the database store in {user.home}/bookstore folder.

    我们在{user.home}/bookstore文件夹中初始化数据库存储。

  • We link the metadata with the store.

    我们将元数据与商店链接。

填写数据 (Filling the data in)

Now that we have initialized the database, it’s time to put something inside. Before doing this, let’s add toString methods to our entity classes. Their only purpose is to allow us to output the database content in a human-readable format.

现在我们已经初始化了数据库,是时候将一些东西放到里面了。 在执行此操作之前,让我们将toString方法添加到我们的实体类中。 它们的唯一目的是允许我们以人类可读的格式输出数据库内容。

class XdAuthor(entity: Entity) : XdEntity(entity) {  ...  override fun toString(): String {    val bibliography = books.asSequence().joinToString("\n")    return "$name ($yearOfBirth-${yearOfDeath ?: "???"}):\n$bibliography"  }}class XdBook(entity: Entity) : XdEntity(entity) {  ...  override fun toString(): String {    val genres = genres.asSequence().joinToString(", ")    return "$title (${year ?: "Unknown"}) - $genres"  }}class XdGenre(entity: Entity) : XdEnumEntity(entity) {  ...  override fun toString(): String {    return this.name.toLowerCase().capitalize()  }}

Notice books.asSequence().joinToString("\n") and genres.asSequence().joinToString(", ") instructions: here we use asSequence() method to convert an XdQuery to a Kotlin collection.

请注意books.asSequence().joinToString("\n")genres.asSequence().joinToString(", ")指令:在这里,我们使用asSequence()方法将XdQuery转换为Kotlin集合。

Right, let’s now add several books from our collection inside the main function. All database operations (creating, reading, updating and removing entities) we do inside transactions — atomic database modifications, which guarantees to preserve the consistency.

正确,现在让我们在主函数中添加我们收藏中的几本书。 我们在事务内部执行所有数据库操作(创建,读取,更新和删除实体)-原子数据库修改,这保证了保持一致性。

In the case of our bookstore, there are plenty of ways to fill it with stuff:

就我们的书店而言,有很多方法可以填充其中的内容:

1. Add an author and a book separately:

1.分别添加作者和书籍:

val bronte = store.transactional {   XdAuthor.new {     name = "Charlotte Brontë"     countryOfBirth = "England"     yearOfBirth = 1816     yearOfDeath = 1855   }  } store.transactional {   XdBook.new {     title = "Jane Eyre"     year = 1847     genres.add(XdGenre.ROMANCE)     authors.add(bronte)   } }

2. Add an author and put several books in their list:

2.添加一位作者,并在列表中放入几本书:

val tolkien = store.transactional {   XdAuthor.new {     name = "J. R. R. Tolkien"     countryOfBirth = "England"     yearOfBirth = 1892     yearOfDeath = 1973   } } store.transactional {   tolkien.books.add(XdBook.new {     title = "The Hobbit"     year = 1937     genres.add(XdGenre.FANTASY)   })   tolkien.books.add(XdBook.new {     title = "The Lord of the Rings"     year = 1955     genres.add(XdGenre.FANTASY)   }) }

3. Add an author with books:

3.为作者添加书籍:

store.transactional {   XdAuthor.new {     name = "George R. R. Martin"     countryOfBirth = "USA"     yearOfBirth = 1948     books.add(XdBook.new {       title = "A Game of Thrones"       year = 1996       genres.add(XdGenre.FANTASY)     })   } }

To check that everything is created, all we need to do is to print the content of our database:

要检查所有内容是否已创建,我们所需要做的就是打印数据库的内容:

store.transactional(readonly = true) {     println(XdAuthor.all().asSequence().joinToString("\n***\n")) }

Now, if you execute ./gradlew run, you should see the following output:

现在,如果执行./gradlew run ,应该会看到以下输出:

Charlotte Brontë (1816-1855):Jane Eyre (1847) - Romance***J. R. R. Tolkien (1892-1973):The Hobbit (1937) - FantasyThe Lord of the Rings (1955) - Fantasy***George R. R. Martin (1948-???):A Game of Thrones (1996) - Fantasy

约束条件 (Constraints)

As mentioned, the transactions guarantee data consistency. One of the operations which Xodus does before saving the changes is checking the constraints. In the DNQ, some of them are encoded in the name of the delegate which provides a property of a given type. For example, xdRequiredIntProp has to always be set to some value, whereas xdNullableIntProp can remain empty.

如前所述,事务保证了数据的一致性。 Xodus在保存更改之前所做的一项操作是检查约束。 在DNQ中,其中一些编码为委托人的名称,该委托人提供给定类型的属性。 例如, xdRequiredIntProp必须始终设置为某个值,而xdNullableIntProp可以保持为空。

Despite this, Xodus-DNQ allows defining more complex constraints which are described in the . I have added several examples to the XdAuthor entity type:

尽管如此,Xodus-DNQ允许定义更复杂的约束,这些约束在中进行了 。 我向XdAuthor实体类型添加了几个示例:

var name by xdRequiredStringProp { containsNone("?!") }  var country by xdStringProp {    length(min = 3, max = 56)    regex(Regex("[A-Za-z.,]+"))  }  var yearOfBirth by xdRequiredIntProp { max(2019) }  var yearOfDeath by xdNullableIntProp { max(2019) }

You may be wondering why I have limited the countryOfBirth property length to 56 characters. Well, the longest official country name which I is “The United Kingdom of Great Britain and Northern Ireland” — precisely 56 characters!

您可能想知道为什么我将countryOfBirth属性的长度限制为56个字符。 好吧,我的最长的官方国家名称是“大不列颠及北爱尔兰联合王国”,正好是56个字符!

查询 (Queries)

We have already used database queries above. Do you remember? We printed the list of authors using XdAuthor.all().asSequence(). As you may guess, the all() method returns all the entries of a given entity type.

上面我们已经使用过数据库查询。 你还记得吗? 我们使用XdAuthor.all().asSequence()打印了作者列表。 您可能会猜到, all()方法返回给定实体类型的所有条目。

More often than not though, we will prefer filtering data. Here are some examples:

通常,我们会更喜欢过滤数据。 这里有些例子:

store.transactional(readonly = true) {  val fantasyBooks = XdBook.filter {     it.genres contains XdGenre.FANTASY }  val booksOf20thCentury = XdBook.filter {     (it.year ge 1900) and (it.year lt 1999) }  val authorsFromEngland = XdAuthor.filter {     it.countryOfBirth eq "England" }    val booksSortedByYear = XdBook.all().sortedBy(XdBook::year)  val allGenres = XdBook.all().flatMapDistinct(XdBook::genres)}

Again, there are plenty of options for building data queries, so I strongly recommend taking a look at the .

同样,构建数据查询有很多选择,因此我强烈建议您阅读 。

I hope this story is as useful for you as it was for me when I wrote it :) Any feedback is highly appreciated!

我希望这个故事对您和我写这篇文章时一样有用:)任何反馈都非常感谢!

You can find the for this tutorial here.

您可以在此处找到本教程的 。

翻译自:

kotlin数据库

转载地址:http://hcrwd.baihongyu.com/

你可能感兴趣的文章
linux安全设置
查看>>
Myflight航班查询系统
查看>>
团队-团队编程项目爬取豆瓣电影top250-代码设计规范
查看>>
表头固定内容可滚动表格的3种实现方法
查看>>
想对你说
查看>>
day5 面向对象
查看>>
{算法}Young司机带你轻松KMP
查看>>
不同方法获得视差图比较
查看>>
jQuery笔记(二)
查看>>
Velocity模版进行shiro验证
查看>>
新生舞会
查看>>
c++实现单向链表的一些操作
查看>>
Vim中无法用Alt键来映射
查看>>
ubuntu硬件配置查看命令
查看>>
第十二周作业
查看>>
Javascript之UI线程与性能优化
查看>>
实现toggleClass功能
查看>>
设计Web2.0图--Aeromatex
查看>>
nginx动静分离配置
查看>>
jQuery 两种方法实现IE10以下浏览器的placeholder效果
查看>>