JavaWeb学习笔记(十六)—— 事务
一、事务概述
1.1 什么是事务
银行转账!张三转10000块到李四的账户,这其实需要两条SQL语句:
给张三的账户减去10000元;
给李四的账户加上10000元。
如果在第一条SQL语句执行成功后,在执行第二条SQL语句之前,程序被中断了(可能是抛出了某个异常,也可能是其他什么原因),那么李四的账户没有加上10000元,而张三却减去了10000元。这肯定是不行的!
你现在可能已经知道什么是事务了吧!事务中的多个操作,要么完全成功,要么完全失败!不可能存在成功一半的情况!也就是说给张三的账户减去10000元如果成功了,那么给李四的账户加上10000元的操作也必须是成功的;否则给张三减去10000元,以及给李四加上10000元都是失败的!
总结:事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功。
1.2 事务的四大特性(ACID)
事务的四大特性是:
- 原子性(Atomicity):事务中所有操作是不可再分割的原子单位。事务中所有操作要么全部执行成功,要么全部执行失败。
- 一致性(Consistency):事务执行后,数据库状态与其它业务规则保持一致。如转账业务,无论事务执行成功与否,参与转账的两个账号余额之和应该是不变的。
- 隔离性(Isolation):隔离性是指在并发操作中,不同事务之间应该隔离开来,使每个并发中的事务不会相互干扰。
- 持久性(Durability):一旦事务提交成功,事务中所有的数据操作都必须被持久化到数据库中,即使提交事务后,数据库马上崩溃,在数据库重启时,也必须能保证通过某种机制恢复数据。
1.3 MySQL中的事务
在默认情况下,MySQL每执行一条SQL语句,都是一个单独的事务。如果需要在一个事务中包含多条SQL语句,那么需要开启事务和结束事务。
- 开启事务:start transaction;
- 结束事务:commit或rollback。
在执行SQL语句之前,先执行strat transaction,这就开启了一个事务(事务的起点),然后可以去执行多条SQL语句,最后要结束事务,commit表示提交,即事务中的多条SQL语句所做出的影响会持久化到数据库中(从开启事务到事务提交,中间的所有的sql都认为有效,真正的更新数据库)。或者rollback,表示回滚,即回滚到事务的起点,之前做的所有操作都被撤消了(从开启事务到事务回滚,中间的所有的sql操作都认为无效,数据库没有被更新)!
下面演示zs给li转账10000元的示例:
START TRANSACTION;
UPDATE account SET balance=balance-10000 WHERE id=1;
UPDATE account SET balance=balance+10000 WHERE id=2;
-- 回滚结束,事务执行失败
ROLLBACK ; START TRANSACTION;
UPDATE account SET balance=balance-10000 WHERE id=1;
UPDATE account SET balance=balance+10000 WHERE id=2;
-- 提交结束,事务执行成功
COMMIT ; START TRANSACTION;
UPDATE account SET balance=balance-10000 WHERE id=1;
UPDATE account SET balance=balance+10000 WHERE id=2;
-- 退出,MySQL会自动回滚事务。
quit ;
二、JDBC事务
在jdbc中处理事务,都是通过Connection完成的!当Jdbc程序向数据库获得一个Connection对象时,默认情况下这个Connection对象会自动向数据库提交在它上面发送的SQL语句。若想关闭这种默认提交方式,让多条SQL在一个事务中执行,可使用下列的JDBC控制事务语句:
- Connection.setAutoCommit(false):设置是否为自动提交事务,如果true(默认值就是true)表示自动提交,也就是每条执行的SQL语句都是一个单独的事务,如果设置false,那么就相当于开启了事务了(start transaction);
- Connection.rollback();//回滚事务(rollback)
- Connection.commit();//提交事务(commit)
jdbc处理事务的代码格式:
try {
con.setAutoCommit(false);//开启事务…
….
…
con.commit();//try的最后提交事务
} catch() {
con.rollback();//回滚事务
}
三、事务的隔离级别
3.1 事务的并发读问题
- 脏读:一个事务读取到另一个事务未提交数据(脏读是不能允许的)
事务1:张三给李四转账100元
事务2:李四查看自己的账户 t1:事务1:开始事务
t2:事务1:张三给李四转账100元
t3:事务2:开始事务
t4:事务2:李四查看自己的账户,看到账户多出100元(脏读)
t5:事务2:提交事务
t6:事务1:回滚事务,回到转账之前的状态 不可重复读:一个事务读到了另一个事务已经提交的update的数据,导致在同一个事务中的多次查询结果不一致。
事务1:酒店查看两次1048号房间状态
事务2:预订1048号房间 t1:事务1:开始事务
t2:事务1:查看1048号房间状态为空闲
t3:事务2:开始事务
t4:事务2:预定1048号房间
t5:事务2:提交事务
t6:事务1:再次查看1048号房间状态为使用
t7:事务1:提交事务
对同一记录的两次查询结果不一致!幻读(虚读):一个事务读到了另一个事务已经提交的insert的数据,导致在同一个事务中的多次查询结果不一致。
事务1:对酒店房间预订记录两次统计
事务2:添加一条预订房间记录 t1:事务1:开始事务
t2:事务1:统计预订记录100条
t3:事务2:开始事务
t4:事务2:添加一条预订房间记录
t5:事务2:提交事务
t6:事务1:再次统计预订记录为101记录
t7:事务1:提交
对同一表的两次查询不一致!
【不可重复读和幻读的区别】
- 不可重复读是读取到了另一事务的更新;
- 幻读是读取到了另一事务的插入(MySQL中无法测试到幻读);
3.2 四大隔离级别
4个等级的事务隔离级别,在相同数据环境下,使用相同的输入,执行相同的工作,根据不同的隔离级别,可以导致不同的结果。不同事务隔离级别能够解决的数据并发问题的能力是不同的。
【SERIALIZABLE(串行化)】
- 不会出现任何并发问题,因为它是对同一数据的访问是串行的,非并发访问的;
- 性能最差(相当于锁表)
【REPEATABLE READ(可重复读)(MySQL)】
- 防止脏读和不可重复读,不能处理幻读问题;
- 性能比SERIALIZABLE好
【READ COMMITTED(读已提交数据)(Oracle)】
- 防止脏读,没有处理不可重复读,也没有处理幻读;
- 性能比REPEATABLE READ好
【READ UNCOMMITTED(读未提交数据)】
- 可能出现任何事务并发问题
- 性能最好
3.3 MySQL隔离级别
mysql数据库默认的事务隔离级别是:Repeatable read(可重复读)
【mysql数据库查询当前事务隔离级别】
select @@tx_isolation
例如:
aaarticlea/png;base64,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" alt="" />
【mysql数据库设置事务隔离级别】
set transaction isolation level 隔离级别名
例如:
aaarticlea/png;base64,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" alt="" />
1、当把事务的隔离级别设置为read uncommitted时,会引发脏读、不可重复读和虚读
A窗口
set transaction isolation level read uncommitted;--设置A用户的数据库隔离级别为Read uncommitted(读未提交)
start transaction;--开启事务
select * from account;--查询A账户中现有的钱,转到B窗口进行操作
select * from account--发现a多了100元,这时候A读到了B未提交的数据(脏读)
B窗口
start transaction;--开启事务
update account set money=money+100 where name='A';--不要提交,转到A窗口查询
2、当把事务的隔离级别设置为read committed时,会引发不可重复读和虚读,但避免了脏读
A窗口
set transaction isolation level read committed;
start transaction;
select * from account;--发现a帐户是1000元,转到b窗口
select * from account;--发现a帐户多了100,这时候,a读到了别的事务提交的数据,两次读取a帐户读到的是不同的结果(不可重复读)
B窗口
start transaction;
update account set money=money+100 where name='aaa';
commit;--转到a窗口
3、当把事务的隔离级别设置为repeatable read(mysql默认级别)时,会引发虚读,但避免了脏读、不可重复读
A窗口
set transaction isolation level repeatable read;
start transaction;
select * from account;--发现表有4个记录,转到b窗口
select * from account;--可能发现表有5条记录,这时候发生了a读取到另外一个事务插入的数据(虚读)
B窗口
start transaction;
insert into account(name,money) values('ggg',1000);
commit;--转到a窗口
4、当把事务的隔离级别设置为Serializable时,会避免所有问题
A窗口
set transaction isolation level Serializable;
start transaction;
select * from account;--转到b窗口
B窗口
start transaction;
insert into account(name,money) values('ggg',1000);--发现不能插入,只能等待a结束事务才能插入
3.4 JDBC设置隔离级别
con. setTransactionIsolation(int level)
参数可选值如下:
- Connection.TRANSACTION_READ_UNCOMMITTED;
- Connection.TRANSACTION_READ_COMMITTED;
- Connection.TRANSACTION_REPEATABLE_READ;
- Connection.TRANSACTION_SERIALIZABLE。
四、JDBC开发中事务的处理
在开发中,对数据库的多个表或者对一个表中的多条数据执行更新操作时要保证对多个更新操作要么同时成功,要么都不成功,这就涉及到对多个更新操作的事务管理问题了。比如银行业务中的转账问题,A用户向B用户转账100元,假设A用户和B用户的钱都存储在Account表,那么A用户向B用户转账时就涉及到同时更新Account表中的A用户的钱和B用户的钱,用SQL来表示就是:
update account set money=money-100 where name='A'
update account set money=money+100 where name='B'
4.1 原始版
[transfer.jsp]
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>transfer</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/transfer" method="post">
转出账户: <input type="text" name="out"/><br/>
转入账户: <input type="text" name="in"/></br/>
转账金额: <input type="text" name="money"/><br/>
<input type="submit" value="确认转账"/>
</form>
</body>
</html>
【web层】
public class TransferServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 接收转账的参数
String out = request.getParameter("out");
String in = request.getParameter("in");
String moneyStr = request.getParameter("money");
double money = Double.parseDouble(moneyStr); // 调用业务层的转账方法
TransferService service = new TransferService();
boolean isTransferSuccess = service.transfer(out, in, money); response.setContentType("text/html;charset=UTF-8");
if (isTransferSuccess) {
response.getWriter().write("转账成功");
} else {
response.getWriter().write("转账失败");
}
} protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
【service层】
public class TransferService {
public boolean transfer(String out, String in, double money) {
TransferDao dao = new TransferDao(); boolean isTransferSuccess = true; try {
// 转出钱的方法
dao.out(out, money);
int i = 1 / 0;
// 转入钱的方法
dao.in(in,money);
} catch (Exception e) {
isTransferSuccess = false;
e.printStackTrace();
}
return isTransferSuccess;
}
}
【Dao层】
public class TransferDao { public void out(String out, double money) throws SQLException {
QueryRunner qr = new QueryRunner();
Connection conn = JdbcUtils.getConnection();
String sql = "update t_account set money=money-? where name=?";
qr.update(conn, sql, money, out);
} public void in(String in, double money) throws SQLException {
QueryRunner qr = new QueryRunner();
Connection conn = JdbcUtils.getConnection();
String sql = "update t_account set money=money+? where name=?";
qr.update(conn, sql, money, in);
}
}
上述处理没有加入事务处理,当我们加入int i = 1/0语句时,程序就会报错,转账失败,但是数据库中就会出现tom的钱减了100,而lucy的钱却因为程序报错而没有加上,违背了事务的一致性,这是不能允许的。
public class TransferService {
public boolean transfer(String out, String in, double money) {
TransferDao dao = new TransferDao(); boolean isTransferSuccess = true;
Connection conn = null; try {
// 开启事务
conn = JdbcUtils.getConnection();
conn.setAutoCommit(false);
// 转出钱的方法
dao.out(out, money);
int i = 1 / 0;
// 转入钱的方法
dao.in(in, money);
// 事务的提交不建议放在这
// conn.commit();
} catch (Exception e) {
isTransferSuccess = false;
// 回滚事务
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
} finally{
try {
// 事务的提交建议放在finally里
conn.commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
return isTransferSuccess;
}
}
这里可能会有一个疑问:如果把commit放在finally里面的话,如果程序报错,回滚完后还提交。
注意回滚与提交的区别:回滚本身内部不包含提交的功能,回滚是“滚”到事务开启的地方,即第11行。程序报错,“滚”到第11行,然后再提交,这时候可以认为第13-16行之间的代码都没执行过。
如果把commit放在第18行,那么finally的代码就可以不用写了。这时候的情况是:第14行代码报错,程序就进入catch代码块,接着进行回滚操作,”滚“到第11行,这时第18行的commit是没有执行的,事务还没有结束,但是回滚完成之后,catch的代码已经执行完毕,即方法结束了,方法结束后,连接池会自动帮你关闭connection,这时候事务就结束了。而且别人再拿到connection的时候,就重新开启了一个新的事务了。
修改完service层后,再次执行转账功能(tom给lucy转100),这是发现,虽然我们已经加入事务的处理了,但是转账失败的时候,tom少了100元,lucy仍然不变,跟之前的情况一样。
问题的原因是:开启事务的时候,是从连接池中拿的一个connection,而在操作TransferDao的时候,又从池子中拿了一个connection,这两个connection并不是同一个!!而dao中转入、转出两个方法中的connection也不是同一个。以上的操作,就有了3个不同的connection,这是不允许的。注意:控制事务、开启事务、以及操作每条sql的connection必须是同一个!下面对原始版进行改进。
4.2 改进版
思路:我们在service中拿到了connection,为保证后面的操作是同一个connection,我们可以将service中的connection以参数的形式传递给dao层,这样就保证了connection是同一个。
【service层】
public class TransferService {
public boolean transfer(String out, String in, double money) {
TransferDao dao = new TransferDao(); boolean isTransferSuccess = true;
Connection conn = null; try {
// 开启事务
conn = JdbcUtils.getConnection();
conn.setAutoCommit(false);
// 转出钱的方法
dao.out(conn,out, money);
int i = 1 / 0;
// 转入钱的方法
dao.in(in,conn, money);
// 事务的提交不建议放在这
// conn.commit();
} catch (Exception e) {
isTransferSuccess = false;
// 回滚事务
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
} finally{
try {
// 事务的提交建议放在finally里
conn.commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
return isTransferSuccess;
}
}
public class TransferDao { public void out(Connection conn, String out, double money) throws SQLException {
QueryRunner qr = new QueryRunner();
//Connection conn = JdbcUtils.getConnection();
String sql = "update t_account set money=money-? where name=?";
qr.update(conn, sql, money, out);
} public void in(String in, Connection conn, double money) throws SQLException {
QueryRunner qr = new QueryRunner();
//Connection conn = JdbcUtils.getConnection();
String sql = "update t_account set money=money+? where name=?";
qr.update(conn, sql, money, in);
}
}
4.3 最终版——使用ThreadLocal
在上面改进版的代码中,我们是在service中调用dao的方法,并把connection对象传递过去,这就保证了connection是同一个。但是这种方式不好, 我们在开发中使用分层的目的就是使每个层之间的逻辑更清楚。在改进版中, service层出现了connection,connection是数据库的连接资源,它应该是在dao层出现的 ,这就涉及到了层与层之间的”污染“了,所以并不好。
现在的矛盾在于:开启事务,需要用connection开,执行sql,也要用connection执行,而且这两个connection必须是同一个。但是事务的操作又必须在service层控制,而在service层还不想看到connection。接下来介绍ThreadLocal。
首先我们需要的一点是:所有的方法调用,用的都是同一个线程(除非你开启一个新的线程)。在本案例的转账功能中,web层-service层-dao层之间的方法调用,都用的是同一个线程的。在操作事务时,我们可用尝试将connection放在一个Map中,map的key就是当前线程,value就是connection,这样就能保证service层和dao层用的是同一个connection。其实ThreadLocal就是利用这样的原理。
ThreadLocal内部其实是个Map来保存数据。虽然在使用ThreadLocal时只给出了值,没有给出键,其实它内部使用了当前线程做为键。
class MyThreadLocal<T> {
private Map<Thread,T> map = new HashMap<Thread,T>();
public void set(T value) {
map.put(Thread.currentThread(), value);
} public void remove() {
map.remove(Thread.currentThread());
} public T get() {
return map.get(Thread.currentThread());
}
}
ThreadLocal类只有三个方法:
- void set(T value):保存值;
- T get():获取值;
- void remove():移除值。
【改进JdbcUtils】
public class JdbcUtils { // 获得Connection——从连接池中获取
private static ComboPooledDataSource dataSource = new ComboPooledDataSource(); // 创建ThreadLocal,<Connection>表示存放的值为Connection类
private static ThreadLocal<Connection> tl = new ThreadLocal(); // 获得当前线程上绑定的connection
public static Connection getCurrentConnection() throws SQLException {
// 从ThreadLocal寻找当前线程是否有对应的Connection
Connection conn = tl.get();
if (conn == null) {
// 获得新的connection
conn = getConnection();
// 将conn资源绑定到ThreadLocal上
tl.set(conn);
}
return conn;
} // 开启事务
public static void startTransaction() throws SQLException {
Connection conn = getCurrentConnection();
conn.setAutoCommit(false);
} // 回滚事务
public static void rollback() throws SQLException {
getCurrentConnection().rollback();
} // 提交事务
public static void commit() throws SQLException {
getCurrentConnection().commit();
} public static DataSource getDataSource() {
return dataSource;
} // 获取连接
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
} // 释放连接
public static void release(Connection connection, PreparedStatement pstmt, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
} }
【service层】
public class TransferService {
public boolean transfer(String out, String in, double money) {
TransferDao dao = new TransferDao(); boolean isTransferSuccess = true; try {
// 开启事务
//conn = JdbcUtils.getConnection();
//conn.setAutoCommit(false);
JdbcUtils.startTransaction();
// 转出钱的方法
dao.out(out, money);
int i = 1 / 0;
// 转入钱的方法
dao.in(in, money);
// 事务的提交不建议放在这
// conn.commit();
} catch (Exception e) {
isTransferSuccess = false;
// 回滚事务
try {
JdbcUtils.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
} finally{
try {
// 事务的提交建议放在finally里
JdbcUtils.commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
return isTransferSuccess;
}
}
【Dao层】——数据库连接对象不再需要service层传递过来,而是直接从JdbcUtils提供的getCurrentConnection方法去获取
public class TransferDao { public void out(String out, double money) throws SQLException {
QueryRunner qr = new QueryRunner();
Connection conn = JdbcUtils.getCurrentConnection();
String sql = "update t_account set money=money-? where name=?";
qr.update(conn, sql, money, out);
} public void in(String in, double money) throws SQLException {
QueryRunner qr = new QueryRunner();
Connection conn = JdbcUtils.getCurrentConnection();
String sql = "update t_account set money=money+? where name=?";
qr.update(conn, sql, money, in);
}
}
这样在service层对事务的处理看起来就更加优雅了。ThreadLocal类在开发中使用得是比较多的,程序运行中产生的数据要想在一个线程范围内共享,只需要把数据使用ThreadLocal进行存储即可。
JavaWeb学习笔记(十六)—— 事务的更多相关文章
- python3.4学习笔记(十六) windows下面安装easy_install和pip教程
python3.4学习笔记(十六) windows下面安装easy_install和pip教程 easy_install和pip都是用来下载安装Python一个公共资源库PyPI的相关资源包的 首先安 ...
- (C/C++学习笔记) 十六. 预处理
十六. 预处理 ● 关键字typeof 作用: 为一个已有的数据类型起一个或多个别名(alias), 从而增加了代码的可读性. typedef known_type_name new_type_nam ...
- 【JAVAWEB学习笔记】19_事务
事务 学习目标 案例-完成转账 一.事务概述 1.什么是事务 一件事情有n个组成单元 要不这n个组成单元同时成功 要不n个单元就同时失败 就是将n个组成单元放到一个事务中 2.mysql的事务 默认的 ...
- 【JAVAWEB学习笔记】19_事务概述、操作、特性和隔离级别
事务 学习目标 案例-完成转账 一.事务概述 1.什么是事务 一件事情有n个组成单元 要不这n个组成单元同时成功 要不n个单元就同时失败 就是将n个组成单元放到一个事务中 2.mysql的事务 默认的 ...
- JavaWeb学习笔记(六)jsp
第六章.jsp 1.什么是jsp jsp:java server pages,java的服务器页面 作用:代替Servlet回传HTML页面的数据 因为Servlet程序回传HTML页面的数据很繁琐, ...
- MySQL学习笔记十六:锁机制
1.数据库锁就是为了保证数据库数据的一致性在一个共享资源被并发访问时使得数据访问顺序化的机制.MySQL数据库的锁机制比较独特,支持不同的存储引擎使用不同的锁机制. 2.MySQL使用了三种类型的锁机 ...
- JavaWeb学习 (二十六)————监听器(Listener)学习(二)
一.监听域对象中属性的变更的监听器 域对象中属性的变更的事件监听器就是用来监听 ServletContext, HttpSession, HttpServletRequest 这三个对象中的属性变更信 ...
- JavaWeb学习笔记(六)—— Cookie&Session
一.会话技术简介 会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话. 在日常生活中,从拨通电话到挂断电话之间的一连串的你问我答的过程 ...
- JavaScript权威设计--CSS(简要学习笔记十六)
1.Document的一些特殊属性 document.lastModified document.URL document.title document.referrer document.domai ...
- python 学习笔记十六 django深入学习一 路由系统,模板,admin,数据库操作
django 请求流程图 django 路由系统 在django中我们可以通过定义urls,让不同的url路由到不同的处理函数 from . import views urlpatterns = [ ...
随机推荐
- myeclipse10.X以上的破解方法
破解补丁下载地址:http://pan.baidu.com/s/1dDzVP3z 本文使用的破解补丁对MyEclipse Standard/ Professional/ Blue/ Spring的10 ...
- 怎样在本地电脑上连接另外一台电脑上的mysql数据?(代码示例)
基本上有两种方式: 1,使用MYSQL的管理工具,可以远程连接,比如用导航猫系列的,只要知道对方IP,然后就可以自己输入账号密码连接了. 2,使用代码连接. Java代码示例: package com ...
- springboot之JdbcTemplate单数据源使用
本文介绍在Spring Boot基础下配置数据源和通过JdbcTemplate编写数据访问的示例. 数据源配置 在我们访问数据库的时候,需要先配置一个数据源,下面分别介绍一下几种不同的数据库配置方式. ...
- Oracle——约束
NOT NULLUNIQUE PRIMARY KEYFOREIGN KEYCHECK 如果不指定约束名 ,Oracle server 自动按照 SYS_Cn 的格式指定约束名 --指定约束名 CREA ...
- Hibernate环境搭建
Hibernate的环境搭建,主要步骤分为一下四步: 首先创建一个工程,在工程里创建一个实体类User,在这个实体类中必须包含无参的构造器,和这个类对属性的存取方法(getter and setter ...
- 实践作业4---DAY4阶段三。
阶段三:给出结论 这一阶段,我们首先列表从核心功能.细节.用户体验.辅助功能差异化功能.软件的适应性和成长性展开.我们得结论前参考了权威网站数据.并自己也做了相应分析. 结论:经过这么多工作,这个软件 ...
- PHP 5.2 5.3 5.4 5.5 memcache dll扩展
在windows下PHP5.2版本的memcache扩展dll文件好找,5.3的可能不是很好找,这里提供PHP5.2.5.3.5.4.5.5的php_memcache.dll扩展,需要的可以下载. 全 ...
- eclipse 导出可执行jar文件
step1: step2: step3: 结果: E:\executable\META-INF\MANIFEST.MF Manifest-Version: 1.0 Main-Class: cn.zno ...
- 浅析Java语言慢的原因
Java在早期(比如JDK1.2以前)是很慢的,后续版本由于有许多优化手段的加入,Java正变得越来越快,所以现在也有很多关于Java和C/C++孰快孰慢的争论.我想就我自己的理解,谈一下影响Java ...
- maven3的安装
先来简单介绍一下maven,Maven是Apache的顶级项目,是基于项目对象模型,也就是POM模型,用作项目管理,基本上是用做Java的项目. 1.安装环境准备,首先确定机子上已经安装和配置好了JD ...