SDP(1):ScalikeJDBC-基本操作介绍
简单来说:JDBC是一种开放标准的跨编程语言、跨数据库类型编程API。各类型数据库产品厂商都会按它的标准要求来提供针对自身产品的JDBC驱动程序。最主要的这是一套成熟的工具,在编程人员中使用很普及。既然我们的目标是开发一套标准的大数据系统集成API,那么采用JDBC系统数据接口可以沿用业内丰富的技术资源、覆盖更多类型用户的编程需要,以及降低使用门槛。对于scala编程语言来讲,ScalikeJDBC是一套最合适满足我们下面开发要求的工具库,因为它可以用最简单易用的方式来实现JDBC的基本功能。
JDBC的基本组件和工作原理比较简单:核心组件就connection、statement(preparedstatement)两个对象。connection提供与数据库的连接以及数据处理的运行环境,statement是connection的一个属性,包含了可运行的SQL语句及对它们的各种运算方法。下面我们就用ScalikeJDBC官网上的例子来示范说明ScalikeJDBC的应用:
- import scalikejdbc._
- object JDBCIntro extends App {
- //standard java procedure to register driver
- Class.forName("org.h2.Driver")
- //default connectionPool named 'default
- //ConnectionPool.singleton("jdbc:h2:mem:hello", "user", "pass")
- //or ConnectionPool.add('default,"jdbc:h2:mem:hello", "user", "pass")
- //db name is 'h2mem
- ConnectionPool.add('h2mem,"jdbc:h2:mem:hello", "user", "pass")
首先,登记jdbc驱动程序及准备连接池connection-pool。注意,ScalikeJDBC提供了固定名为'default的默认连接池。所以ConnectionPool.singleton == ConnectionPool.add('default。在'default连接池上的操作都无需提供名称,如:(DB==NamedDB('default)),(AutoSession==NamedAutoSession('default))等。上面的代码示范了手工方式的连接池设置。如果应用需要替换增加数据库或调整数据库参数必须通过修改代码来实现。实用的程序应该使用配置文件的方式进行数据库设置可以轻松随意调整数据库参数,我们会在下一章介绍配置文件使用方法。
跟着是一条SQL语句的构建和运算:
- //construct SQL object
- val createSQL: SQL[Nothing,NoExtractor] =SQL("""
- create table members (
- id bigint primary key auto_increment,
- name varchar() not null,
- description varchar(),
- birthday date,
- created_at timestamp not null
- )""")
- //implicit val session = AutoSession 'default
- //run this SQL
- createSQL.execute().apply()(NamedAutoSession('h2mem)) //autoCommit
通过SQL("...")方法构建SQL[A,E]类型的实例,用execute()方法设定不同的运算方式,然后用apply()(implicit s:DBSession)来实际运算SQL,DBSession其实就是个connection实例。相关SQL[A,E]的类型定义如下:
- /**
- * SQL abstraction.
- *
- * @param statement SQL template
- * @param rawParameters parameters
- * @param f extractor function
- * @tparam A return type
- */
- abstract class SQL[A, E <: WithExtractor](
- val statement: String,
- private[scalikejdbc] val rawParameters: Seq[Any]
- )(f: WrappedResultSet => A)
- extends Extractor[A] {...}
- /**
- * Extractor
- */
- private[scalikejdbc] trait Extractor[A] {
- def extractor: (WrappedResultSet) => A
- }
SQL("...")调用了apply构建方法:
- object SQL {
- ...
- def apply[A](sql: String): SQL[A, NoExtractor] = new SQLToTraversableImpl[A, NoExtractor](sql, Seq.empty)(noExtractor[A](
- ErrorMessage.THIS_IS_A_BUG
- ))
- }
- /**
- * SQL which execute java.sql.Statement#executeQuery() and returns the result as scala.collection.Traversable value.
- *
- * @param statement SQL template
- * @param rawParameters parameters
- * @param extractor extractor function
- * @tparam A return type
- */
- class SQLToTraversableImpl[A, E <: WithExtractor](
- override val statement: String, override val rawParameters: Seq[Any]
- )(
- override val extractor: WrappedResultSet => A
- )
- extends SQL[A, E](statement, rawParameters)(extractor)
- with SQLToTraversable[A, E] {
- override protected def withParameters(params: Seq[Any]): SQLToResult[A, E, Traversable] = {
- new SQLToTraversableImpl[A, E](statement, params)(extractor)
- }
- override protected def withStatementAndParameters(state: String, params: Seq[Any]): SQLToResult[A, E, Traversable] = {
- new SQLToTraversableImpl[A, E](state, params)(extractor)
- }
- override protected def withExtractor[B](f: WrappedResultSet => B): SQLToResult[B, HasExtractor, Traversable] = {
- new SQLToTraversableImpl[B, HasExtractor](statement, rawParameters)(f)
- }
- }
SQLToTraversableImpl类型提供了SQL语句参数组合方法并构建SQLToResult类型:
- trait SQLToResult[A, E <: WithExtractor, C[_]] extends SQL[A, E] with Extractor[A] {
- import GeneralizedTypeConstraintsForWithExtractor._
- def result[AA](f: WrappedResultSet => AA, session: DBSession): C[AA]
- val statement: String
- private[scalikejdbc] val rawParameters: Seq[Any]
- def apply()(
- implicit
- session: DBSession,
- context: ConnectionPoolContext = NoConnectionPoolContext,
- hasExtractor: ThisSQL =:= SQLWithExtractor
- ): C[A] = {
- val attributesSwitcher = createDBSessionAttributesSwitcher()
- val f: DBSession => C[A] = s => result[A](extractor, DBSessionWrapper(s, attributesSwitcher))
- // format: OFF
- session match {
- case AutoSession | ReadOnlyAutoSession => DB.readOnly(f)
- case NamedAutoSession(name, _) => NamedDB(name, session.settings).readOnly(f)
- case ReadOnlyNamedAutoSession(name, _) => NamedDB(name, session.settings).readOnly(f)
- case _ => f(session)
- }
- // format: ON
- }
- }
这个SQLToResult的apply方法就是SQL-readonly-query的运算方法。
SQL[A,E]中类型参数A是运算SQL返回值类型,E是一种函数类型:(WrappedResultSet => A),用来把返回结果转换成A类型值。SQL类型还提供了bind,bindByName方法来绑定SQL参数。SQL是个泛式的类型,除默认转换成Query型的SQLToResult类型外还可以转换成execute、update、batch等等其它SQL类型:
- /**
- * Binds parameters for batch
- *
- * @param parameters parameters
- * @return SQL for batch
- */
- def batch(parameters: Seq[Any]*): SQLBatch = {
- new SQLBatch(statement, parameters, tags)
- }
- /**
- * Binds parameters for largeBatch
- *
- * @param parameters parameters
- * @return SQL for batch
- */
- def largeBatch(parameters: Seq[Any]*): SQLLargeBatch =
- new SQLLargeBatch(statement, parameters, tags)
- /**
- * Binds parameters for batch
- *
- * @param parameters parameters
- * @return SQL for batch
- */
- def batchAndReturnGeneratedKey(parameters: Seq[Any]*): SQLBatchWithGeneratedKey = {
- new SQLBatchWithGeneratedKey(statement, parameters, tags)(None)
- }
- /**
- * Binds parameters for batch
- *
- * @param generatedKeyName generated key name
- * @param parameters parameters
- * @return SQL for batch
- */
- def batchAndReturnGeneratedKey(generatedKeyName: String, parameters: Seq[Any]*): SQLBatchWithGeneratedKey = {
- new SQLBatchWithGeneratedKey(statement, parameters, tags)(Some(generatedKeyName))
- }
- /**
- * Binds parameters for batch
- *
- * @param parameters parameters
- * @return SQL for batch
- */
- def batchByName(parameters: Seq[(Symbol, Any)]*): SQLBatch = {
- val names = SQLTemplateParser.extractAllParameters(statement)
- val sqlWithPlaceHolders = SQLTemplateParser.convertToSQLWithPlaceHolders(statement)
- val _sql = validateAndConvertToNormalStatement(statement, sqlWithPlaceHolders, names, _settings, parameters.headOption.getOrElse(Seq.empty))._1
- val _parameters: Seq[Seq[Any]] = parameters.map { p =>
- validateAndConvertToNormalStatement(statement, sqlWithPlaceHolders, names, _settings, p)._2
- }
- new SQLBatch(_sql, _parameters, tags)
- }
- /**
- * Set execution type as execute
- *
- * @return SQL instance
- */
- def execute(): SQLExecution = {
- new SQLExecution(statement, rawParameters, tags)((stmt: PreparedStatement) => {})((stmt: PreparedStatement) => {})
- }
- /**
- * Set execution type as execute with filters
- *
- * @param before before filter
- * @param after after filter
- * @return SQL instance
- */
- def executeWithFilters(before: (PreparedStatement) => Unit, after: (PreparedStatement) => Unit): SQLExecution = {
- new SQLExecution(statement, rawParameters, tags)(before)(after)
- }
- /**
- * Set execution type as executeUpdate
- *
- * @return SQL instance
- */
- def executeUpdate(): SQLUpdate = update()
- /**
- * Set execution type as executeUpdate with filters
- *
- * @param before before filter
- * @param after after filter
- * @return SQL instance
- */
- def executeUpdateWithFilters(before: (PreparedStatement) => Unit, after: (PreparedStatement) => Unit): SQLUpdate = {
- updateWithFilters(before, after)
- }
- /**
- * Set execution type as executeUpdate
- *
- * @return SQL instance
- */
- def update(): SQLUpdate = {
- new SQLUpdate(statement, rawParameters, tags)((stmt: PreparedStatement) => {})((stmt: PreparedStatement) => {})
- }
- /**
- * Set execution type as executeUpdate with filters
- *
- * @param before before filter
- * @param after after filter
- * @return SQL instance
- */
- def updateWithFilters(before: (PreparedStatement) => Unit, after: (PreparedStatement) => Unit): SQLUpdate = {
- new SQLUpdate(statement, rawParameters, tags)(before)(after)
- }
- /**
- * Set execution type as `executeLargeUpdate`
- *
- * @return SQL instance
- */
- def largeUpdate(): SQLLargeUpdate =
- new SQLLargeUpdate(statement, rawParameters, tags)(_ => {})(_ => {})
- /**
- * Set execution type as `executeLargeUpdate` with filters
- *
- * @param before before filter
- * @param after after filter
- * @return SQL instance
- */
- def largeUpdateWithFilters(before: PreparedStatement => Unit, after: PreparedStatement => Unit): SQLLargeUpdate =
- new SQLLargeUpdate(statement, rawParameters, tags)(before)(after)
- /**
- * Set execution type as updateAndReturnGeneratedKey
- *
- * @return SQL instance
- */
- def updateAndReturnGeneratedKey(): SQLUpdateWithGeneratedKey = {
- updateAndReturnGeneratedKey()
- }
- def updateAndReturnGeneratedKey(name: String): SQLUpdateWithGeneratedKey = {
- new SQLUpdateWithGeneratedKey(statement, rawParameters, this.tags)(name)
- }
- def updateAndReturnGeneratedKey(index: Int): SQLUpdateWithGeneratedKey = {
- new SQLUpdateWithGeneratedKey(statement, rawParameters, this.tags)(index)
- }
以上每种SQL类型都是一种特别的SQL运算方式,即它们都有自己独特的apply()函数如:
- /**
- * SQL which execute java.sql.Statement#execute().
- *
- * @param statement SQL template
- * @param parameters parameters
- * @param before before filter
- * @param after after filter
- */
- class SQLExecution(val statement: String, val parameters: Seq[Any], val tags: Seq[String] = Nil)(
- val before: (PreparedStatement) => Unit
- )(
- val after: (PreparedStatement) => Unit
- ) {
- def apply()(implicit session: DBSession): Boolean = {
- val attributesSwitcher = new DBSessionAttributesSwitcher(SQL("").tags(tags: _*))
- val f: DBSession => Boolean = DBSessionWrapper(_, attributesSwitcher).executeWithFilters(before, after, statement, parameters: _*)
- // format: OFF
- session match {
- case AutoSession => DB.autoCommit(f)
- case NamedAutoSession(name, _) => NamedDB(name, session.settings).autoCommit(f)
- case ReadOnlyAutoSession => DB.readOnly(f)
- case ReadOnlyNamedAutoSession(name, _) => NamedDB(name, session.settings).readOnly(f)
- case _ => f(session)
- }
- // format: ON
- }
- }
- /**
- * SQL which execute java.sql.Statement#executeUpdate().
- *
- * @param statement SQL template
- * @param parameters parameters
- * @param before before filter
- * @param after after filter
- */
- class SQLUpdate(val statement: String, val parameters: Seq[Any], val tags: Seq[String] = Nil)(
- val before: (PreparedStatement) => Unit
- )(
- val after: (PreparedStatement) => Unit
- ) {
- def apply()(implicit session: DBSession): Int = {
- val attributesSwitcher = new DBSessionAttributesSwitcher(SQL("").tags(tags: _*))
- session match {
- case AutoSession =>
- DB.autoCommit(DBSessionWrapper(_, attributesSwitcher).updateWithFilters(before, after, statement, parameters: _*))
- case NamedAutoSession(name, _) =>
- NamedDB(name, session.settings).autoCommit(DBSessionWrapper(_, attributesSwitcher).updateWithFilters(before, after, statement, parameters: _*))
- case ReadOnlyAutoSession =>
- DB.readOnly(DBSessionWrapper(_, attributesSwitcher).updateWithFilters(before, after, statement, parameters: _*))
- case ReadOnlyNamedAutoSession(name, _) =>
- NamedDB(name, session.settings).readOnly(DBSessionWrapper(_, attributesSwitcher).updateWithFilters(before, after, statement, parameters: _*))
- case _ =>
- DBSessionWrapper(session, attributesSwitcher).updateWithFilters(before, after, statement, parameters: _*)
- }
- }
- }
我们也可以把多个SQL语句共同放在一个session-block里:
- import org.joda.time._
- // DB autoCommit == NamedDB('default) autoCommit
- NamedDB('h2mem) autoCommit { implicit session =>
- val insertSql: SQL[Nothing,NoExtractor] =
- SQL("insert into members (name, birthday, created_at) values (?, ?, ?)")
- val createdAt: DateTime = DateTime.now
- insertSql.bind("Alice", Some(new LocalDate("1980-01-01")), createdAt).update.apply() //commit
- insertSql.bind("Bob", None, createdAt).update.apply() //commit
- }
这是种批次更新SQL:使用了共同PreparedStatement模版,然后绑定不同的参数进行运算。上面的代码是一种loan-pattern编程模式。除autoCommit还有readOnly,localTx等:
- /**
- * Provides auto-commit session block.
- * @param execution block
- * @tparam A return type
- * @return result value
- */
- def autoCommit[A](execution: DBSession => A): A = {
- if (autoCloseEnabled) using(conn)(_ => execution(autoCommitSession()))
- else execution(autoCommitSession())
- }
- /**
- * Provides read-only session block.
- * @param execution block
- * @tparam A return type
- * @return result value
- */
- def readOnly[A](execution: DBSession => A): A = {
- if (autoCloseEnabled) using(conn)(_ => execution(readOnlySession()))
- else execution(readOnlySession())
- }
- /**
- * Provides local-tx session block.
- * @param execution block
- * @tparam A return type
- * @return result value
- */
- def localTx[A](execution: DBSession => A)(implicit boundary: TxBoundary[A] = defaultTxBoundary[A]): A = {
- val doClose = if (autoCloseEnabled) () => conn.close() else () => ()
- val tx = newTx
- begin(tx)
- val txResult = try {
- rollbackIfThrowable[A] {
- val session = DBSession(
- conn = conn,
- tx = Option(tx),
- connectionAttributes = connectionAttributes,
- settings = this.settingsProvider
- )
- val result: A = execution(session)
- boundary.finishTx(result, tx)
- }
- } catch {
- case e: Throwable => doClose(); throw e
- }
- boundary.closeConnection(txResult, doClose)
- }
在autoCommit域中每一次SQL运算都自动提交事务,不支持交易回滚rollback。在下面的localTx示范里,任何运算出现异常都会导致整体事务的回滚并抛出异常:
- //data model
- case class Member(
- id: Long,
- name: String,
- description: Option[String] = None,
- birthday: Option[LocalDate] = None,
- createdAt: DateTime)
- def create(name: String, birthday: Option[LocalDate], remarks: Option[String])(implicit session: DBSession): Member = {
- val insertSQL: SQL[Nothing,NoExtractor] =
- sql"""insert into members (name, birthday, description, created_at)
- values (${name}, ${birthday}, ${remarks}, ${DateTime.now})"""
- val id: Long = insertSQL.updateAndReturnGeneratedKey.apply()
- Member(id, name, remarks, birthday,DateTime.now)
- }
- val users = List(
- ("John",new LocalDate("2008-03-01"),"youngest user"),
- ("Susan",new LocalDate("2000-11-03"),"middle aged user"),
- ("Peter",new LocalDate("1983-01-21"),"oldest user"),
- )
- import scala.util._ //Try
- import scalikejdbc.TxBoundary.Try._
- val result: Try[List[Member]] =
- NamedDB('h2mem) localTx { implicit session =>
- Try {
- val members: List[Member] = users.map { person =>
- create(person._1, Some(person._2), Some(person._3))
- }
- throw new RuntimeException("fail test. boooommmm!!!!!")
- members
- }
- }
- result match {
- case Success(mlist) => println(s"batch added members: $mlist")
- case Failure(err) => println(s"${err.getMessage}")
- }
在上面这段代码中localTx block中的所有运算是包嵌在Try{}里的,即交易界限transaction-boundary,任何异常都被框定在这个界限里。以上的例子中不会抛出异常,返回结果包括了运算的所有状态。
我们也可以用case class或其它类型来模拟数据行类型以实现数据行的强类型操作:
- //data row converter
- val toMember = (rs: WrappedResultSet) => Member(
- id = rs.long("id"),
- name = rs.string("name"),
- description = rs.stringOpt("description"),
- birthday = rs.jodaLocalDateOpt("birthday"),
- createdAt = rs.jodaDateTime("created_at")
- )
- val selectSQL: SQL[Member,HasExtractor] = sql"""select * from members""".map(toMember)
- val members: List[Member] = NamedDB('h2mem) readOnly { implicit session =>
- selectSQL.list.apply()
- }
- println(s"all members: $members")
- NamedDB('h2mem).close()
上面的代码中使用了sql"""...""",这是标准的scala-string-interpolation,是构建SQL[A,E]的另一种方法。
下面是本次讨论的示范源代码:
- import scalikejdbc._
- object JDBCIntro extends App {
- //standard java procedure to register driver
- Class.forName("org.h2.Driver")
- //must have a db named 'default
- //ConnectionPool.singleton("jdbc:h2:mem:hello", "user", "pass")
- //==ConnectionPool.add('default,"jdbc:h2:mem:hello", "user", "pass")
- //db name is 'h2mem
- ConnectionPool.add('h2mem,"jdbc:h2:mem:hello", "user", "pass")
- //construct SQL object
- val createSQL: SQL[Nothing,NoExtractor] =SQL("""
- create table members (
- id bigint primary key auto_increment,
- name varchar() not null,
- description varchar(),
- birthday date,
- created_at timestamp not null
- )""")
- //implicit val session = AutoSession 'default
- //run this SQL
- createSQL.execute().apply()(NamedAutoSession('h2mem)) //autoCommit
- import org.joda.time._
- // DB autoCommit == NamedDB('default) autoCommit
- NamedDB('h2mem) autoCommit { implicit session =>
- val insertSql: SQL[Nothing,NoExtractor] =
- SQL("insert into members (name, birthday, created_at) values (?, ?, ?)")
- val createdAt: DateTime = DateTime.now
- insertSql.bind("Alice", Some(new LocalDate("1980-01-01")), createdAt).update.apply() //commit
- insertSql.bind("Bob", None, createdAt).update.apply() //commit
- }
- //data model
- case class Member(
- id: Long,
- name: String,
- description: Option[String] = None,
- birthday: Option[LocalDate] = None,
- createdAt: DateTime)
- def create(name: String, birthday: Option[LocalDate], remarks: Option[String])(implicit session: DBSession): Member = {
- val insertSQL: SQL[Nothing,NoExtractor] =
- sql"""insert into members (name, birthday, description, created_at)
- values (${name}, ${birthday}, ${remarks}, ${DateTime.now})"""
- val id: Long = insertSQL.updateAndReturnGeneratedKey.apply()
- Member(id, name, remarks, birthday,DateTime.now)
- }
- val users = List(
- ("John",new LocalDate("2008-03-01"),"youngest user"),
- ("Susan",new LocalDate("2000-11-03"),"middle aged user"),
- ("Peter",new LocalDate("1983-01-21"),"oldest user"),
- )
- import scala.util._ //Try
- import scalikejdbc.TxBoundary.Try._
- val result: Try[List[Member]] =
- NamedDB('h2mem) localTx { implicit session =>
- Try {
- val members: List[Member] = users.map { person =>
- create(person._1, Some(person._2), Some(person._3))
- }
- throw new RuntimeException("fail test. boooommmm!!!!!")
- members
- }
- }
- result match {
- case Success(mlist) => println(s"batch added members: $mlist")
- case Failure(err) => println(s"${err.getMessage}")
- }
- //data row converter
- val toMember = (rs: WrappedResultSet) => Member(
- id = rs.long("id"),
- name = rs.string("name"),
- description = rs.stringOpt("description"),
- birthday = rs.jodaLocalDateOpt("birthday"),
- createdAt = rs.jodaDateTime("created_at")
- )
- val selectSQL: SQL[Member,HasExtractor] = sql"""select * from members""".map(toMember)
- val members: List[Member] = NamedDB('h2mem) readOnly { implicit session =>
- selectSQL.list.apply()
- }
- println(s"all members: $members")
- NamedDB('h2mem).close()
- }
SDP(1):ScalikeJDBC-基本操作介绍的更多相关文章
- PyQuery基本操作介绍
PyQuery基本操作介绍 PyQuery为Python提供一个类似于jQuery对HTML的操作方式,可以使用jQuery的语法对html文档进行查询操作. 本文以百度首页为例来介绍PyQuery的 ...
- vim编辑器基本操作介绍
vim编辑器基本操作介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 可能很多小伙伴都听说过vi编辑器或是vim编辑器.它们是Unix和Linux世界最流行的编辑器之一,他们的特 ...
- SpringBoot中Redis的set、map、list、value、实体类等基本操作介绍
今天给大家介绍一下SpringBoot中Redis的set.map.list.value等基本操作的具体使用方法 上一节中给大家介绍了如何在SpringBoot中搭建Redis缓存数据库,这一节就针对 ...
- Activiti工作流(一)之基本操作介绍
工作流的概念: 工作流(Workflow),就是"业务过程的部分或整体在计算机应用环境下的自动化",它主要解决的是"使在多个参与者之间按照某种预定义的规则传递文档.信息或 ...
- 基于MyBatis3.0.6的基本操作介绍
每 一 个 MyBatis 的 应 用 程 序 都 以 一 个 SqlSessionFactory 对 象 的 实 例 为 核 心 .SqlSessionFactory本身是由SqlSessionFa ...
- tensorflow基本操作介绍
1.tensorflow的基本运作 为了快速的熟悉TensorFlow编程,下面从一段简单的代码开始: import tensorflow as tf #定义‘符号’变量,也称为占位符 a = tf. ...
- JDBC基本操作介绍
一 .JDBC主要的API介绍 JDBC为开发人员提供了一套标准的API,都是由JAVA语言编写的类和接口.用于连接数据库和执行SQL语句.JDBC也是JAVA核心类库的一部分,位于Java.sql包 ...
- Jquery EasyUI的添加,修改,删除,查询等基本操作介绍
http://www.jb51.net/article/42016.htm 初识Jquery EasyUI看了一些博主用其开发出来的项目,页面很炫,感觉功能挺强大,效果也挺不错,最近一直想系统学习一套 ...
- 【HBase】表模型和基本操作介绍
目录 HBase表模型 常用Shell操作 HBase表模型 创建一个hbase表,必须要有表名和列族名 列族 -- columnFamily,就是上图中的Column Family1 userInf ...
- redis基本操作介绍
一.字符串 单个设置:set key value,如果key不存在则设置键值对,如果存在则修改 批量设置:mset key1 value1 [key2 value2] 单个获取:get key,如果k ...
随机推荐
- cut 命令详解
cut 作用:按列提取文件 参数: -d 指明列分隔符 -f 选择输出的区域 -c 指定字符位置 -b :以字节为单位进行分割.这些字节位置将忽略多字节字符边界,除非也指定了 -n 标志. -c : ...
- Nginx (三) 使用Keepalived搭建高可用服务
Nginx可以实现高并发反向代理,实现负载均衡,但是有个问题就是Nginx是单点的.如果Nginx故障,则整个服务将会处于不可用状态.所以我们就需要想办法让nginx高可用,即使一个Nginx宕机,还 ...
- 发布 Google Chrome插件教程
换个视角,世界不一样.嘘~~~ 如果你会使用js的话,那么你就可以自己动手写一个chrome插件,而且非常容易.google是一个全球化的平台,想想自己的程序被世界人民所使用,是不是很激动? 注册开发 ...
- Odwiedziny[POI 2015]
题目描述 给定一棵n个点的树,树上每条边的长度都为1,第i个点的权值为a[i]. Byteasar想要走遍这整棵树,他会按照某个1到n的全排列b走n-1次,第i次他会从b[i]点走到b[i+1]点,并 ...
- SpringMvc开发步骤
1.导入基本jar包 2.在Web.xml中配置DispatcherServlet <!-- 配置 DispatcherServlet --> <servlet> <se ...
- [Spark內核] 第42课:Spark Broadcast内幕解密:Broadcast运行机制彻底解密、Broadcast源码解析、Broadcast最佳实践
本课主题 Broadcast 运行原理图 Broadcast 源码解析 Broadcast 运行原理图 Broadcast 就是将数据从一个节点发送到其他的节点上; 例如 Driver 上有一张表,而 ...
- Go同步和异步执行多个任务封装
同步执行类RunnerAsync 支持返回超时检测,系统中断检测 错误常量定义 //超时错误 var ErrTimeout = errors.New("received timeout&qu ...
- 自学Aruba之路
自学Aruba之路[第一回]:体系结构 1.1 自学Aruba1.1-Aruba体系结构-产品线 1.2 自学Aruba1.2-WLAN一些基本常识 1.3 自学Aruba1.3-W ...
- Java学习笔记14(面向对象七:final、static)
final:意为最终,不可变,是一个修饰词 有时候一个类地功能被开发好了,不想让子类重写,修改,这里就会用到final关键字 final修饰类: 不可以被继承,但是可以继承其他类 示例: public ...
- ABAP开发人员未来应该学些什么
2007年1月我大学毕业进入SAP Chengdu从事开发工作,到今天已经整整11年了. 这张图在ABAP开发圈子里曾广为流传: 为了避免这种窘境发生在ABAPer身上,我们应该做点什么呢? 2017 ...