Java数据库连接池--DBCP浅析

转载自   Java数据库连接池--DBCP浅析

前言
对于数据库连接池, 想必大家都已经不再陌生, 这里仅仅设计Java中的两个常用数据库连接池: DBCP和C3P0(后续会更新). 

一. 为何要使用数据库连接池
假设网站一天有很大的访问量,数据库服务器就需要为每次连接创建一次数据库连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、拓机。
数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现的尤为突出.对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标.数据库连接池正式针对这个问题提出来的.数据库连接池负责分配,管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个

数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中, 这些数据库连接的数量是由最小数据库连接数来设定的.无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量.连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中.

      数据库连接池的最小连接数和最大连接数的设置要考虑到以下几个因素:

  1, 最小连接数:是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费.
  2, 最大连接数:是连接池能申请的最大连接数,如果数据库连接请求超过次数,后面的数据库连接请求将被加入到等待队列中,这会影响以后的数据库操作
  3, 如果最小连接数与最大连接数相差很大:那么最先连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新的数据库连接.不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,他将被           放到连接池中等待重复使用或是空间超时后被释放.

二, 数据库连接池的原理及实现
到了这里我们已经知道数据库连接池是用来做什么的了, 下面我们就来说数据库连接池是如何来实现的. 
1, 建立一个数据库连接池pool, 池中有若干个Connection 对象, 当用户发来请求需要进行数据库交互时则会使用池中第一个Connection对象.
2, 当本次连接结束时, 再将这个Connection对象归还池中, 这样就可以保证池中一直有足够的Connection对象.

public class SimplePoolDemo {//创建一个连接池private static LinkedList<Connection> pool = new LinkedList<Connection>(); //初始化10个连接static{try {for (int i = 0; i < 10; i++) {Connection conn = DBUtils.getConnection();//得到一个连接
                pool.add(conn);}} catch (Exception e) {throw new ExceptionInInitializerError("数据库连接失败,请检查配置");}}//从池中获取一个连接public static Connection getConnectionFromPool(){return pool.removeFirst();//移除一个连接对象
    }//释放资源public static void release(Connection conn){pool.addLast(conn);}
}

以上的Demo就是一个简单的数据库连接池的例子, 先在静态代码块中初始化10个Connection对象, 当本次请求结束后再将Connection添加进池中. 
这只是我们自己手动去实现的, 当然在实际生产中并不需要我们去手动去写数据库连接池. 下面就重点讲DBCP和C3P0的实现方式.

三, DBCP连接池

首先我们来看DBCP 的例子, 然后根据例子来分析:

#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/day14
username=root
password=abc#<!-- 初始化连接 -->
initialSize=10#最大连接数量
maxActive=50#<!-- 最大空闲连接 -->
maxIdle=20#<!-- 最小空闲连接 -->
minIdle=5#<!-- 超时等待时间以毫秒为单位 60000毫秒/1000等于60秒 -->
maxWait=60000#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;] 
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=utf8#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=REPEATABLE_READDBCP配置文件


DBCPUtils:

public class DBCPUtils {private static DataSource ds;//定义一个连接池对象static{try {Properties pro = new Properties();pro.load(DBCPUtils.class.getClassLoader().getResourceAsStream("dbcpconfig.properties"));ds = BasicDataSourceFactory.createDataSource(pro);//得到一个连接池对象} catch (Exception e) {throw new ExceptionInInitializerError("初始化连接错误,请检查配置文件!");}}//从池中获取一个连接public static Connection getConnection() throws SQLException{return ds.getConnection();}public static void closeAll(ResultSet rs,Statement stmt,Connection conn){if(rs!=null){try {rs.close();} catch (SQLException e) {e.printStackTrace();}}if(stmt!=null){try {stmt.close();} catch (SQLException e) {e.printStackTrace();}}if(conn!=null){try {conn.close();//关闭} catch (SQLException e) {e.printStackTrace();}}}
}

在这个closeAll方法中, conn.close(); 这个地方会将connection还回到池子中吗? DataSource 中是如何处理close()方法的呢?
上面的两个问题就让我们一起来看看源码是如何来实现的吧.
这里我们从ds.getConnection();入手, 看看一个数据源DataSource是如何创建connection的.
用eclipse导入:commons-dbcp-1.4-src.zip和commons-pool-1.5.6-src.zip则可查看源码:

BasicDataSource.class:(implements DataSource)

public Connection getConnection() throws SQLException {return createDataSource().getConnection();
}

3.1 接下来看createDataSoruce() 方法:

protected synchronized DataSource createDataSource()throws SQLException {if (closed) {throw new SQLException("Data source is closed");}// Return the pool if we have already created itif (dataSource != null) {return (dataSource);}// create factory which returns raw physical connectionsConnectionFactory driverConnectionFactory = createConnectionFactory();// create a pool for our connectionscreateConnectionPool();// Set up statement pool, if desiredGenericKeyedObjectPoolFactory statementPoolFactory = null;if (isPoolPreparedStatements()) {statementPoolFactory = new GenericKeyedObjectPoolFactory(null,-1, // unlimited maxActive (per key)GenericKeyedObjectPool.WHEN_EXHAUSTED_FAIL,0, // maxWait1, // maxIdle (per key)maxOpenPreparedStatements);}// Set up the poolable connection factorycreatePoolableConnectionFactory(driverConnectionFactory, statementPoolFactory, abandonedConfig);// Create and return the pooling data source to manage the connectionscreateDataSourceInstance();try {for (int i = 0 ; i < initialSize ; i++) {connectionPool.addObject();}} catch (Exception e) {throw new SQLNestedException("Error preloading the connection pool", e);}return dataSource;
}

从源代码可以看出,createDataSource()方法通过7步,逐步构造出一个数据源,下面是详细的步骤:

   1、检查数据源是否关闭或者是否创建完成,如果关闭了就抛异常,如果已经创建完成就直接返回。

   2、调用createConnectionFactory()创建JDBC连接工厂driverConnectionFactory,这个工厂使用数据库驱动来创建最底层的JDBC连接

   3、调用createConnectionPool()创建数据源使用的连接池,连接池顾名思义就是缓存JDBC连接的地方。

   4、如果需要就设置statement的缓存池,这个一般不需要设置

   5、调用createPoolableConnectionFactory创建PoolableConnection的工厂,这个工厂使用上述driverConnectionFactory来创建底层JDBC连接,然后包装出一个PoolableConnection,这个PoolableConnection与连接池设置了一对多的关系,也就是说,连接池中存在多个PoolableConnection,每个PoolableConnection都关联同一个连接池,这样的好处是便于该表PoolableConnection的close方法的行为,具体会在后面详细分析。

   6、调用createDataSourceInstance()创建内部数据源

   7、为连接池中添加PoolableConnection

经过以上7步,一个数据源就形成了,这里明确一点,一个数据源本质就是连接池+连接+管理策略。下面,将对每一步做详细的分析。

3.2 JDBC连接工厂driverConnectionFactory的创建过程

protected ConnectionFactory createConnectionFactory() throws SQLException {// Load the JDBC driver classClass driverFromCCL = null;if (driverClassName != null) {try {try {if (driverClassLoader == null) {Class.forName(driverClassName);} else {Class.forName(driverClassName, true, driverClassLoader);}} catch (ClassNotFoundException cnfe) {driverFromCCL = Thread.currentThread().getContextClassLoader().loadClass(driverClassName);}} catch (Throwable t) {String message = "Cannot load JDBC driver class '" +driverClassName + "'";logWriter.println(message);t.printStackTrace(logWriter);throw new SQLNestedException(message, t);}}// Create a JDBC driver instanceDriver driver = null;try {if (driverFromCCL == null) {driver = DriverManager.getDriver(url);} else {// Usage of DriverManager is not possible, as it does not// respect the ContextClassLoaderdriver = (Driver) driverFromCCL.newInstance();if (!driver.acceptsURL(url)) {throw new SQLException("No suitable driver", "08001"); }}} catch (Throwable t) {String message = "Cannot create JDBC driver of class '" +(driverClassName != null ? driverClassName : "") +"' for connect URL '" + url + "'";logWriter.println(message);t.printStackTrace(logWriter);throw new SQLNestedException(message, t);}// Can't test without a validationQueryif (validationQuery == null) {setTestOnBorrow(false);setTestOnReturn(false);setTestWhileIdle(false);}// Set up the driver connection factory we will useString user = username;if (user != null) {connectionProperties.put("user", user);} else {log("DBCP DataSource configured without a 'username'");}String pwd = password;if (pwd != null) {connectionProperties.put("password", pwd);} else {log("DBCP DataSource configured without a 'password'");}ConnectionFactory driverConnectionFactory = new DriverConnectionFactory(driver, url, connectionProperties);return driverConnectionFactory;
}

上面一连串代码干了什么呢?其实就干了两件事:1、获取数据库驱动 2、使用驱动以及参数(url、username、password)构造一个工厂。一旦这个工厂构建完毕了,就可以来生成连接,而这个连接的生成其实是驱动加上配置来完成的.

3.3 创建连接池的过程

protected void createConnectionPool() {// Create an object pool to contain our active connectionsGenericObjectPool gop;if ((abandonedConfig != null) && (abandonedConfig.getRemoveAbandoned())) {gop = new AbandonedObjectPool(null,abandonedConfig);}else {gop = new GenericObjectPool();}gop.setMaxActive(maxActive);gop.setMaxIdle(maxIdle);gop.setMinIdle(minIdle);gop.setMaxWait(maxWait);gop.setTestOnBorrow(testOnBorrow);gop.setTestOnReturn(testOnReturn);gop.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);gop.setNumTestsPerEvictionRun(numTestsPerEvictionRun);gop.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);gop.setTestWhileIdle(testWhileIdle);connectionPool = gop;}

在创建连接池的时候,用到了common-pool里的GenericObjectPool,对于JDBC连接的缓存以及管理其实是交给GenericObjectPool的,DBCP其实只是负责创建这样一种pool然后使用它而已。

3.4 创建statement缓存池
一般来说,statement并不是重量级的对象,创建过程消耗的资源并不像JDBC连接那样重,所以没必要做缓存池化,这里为了简便起见,对此不做分析。

3.5 创建PoolableConnectionFactory

这一步是一个承上启下的过程,承上在于利用上面两部创建的连接工厂和连接池,构建PoolableConnectionFactory,启下则在于为后面的向连接池里添加连接做准备。
   下面先上一张静态的类关系图:

protected void createPoolableConnectionFactory(ConnectionFactory driverConnectionFactory,KeyedObjectPoolFactory statementPoolFactory, AbandonedConfig configuration) throws SQLException {PoolableConnectionFactory connectionFactory = null;try {connectionFactory =new PoolableConnectionFactory(driverConnectionFactory,connectionPool,statementPoolFactory,validationQuery,validationQueryTimeout,connectionInitSqls,defaultReadOnly,defaultAutoCommit,defaultTransactionIsolation,defaultCatalog,configuration);validateConnectionFactory(connectionFactory);} catch (RuntimeException e) {throw e;} catch (Exception e) {throw new SQLNestedException("Cannot create PoolableConnectionFactory (" + e.getMessage() + ")", e);}
}

可以看见,在创建PoolableConnectionFactory的时候,需要用到前面创建的driverConnectionFactory以及连接池connectionPool,那么那个构造函数到底干了先什么呢?

public PoolableConnectionFactory(ConnectionFactory connFactory,ObjectPool pool,KeyedObjectPoolFactory stmtPoolFactory,String validationQuery,int validationQueryTimeout,Collection connectionInitSqls,Boolean defaultReadOnly,boolean defaultAutoCommit,int defaultTransactionIsolation,String defaultCatalog,AbandonedConfig config) {_connFactory = connFactory;_pool = pool;_config = config;_pool.setFactory(this);_stmtPoolFactory = stmtPoolFactory;_validationQuery = validationQuery;_validationQueryTimeout = validationQueryTimeout;_connectionInitSqls = connectionInitSqls;_defaultReadOnly = defaultReadOnly;_defaultAutoCommit = defaultAutoCommit;_defaultTransactionIsolation = defaultTransactionIsolation;_defaultCatalog = defaultCatalog;
}

 它在内部保存了真正的JDBC 连接的工厂以及连接池,然后,通过一句_pool.setFactory(this); 将它自己设置给了连接池。这行代码十分重要,要理解这行代码,首先需要明白common-pool中的GenericObjectPool添加内部元素的一般方法,没错,那就是必须要传入一个工厂Factory。GenericObjectPool添加内部元素时会调用addObject()这个方法,内部其实是调用工厂的makeObejct()方法来创建元素,然后再加入到自己的池中。_pool.setFactory(this)这句代码其实起到了启下的作用,没有它,后面的为连接池添加连接也就不可能完成。

   当创建完工厂后,会有个validateConnectionFactory(connectionFactory);这个方法的作用仅仅是用来验证数据库连接可使用,看代码:

protected static void validateConnectionFactory(PoolableConnectionFactory connectionFactory) throws Exception {Connection conn = null;try {conn = (Connection) connectionFactory.makeObject();connectionFactory.activateObject(conn);connectionFactory.validateConnection(conn);connectionFactory.passivateObject(conn);}finally {connectionFactory.destroyObject(conn);}
}

先是用makeObject方法来创建一个连接,然后做相关验证(就是用一些初始化sql来试着执行一下,看看能不能连接到数据库),然后销毁连接,这里并没有向连接池添加连接,真正的添加连接在后面,不过,我们可以先通过下面一张时序图来看看makeObject方法到底做了什么。

下面是一张整体流程的时序图:

从图中可以看出,makeObject方法的大致流程:从driverConnectionFactory那里拿到底层连接,初始化验证,然后创建PoolableConnection,在创建这个PoolableConnection的时候,将PoolableConnection与连接池关联了起来,真正做到了连接池和连接之间的一对多的关系,这也为改变PoolableConnection的close方法提供了方便。

下面是makeObject方法的源代码:

public Object makeObject() throws Exception {Connection conn = _connFactory.createConnection();if (conn == null) {throw new IllegalStateException("Connection factory returned null from createConnection");}initializeConnection(conn); //初始化,这个过程可有可无if(null != _stmtPoolFactory) {  KeyedObjectPool stmtpool = _stmtPoolFactory.createPool();conn = new PoolingConnection(conn,stmtpool);stmtpool.setFactory((PoolingConnection)conn);}//这里是关键return new PoolableConnection(conn,_pool,_config); 
}

其中PoolableConnection的构造函数如下:

public PoolableConnection(Connection conn, ObjectPool pool, AbandonedConfig config) {super(conn, config);_pool = pool;
}

内部关联了一个连接池,这个连接池的作用体现在PoolableConnection的close方法中:

public synchronized void close() throws SQLException {if (_closed) {// already closedreturn;}boolean isUnderlyingConectionClosed;try {isUnderlyingConectionClosed = _conn.isClosed();} catch (SQLException e) {try {_pool.invalidateObject(this); // XXX should be guarded to happen at most once} catch(IllegalStateException ise) {// pool is closed, so close the connectionpassivate();getInnermostDelegate().close();} catch (Exception ie) {// DO NOTHING the original exception will be rethrown}throw (SQLException) new SQLException("Cannot close connection (isClosed check failed)").initCause(e);}if (!isUnderlyingConectionClosed) {// Normal close: underlying connection is still open, so we// simply need to return this proxy to the pooltry {_pool.returnObject(this); // XXX should be guarded to happen at most once} catch(IllegalStateException e) {// pool is closed, so close the connectionpassivate();getInnermostDelegate().close();} catch(SQLException e) {throw e;} catch(RuntimeException e) {throw e;} catch(Exception e) {throw (SQLException) new SQLException("Cannot close connection (return to pool failed)").initCause(e);}} else {// Abnormal close: underlying connection closed unexpectedly, so we// must destroy this proxytry {_pool.invalidateObject(this); // XXX should be guarded to happen at most once} catch(IllegalStateException e) {// pool is closed, so close the connectionpassivate();getInnermostDelegate().close();} catch (Exception ie) {// DO NOTHING, "Already closed" exception thrown below}throw new SQLException("Already closed.");}
}

一行_pool.returnObject(this)表明并非真的关闭了,而是返还给了连接池。

 到这里, PoolableConnectionFactory创建好了,它使用driverConnectionFactory来创建底层连接,通过makeObject来创建PoolableConnection,这个PoolableConnection通过与connectionPool关联来达到改变close方法的作用,当PoolableConnectionFactory创建好的时候,它自己已经作为一个工厂类被设置到了connectionPool,后面connectionPool会使用这个工厂来生产PoolableConnection,而生成的所有的PoolableConnection都与connectionPool关联起来了,可以从connectionPool取出,也可以还给connectionPool。接下来,让我们来看一看到底怎么去初始化connectionPool。

3.6 创建数据源并初始化连接池

createDataSourceInstance();try {for (int i = 0 ; i < initialSize ; i++) {connectionPool.addObject();}} catch (Exception e) {throw new SQLNestedException("Error preloading the connection pool", e);}

我们先看 createDataSourceInstance();

protected void createDataSourceInstance() throws SQLException {PoolingDataSource pds = new PoolingDataSource(connectionPool);pds.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());pds.setLogWriter(logWriter);dataSource = pds;
}

其实就是创建一个PoolingDataSource,作为底层真正的数据源,这个PoolingDataSource比较简单,这里不做详细介绍

接下来是一个for循环,通过调用connectionPool.addObject();来为连接池添加数据库连接,下面是一张时序图:

可以看出,在3.5中创建的PoolableConnectionFactory在这里起作用了,addObject依赖的正是makeObject,而makeObject在上面也介绍过了。

到此为止,数据源创建好了,连接池里也有了可以使用的连接,而且每个连接和连接池都做了关联,改变了close的行为。这个时候BasicDataSource正是可以工作了,调用getConnection的时候,实际是调用底层数据源的getConnection,而底层数据源其实就是从连接池中获取的连接。

四.总结

 整个数据源最核心的其实就三个东西:一个是连接池,在这里体现为common-pool中的GenericObjectPool它负责缓存和管理连接,所有的配置策略都是由它管理第二个是连接,这里的连接就是PoolableConnection,当然它是对底层连接进行了封装。第三个则是连接池和连接的关系,在此表现为一对多的互相引用。对数据源的构建则是对连接池,连接以及连接池与连接的关系的构建,掌握了这些点,就基本能掌握数据源的构建。

 


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/328881.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用Visual Studio 2015 开发ASP.NET MVC 5 项目部署到Mono/Jexus

最新的Mono 4.4已经支持运行asp.net mvc5项目&#xff0c;有的同学听了这句话就兴高采烈的拿起Visual Studio 2015创建了一个mvc 5的项目&#xff0c;然后部署到Mono上&#xff0c;浏览下发现一堆错误出现&#xff0c;心中一万只草泥马奔腾而来&#xff0c;这也叫支持吗&#x…

体质测试数据统计软件,[体质测试数据excel自动统计表]体质测试数据Excel自动统计模板的研制...

《[体质测试数据excel自动统计表]体质测试数据Excel自动统计模板的研制》由会员分享&#xff0c;可在线阅读&#xff0c;更多相关《[体质测试数据excel自动统计表]体质测试数据Excel自动统计模板的研制(3页珍藏版)》请在装配图网上搜索。1、体质测试数据excel自动统计表体质测试…

常用数据库连接池 (DBCP、c3p0、Druid) 配置说明

转载自 常用数据库连接池 (DBCP、c3p0、Druid) 配置说明1. 引言 1.1 定义 数据库连接是一种关键的有限的昂贵的资源&#xff0c;这一点在多用户的网页应用程序中体现得尤为突出。对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性&#xff0c;影响到程序的性能指标…

分布式基础通信协议:paxos,totem和gossip

背景&#xff1a; 在分布式中&#xff0c;最难解决的一个问题就是多个节点间数据同步问题。为了解决这样的问题&#xff0c;涌现出了各种奇思妙想。只有在解决了如何进行信息同步的基础之上才衍生出形形色色的应用。这里开始介绍几种分布式通信协议。 简单即有效——totem协议:…

计算机专业可以评机械工程师,机械工程师个人评价

人就像一个多面体。仅仅由别人的评价来判定自己是不全面的&#xff0c;当自己也沉淀下来&#xff0c;窥探自己的内心&#xff0c;写下自己的自我评价&#xff0c;这样才可以不断进步。但是自我评价要怎么写呢?下面是学习啦小编带来机械工程师个人评价范文的内容&#xff0c;欢…

分布式系统之消息队列

转载自 分布式系统之消息队列 一、MQ简介 消息队列中间件是分布式系统中重要的组件&#xff0c;主要解决应用耦合&#xff0c;异步消息&#xff0c;流量削锋等问题&#xff0c;实现高性能&#xff0c;高可用&#xff0c;可伸缩和最终一致性架构。 使用较多的消息队…

微软准备开源PowerShell

近日微软再次在向开源投出橄榄枝&#xff0c; PowerShell是面向Windows和Windows Server的自动化平台和脚本语言&#xff0c;帮助用户简化系统的管理。在纳德拉的带领下微软也逐渐走向开放&#xff0c;根据相关爆料称微软有计划在近期内 开源该脚本语言。 知名Windows爆料人士W…

华为荣耀畅玩7c计算机在那,华为荣耀畅玩7C内存多大

华为荣耀畅玩7C内存多大这是很多朋友咨询的问题&#xff0c;华为荣耀畅玩7C凭借高颜值&#xff0c;双摄&#xff0c;人脸识别受到广泛关注&#xff0c;但是也有很多朋友更关心内存多大的问题&#xff0c;下面就来详细介绍一下华为荣耀畅玩7C内存多大。华为荣耀畅玩7C内存多大?…

谈谈数据库连接池的原理

转载自 谈谈数据库连接池的原理这次我们采取技术演进的方式来谈谈数据库连接池的技术出现过程及其原理&#xff0c;以及当下最流行的开源数据库连接池jar包。 一.早期我们怎么进行数据库操作1.原理&#xff1a;一般来说&#xff0c;java应用程序访问数据库的过程是&#xff1a…

华为荣耀20计算机,华为云电脑将停止服务/荣耀新机保护壳曝光/小米新机渲染图曝光...

华为云电脑8月16日停止服务和运营近日&#xff0c;华为官方宣布&#xff0c;“华为云电脑”APP将于北京时间2021年8月15日23点59分停止服务和运营&#xff0c;自那之后用户将再无法登录和使用。华为表示“届时及以后您将无法登录和使用。在此之前&#xff0c;有效套餐仍可连接使…

.NET Core系列 :3 、使用多个项目

通过前面的两篇文章.NET Core系列 &#xff1a; 1、.NET Core 环境搭建和命令行CLI入门 和.NET Core系列 &#xff1a; 2 、project.json 这葫芦里卖的什么药&#xff0c;我们已经知道如何创建新的项目&#xff0c;如何生成并运行我们的应用程序&#xff0c;也知道&#xff08;…

Docker 入门教程

转载自 Docker 入门教程2013年发布至今&#xff0c; Docker 一直广受瞩目&#xff0c;被认为可能会改变软件行业。 但是&#xff0c;许多人并不清楚 Docker 到底是什么&#xff0c;要解决什么问题&#xff0c;好处又在哪里&#xff1f;本文就来详细解释&#xff0c;帮助大家理…

ftb测试软件,EXFO推出FTB-2光纤测试平台

ICCSZ讯 EXFO发布新的基于模块FTB-2 Pro紧凑型平台&#xff0c;这是目前市场上最小的高速、多技术和光学测试平台。它支持两个单插槽模块&#xff0c;如在同一单元中可进行以太网和光时域反射仪(OTDR)的组合测试功能。该系统还支持光谱和多层测试。总而言之&#xff0c;该平台提…

数据库设计【笔记】

数据库设计一、设计步骤1.收集信息&#xff08;来源项目需求分析&#xff09;2.标识实体&#xff08;一般是需求分析中需要管理的信息名词&#xff09;3.标识每个实体的属性4.实体之间的关系 二、画e-r图&#xff08;实体关系图&#xff09;1.矩形表示实体2.椭圆表示实体的属性…

你必须知道的EF知识和经验

注意&#xff1a;以下内容如果没有特别申明&#xff0c;默认使用的EF6.0版本&#xff0c;code first模式。 推荐MiniProfiler插件 工欲善其事&#xff0c;必先利其器。 我们使用EF和在很大程度提高了开发速度&#xff0c;不过随之带来的是很多性能低下的写法和生成不太高效的sq…

Go语言、Docker 和新技术

转载自 Go语言、Docker 和新技术上个月&#xff0c;作为 Go 语言的三位创始人之一&#xff0c;Unix 老牌黑客罗勃派克&#xff08;Rob Pike&#xff09;在新文章“Go: Ten years and climbing”中&#xff0c;回顾了一下 Go 语言的发展过程。其中提到&#xff0c;Go 语言这十年…

服务器 .err文件,IIS 关闭HTTPERR(IIS日志)的方法

关闭HTTPERR的方法运行里输入 regedit 进入注册表编辑器[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\HTTP\Parameters]在右边 点鼠标右键 新建dword值 EnableErrorLogging 重新启动服务器就可以了"EnableErrorLogging"dword:00000000重起后&#xff0c;2…

数据库的实现【笔记】

一、创建数据库1.简单的方式create database 数据库名2.指定主文件和日志文件参数create database 数据库名 on [primary](name数据库逻辑名, --数据库名_datafilename数据库主文件&#xff08;包括路径&#xff09;,--E:\第二期\第二章数据库的实现\数据库名_d…

Entity Framework Core Lolita

这是Entity Framework Core的一个轻量的扩展&#xff0c;提供批量更新和删除操作的支持。而且这个库出自中国一位MVP之手&#xff0c;虽然内容是英文&#xff0c;也很简单&#xff0c;相信你也能看懂。 This is a light-weight extension which provides bulk update and delet…

如何显示服务器控件,Panel Web 服务器控件概述

Panel Web 服务器控件概述10/22/2014本文内容更新&#xff1a;2007 年 11 月Panel Web 服务器控件在 ASP.NET 网页内提供了一种容器控件&#xff0c;您可以将它用作静态文本和其他控件的父级。本主题包括&#xff1a;背景代码示例类参考背景可以将 Panel 控件用作其他控件的容器…