1 Star 0 Fork 0

氘発孜然 / easysql-scala

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
Apache-2.0

项目介绍

easysql-scala是easysql项目 https://gitee.com/wangzhang/easysql 的Scala3衍生版本。

利用Scala3的dsl构造能力,提供了比前者更简洁的调用方式;

使用Scala3强大的类型系统提供严格的类型检查;

以及使用宏(macro)来提供更高的sql生成性能,和额外的编译期检查。

我们可以使用接近于原生sql的dsl构建跨数据库的sql语句,无需任何代码以外的配置,就能构造出复杂的查询,比如:

val select = (select (*)
        from User 
        leftJoin Post on User.id === Post.uid
        orderBy User.id.asc
        limit 10 offset 10)

使用这种dsl,有下面的好处:

  1. 类型安全:将表达式与错误类型的值或者表达式比较时,将会编译失败;
  2. 防注入:dsl的背后是sql语法树,并非是单纯字符串拼接,静态的语法树类型可以防止绝大多数sql注入;
  3. 查询的任何部分都可以被封装到方法或变量中,我们可以用来动态构建sql;
  4. 获得ide提示。

支持mysql、postgres sql、oracle、sqlserver、sqlite在内的多种数据库,并且封装出了统一的api。(mysql与pgsql为第一优先级支持

此版本没有考虑与Java的兼容性,如果需要使用Java调用,请使用Kotlin版。

快速开始

我们编写一个object,继承TableSchema,例如:

object User extends TableSchema {
   override val tableName: String = "user"
   val id: TableColumnExpr[Int] = intColumn("id")
   val name: TableColumnExpr[String] = varcharColumn("name")
}

在伴生对象中添加override val tableName: String,其值为表名。

给伴生对象的属性赋值成column()类型,在column()函数中添加数据库列名。

column类型支持:intColumn、longColumn、varcharColumn、floatColumn、doubleColumn、booleanColumn、dateColumn。

然后我们就可以使用select方法创建一个Select实例,并编写一个简单的查询:

val s = select (User.id) from User

上面代码中的User和User.id即是来自我们定义好的object,藉此我们可以获得类似原生sql的编写体验。

元数据配置

我们对上文的object加以改造,对主键字段填加.primaryKey调用(此时字段类型将不再是TableColumnExpr,而是PrimaryKeyColumnExpr)。

object User extends TableSchema {
   val id: PrimaryKeyColumnExpr[Int] = intColumn("id").primaryKey
   val name: TableColumnExpr[String] = varcharColumn("name")
}

如果是自增主键,使用.incr(一张表支持多个主键但只支持一个自增主键):

object User extends TableSchema {
   val id: PrimaryKeyColumnExpr[Int] = intColumn("id").incr
   val name: TableColumnExpr[String] = varcharColumn("name")
}

对可空类型的字段,我们添加.haveNull,并在类型参数中添加(| Null):

object User extends TableSchema {
   val id: PrimaryKeyColumnExpr[Int] = intColumn("id").incr
   val name: TableColumnExpr[String | Null] = varcharColumn("name").haveNull
}

后续的查询构造器中,将会利用object中的配置,进行类型检查。

查询构造器

接下来我们将开始了解查询构造器,为了更好地使用查询构造器,也为了使用者更深入的了解sql的本质,我们先来了解表达式和运算符:

表达式和运算符

我们首先来介绍库提供的表达式和各种运算符。

表达式拥有共同的父类Expr,而大多数表达式的参数中也可以接收Expr类型,因此,表达式之间可以互相嵌套,便可以提供高抽象能力。

表达式类型中如果代入进了字符串,会在生成sql时自动转义特殊符号,以此来防止sql注入。

字段

字段是最基本的表达式,比如上文object中的属性User.id就是一个字段类型,可以代入到其他表达式或查询语句中:

select (User.id) from User

如果我们需要查询全部字段,那么可以像下面这样调用:

select (*) from User

这个表达式的定义如下:

def * = AllColumnExpr()

假如你的项目中,表名字段名是动态的,并不能构建出实体类,那可以使用另一种方式生成字段表达式:

col[Int]("c1")

其中的类型参数为字段的实际类型(也可以不传类型参数,但这会失去类型安全性,请使用者自行斟酌),可空字段可以写成:

col[Int | Null]("c1")

将这种字段代入查询:

select (col[Int]("c1")) from User

如果col()中的字符串包含.,那么.左侧会被当做表名,右侧会被当做字段名;如果包含*,那么会产生一个sql通配符。

表达式别名

表达式类型可以使用中缀函数as来起别名,我们在此以字段类型为例,后文的其他表达式类型也支持这个功能:

select(User.id as "c1").from(User)

带入进查询时,需要使用小括号包裹。

常量

在某些需求中,可能会将某一种常量来作为查询结果集的一列,比如:

SELECT 1 AS c1

我们可以使用const()来生成常量类型的表达式:

select(const(1) as "c1")

当然,利用Scala强大的编译器,只要在使用时引入dsl.constToExpr这个隐式转换,此处就可以省略掉const():

import dsl.constToExpr
select(1 as "c1")

隐式转换后也可以在运算符左侧使用此值:

import dsl.constToExpr
select (*) from User where 1 < User.id

constToExpr虽然方便,但并非是类型安全的,如果需要严格的类型检查,我们可以根据需要导入intToExpr、stringToExpr等隐式转换。

聚合函数

内置了count、countDistinct、sum、avg、max、min这些标准的聚合函数,比如:

select(count(*) as "col1", sum(User.id) as "col2").from(User)

上文的*返回的也是一个Expr的子类,所以我们可以像上面那样来代入到count函数中。

逻辑运算符

库内置了==、===(由于==与内置库函数同名,所以使用==可能在某些情况下会产生预期之外的结果,所以更推荐使用===代替==)、<>、>、>=、<、<=、&&(AND)、||(OR)、^(XOR)等逻辑运算符,我们可放入where条件中:

select (*) from User where User.id === 1

因为Scala的强大,在一些复杂条件中,我们不需要像easysql的Kotlin版本那样小心翼翼。

因为Kotlin中这些运算符只是普通函数,并不能指定结合性,所以在较复杂的条件中,我们需要使用小括号来将每一组子条件括起来:

select().from(User).where((User.name eq "小黑") and ((User.id gt 1) or (User.gender eq 1)))

而Scala版就更简洁:

select (*) from User where User.name === "小黑" && (User.id > 1 || User.gender === 1)

如果使用AND来拼接条件,也可以使用多个where。

除了上文的逻辑运算符外,还支持in、notIn、like、notLike、isNull、isNotNull、between、notBetween:

select(*)
    .from(User)
    .where(User.gender in (1, 2))
    .where(User.id between (1, 10))
    .where(User.name.isNotNull)
    .where(User.name like "%xxx%")

上文中object里未使用haveNull来标注的字段,使用isNull或isNotNull时,产生一个编译错误。

这些运算符不仅可以代入数值,字符串等常量,表达式类型的子类也可以代入其中,比如我们需要做一个需求,查询当前的时间在表的两个字段范围内的数据,我们可以这样写:

select(*).from(User).where(const(Date()).between(User.time1, User.time2))

这已经体现出运算符的抽象能力了,但是,我们还可以再简洁一些:

import dsl.dateToExpr
select (*) from User where Date().between(User.time1, User.time2)

数学运算符

库提供了+、-、*、/、%五个数学运算符,比如:

select(count(*) + 1).from(User)

case表达式

使用caseWhen()方法和中缀函数thenIs与elseIs来生成一个case表达式:

val c = caseWhen(User.gender === 1 thenIs "男", User.gender === 2 thenIs "女") elseIs "其他"

select(c as "gender").from(User)

这会产生下面的查询:

SELECT CASE 
		WHEN user.gender = 1 THEN '男'
		WHEN user.gender = 2 THEN '女'
		ELSE '其他'
	END AS gender
FROM user

case when表达式也可以传入聚合函数中:

val c = caseWhen(User.gender === 1 thenIs User.gender) elseIs null

val select = select(count(c) as "male_count").from(User)

这会产生下面的查询:

SELECT COUNT(CASE 
		WHEN user.gender = 1 THEN user.gender
		ELSE NULL
	END) AS male_count
FROM user

窗口函数

使用聚合函数或rank()、denseRank()、rowNumber()三个窗口专用函数,后面调用.over,来创建一个窗口函数,然后通过partitionBy和orderBy来构建一个窗口:

select(rank().over.partitionBy(User.id).orderBy(User.name.asc) as "over")
    .from(User)

这会产生如下的查询:

SELECT RANK() OVER (PARTITION BY user.id ORDER BY user.user_name ASC) AS over FROM user

partitionBy()接收若干个表达式类型

orderBy()接收若干个排序列,在表达式类型之后调用.asc或.desc来生成排序规则。

窗口函数是一种高级查询方式,使用时需要注意数据库是否支持(比如mysql8.0以下版本不支持窗口函数功能)。

普通函数

可以使用内置的NormalFunctionExpr来封装一个数据库函数。

函数不利于查询优化以及不同数据库转换,因此不推荐使用

cast表达式

使用cast()方法生成一个cast表达式用于数据库类型转换。

第一个参数为表达式类型,为待转换的表达式;

第二个参数为String,为想转换的数据类型。

比如:

val select = select(cast(User.id, "CHAR")).from(User)

这会产生下面的查询:

SELECT CAST(user.id AS CHAR) FROM user

会影响查询效率,不推荐使用

查询语句

在介绍完表达式和运算符之后,我们便可以开始着重来讲sql的核心:select语句的构建。

库内置了一系列方法来支持SELECT语句的编写,比如:

val select = select (*) from User where User.name === "小黑"

然后我们就可以用sql方法,并传入数据库类型枚举DB,获取生成的sql:

val sql = select.sql(DB.MYSQL)

当然,生成sql语句的方法并非只有sql,后文会详细说明。

select子句

select()方法中支持传入若干个前文介绍的表达式类型:

select(User.id, User.name)

链式调用多个select,会在生成sql时依次拼接进sql语句。 由于需要对查询类型校验,每次调用select都会产生一个新的Select对象,如无必要(比如动态生成sql),请慎用多次select功能。

from子句

from()方法支持传入一个字符串表名,或者前文介绍的继承了TableSchema的对象名:

select(*).from(User)

select(*).from("table")

此功能使用Scala3的union type功能实现。

不支持from多张表,如果有此类需求,请使用join功能。

表别名

alias()方法给表起别名:

select(*).from(User).as("t1")

如果别名需要加入列名,alias()的表名参数后面继续添加列名即可(这种别名方式对后文介绍的values临时表非常有用):

select(*).from(User).as("table", "col1", "col2")

as调用之前,必须保证调用了from,否则运行时会抛出异常。

where子句

使用where()配合各种前面介绍的运算符和表达式,生成where条件:

select(*).from(User).where(User.id === 1).where(User.gender <> 1)

多个where()会使用AND来拼接条件,如果需要使用OR和XOR,请参考前文的运算符部分。

有些时候,我们需要根据一些条件动态拼接where条件,我们可以这样调用:

select(*).from(User).where(testCondition, User.name === "")

where()的第一个参数接收一个Boolean表达式,只有表达式返回的值是true的时候,条件才会被拼接到sql中。

如果判断条件比较复杂,第一个参数也可以传入一个返回Boolean类型的lambda表达式。

不止是select语句,后文的update和delete语句也支持这些where()的调用方式,以后便不再赘述。

order by子句

使用orderBy()方法传入表达式类型的.asc或者.desc方法来生成的排序规则:

select (*) from User orderBy (User.id.asc, User.name.desc)

这可能还不够像sql风格,我们可以导入scala.language.postfixOps来去掉asc和desc之前的点:

import scala.language.postfixOps
select (*) from User orderBy (User.id asc, User.name desc)

group by和having子句

使用groupBy()来聚合数据,having()来做聚合后的筛选:

select(User.gender, count(*)).from(User).groupBy(User.gender).having(count(*) > 1)

groupBy()接收若干个表达式类型,having()的使用方式与where()相似。

distinct子句

在调用链中添加distinct即可,会对查出的列进行去重。

select(User.name).from(User).distinct

limit子句

使用limit(count, offset)来做数据条数筛选(注意此处与mysql的参数顺序不一样),如:

select(*).from(User).limit(1, 100)

limit中第二个参数也可以不填,即为默认值0:

select(*).from(User).limit(1)

我们也可以使用中缀函数limit和offset组合调用

select (*) from User limit 1 offset 10

也可以不调用offset函数,即为默认值0。

limit语句并不是sql标准用法,因此每个数据库厂商采用的语法都有差异,生成sql时会根据数据源的数据库类型进行方言适配。

oracle需要版本在12c以上,sqlserver需要版本在2012以上。低于此版本,需要使用者自行处理ROW NUMBER。

join子句

提供:join()、innerJoin()、leftJoin()、rightJoin()、crossJoin()、fullJoin()几种不同的join方法。

上述方法配合on()方法来做表连接:

select(*).from(User).leftJoin(Post).on(User.id === Post.uid)

对于表的as()方法,会给最近的一个表起别名。

子查询

如果需要使用子查询,我们另外声明一个Select对象传入调用链即可:

select(*).from(Select().from(User)).as("t")

join中的子查询:

select(*).from("t1")
	.leftJoin(Select().from("t2").limit(10))
	.as("t2")
	.on(col("t1.id") === col("t2.id"))

操作符中的子查询:

select(*).from(User)
	.where(User.id in Select().select(User.id).from(User).limit(10))

支持EXISTS、NOT EXISTS、ANY、ALL、SOME这五个子查询谓词,使用对应的全局函数把查询调用链代入即可:

select(*).from(User)
   .where(exists(Select().select(max(User.id)).from(User)))

当然子查询谓词依然是表达式类型,所以可以使用操作符函数来计算:

select(*).from(User)
   .where(User.id < any(Select().select(max(User.id)).from(User)))

如果需要使用LATERAL子查询,把from()改为fromLateral()即可(join的调用方式类似,需要注意使用的数据库版本是否支持LATERAL关键字):

select(*).fromLateral(Select().from(User)).as("t")

for update

使用forUpdate方法将查询加锁:

select(*).from(User).forUpdate

不支持sqlite;在sqlserver中会在FROM子句后生成WITH (UPDLOCK);其他数据库会在sql语句末尾生成FOR UPDATE。

获取sql

前面通过链式调用构建的查询,其实只是构建出sql语法树,还并未生成sql,我们还需要一个链式调用终止操作,来生成需要的sql语句:

select.sql(DB) // 直接生成sql

select.fetchCountSql(DB) // 生成查询count的sql(会复制查询副本并去掉limit和order by信息)

select.pageSql(10, 1)(DB) // 生成分页sql(会复制查询副本并根据参数替换掉limit信息)

其他查询语句

除了普通的select语句之外,还支持union等一些特殊查询,这些查询只支持使用sql方法获取sql语句:

union查询

支持union、unionAll、except、interSect来将两个查询拼接在一起:

val s = (select (User.name) from User where User.id === 1) union (select (User.name) from User where User.id === 2)

如果两个查询select中的返回类型不一致,将无法通过编译。

with查询

生成一个with查询(mysql和pgsql使用递归查询在调用链添加.recursive)

import dsl.boolToExpr
val w = WithSelect()
    .add("q1", List("name"), Select() select User.name from User where User.id === 1)
    .add("q2", List("name"), Select() select User.name from User where User.id === 2)
    .select { s =>
        s from "q1" join "q2" on true
    }

values临时表

val v = ValuesSelect().addRow(1, "x").addRow(2, "y")

values临时表也可以被放入union中。

插入语句

val i = insertInto(User)(User.id, User.name).values((1, "x"), (2, "y"))
val sql = i.sql(DB)

得益于Scala3强大的类型系统,values中如果类型与前面的字段定义不一致,将会产生编译错误。

我们也可以使用实体类来插入数据,实体类继承TableEntity, 并添加伴生对象:

case class User(id: Int = 123, name: Option[String] = Some("")) extends TableEntity[Int]

object User extends TableSchema {
    override val tableName: String = "user"
    val id: PrimaryKeyColumnExpr[Int] = intColumn("id").incr
    val name: TableColumnExpr[String | Null] = varcharColumn("user_name").haveNull
}

TableEntity中的类型参数为主键类型,如果是联合主键,可以传入一个元组类型,比如:TableEntity[(Int, String)]

联合主键的表,请确保实体类、伴生对象、TableEntity的主键定义顺序一致。

然后使用insert方法:

val user = User(name = Some("x"))
val sql = insert(user).sql(DB)

使用incr标注的字段将会在语句生成时忽略。

更新语句

val u = update (User) set (User.name to "x") where User.id === 1

to是对TableColumnExpr添加的扩展函数,其中添加了类型检查,所以如果更新成与字段不同的类型,将不会通过编译检查;

并且由于是对TableColumnExpr的扩展,所以如果更新主键,将会产生编译错误。

to的右边不止可以是值,也可以是其他表达式,所以我们可以实现这样的功能:

update (User) set (User.followers to User.followers + 1) where User.id === 1

我们也可以传入上文插入语句介绍中的实体类,用主键字段的值作为条件更新其他字段:

val user = User(1, Some("x"))
update(user)

删除语句

我们这样创建一个删除语句:

val d = deleteFrom (User) where User.id === 1

我们也可以使用实体类的类型生成按主键删除的sql:

delete[User](1)

插入或更新

使用实体类生成按主键插入或更新的sql:

val user = User(1, Some("x"))
val s = save(user)
val sql = s.sql(DB)

每一种数据库生成的sql均不同。

与数据库交互

上面我们了解了查询构造器,并知道了如何利用它来生成sql语句,但是这还不够。

实际项目中,我们需要连接到数据库,并修改数据库中的数据或是拿到查询结果。

目前,easysql添加了jdbc子项目,基于jdbc做了数据库交互的实现(以后会逐步添加其他的数据库驱动实现, 比如异步数据库连接

下面我们以jdbc子项目为例,来介绍easysql与数据库交互的方式(以后其他的数据库驱动方式,由于跟jdbc共享同一个父接口,使用方式大同小异):

为了更好地使用jdbc,我们需要一个数据库连接池,此处以druid为例,使用者可以自行替换成实现了DataSource接口的连接池。

我们首先创建一个连接池,并交由JdbcConnection类管理:

// 此处省略连接池配置
val druid = new DruidDataSource()

// 此处第一个参数为连接池,第二个参数为数据库类型的枚举,用户可自行根据项目需要替换
val db = new JdbcConnection(druid, DB.MYSQL)

拿到了JdbcConnection的实例之后,我们可以在此之上操作数据库:

执行sql

对于insert、update、delete等修改数据的sql,我们可以使用run方法执行,并返回Int类型的受影响行数:

val insert = insertInto(User)(User.id, User.name)((1, "x"), (2, "y"))
val result: Int = db.run(insert)

如果insert操作时,数据库中有自增主键,我们可以使用runAndReturnKey方法,返回一个List[Long]类型的结果集:

val insert = insertInto(User)(User.id, User.name)((1, "x"), (2, "y"))
val result: List[Long] = db.runAndReturnKey(insert)

接收查询结果

对于select、values临时表等查询类sql,可以使用JdbcConnection类进行查询,并返回查询结果。

支持的单条结果映射类型有三种:

  1. 映射到继承了TableEntity的实体类(可空字段将会被映射到Option类型);
  2. 映射到一个Tuple,Tuple的实际类型取决于select方法的参数(此时不能使用select *或者无类型参数的col,否则会出现运行时异常);
  3. 映射到一个Map[String, Any],map的key为字段名(或查询中的别名),value为查询结果的值。

查询结果集

使用queryMap、queryTuple、queryEntity来查询结果集,返回结果是一个List,如果没有查询到结果,返回一个0元素的List:

val select = select (User.id, User.name) from User

val result1: List[Map[String, Any]] = db.queryMap(select)
val result2: List[(Int, String | Null)] = db.queryTuple(select)
val result3: List[User] = db.queryEntity[User](select)

查询单条结果

使用findMap、findTuple、findEntity来查询单条结果,返回结果是一个Option,如果没有查询到结果,返回一个None:

val select = select (User.id, User.name) from User

val result1: Option[Map[String, Any]] = db.findMap(select)
val result2: Option[(Int, String | Null)] = db.findTuple(select)
val result3: Option[User] = db.findEntity[User](select)

分页查询

使用pageMap、pageTuple、pageEntity来进行分页查询,返回结果是一个Page类型,其定义如下:

case class Page[T](totalPage: Int = 0, totalCount: Int = 0, data: List[T] = List())

分页查询参数中除了需要传入查询dsl之外,还需要依次传入一页的结果集条数,页数,和是否需要查询count;

其中最后一个参数,默认值为true,为true时会附带执行一个查询count的sql,如无必要,请传入false,以便提升效率:

val select = select (User.id, User.name) from User

val result1: Page[Map[String, Any]] = db.pageMap(select)(10, 1)
val result2: Page[(Int, String | Null)] = db.pageTuple(select)(10, 1, true)
val result3: Page[User] = db.pageEntity[User](select)(10, 1, false)

查询count

使用fetchCount方法来查询结果集大小,返回结果是Int类型。

此处会对生成的sql语法树进行复制,并去除对于查询count无用的order by和limit信息,并把select列表替换成COUNT(*),以提高查询效率

val select = select (User.id, User.name) from User orderBy User.id.asc limit 10 offset 10

val count: Int = db.fetchCount(select)

此处实际生成的sql为:

SELECT COUNT(*) AS count FROM user

数据库事务

使用transaction函数来产生一个事务,该函数是一个高阶函数。

高阶函数中如果出现了异常,将会进行回滚,如无异常,将会提交事务,高阶函数结算后回收数据库连接:

db.transaction { t =>
    t.run(...) // 高阶函数里可以执行一些查询
    
    throw Exception() // 出现异常后会回滚事务
}

我们也可以手动传入java.sql.Connection中定义的隔离级别,比如:

db.transaction(TRANSACTION_READ_UNCOMMITTED, { t =>
    t.run(...)
})

致谢

easysql的scala版本的诞生,需要感谢两个人:

jilen:https://github.com/jilen

scala最好的orm:quill的核心作者,在此感谢jilen指引我前进的方向。

氘発孜然:https://github.com/daofaziran1

在此感谢氘発孜然在scala3的macro等方面给予我的帮助。

Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

简介

暂无描述 展开 收起
Scala
Apache-2.0
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
Scala
1
https://gitee.com/daofaziran01/easysql-scala.git
git@gitee.com:daofaziran01/easysql-scala.git
daofaziran01
easysql-scala
easysql-scala
master

搜索帮助