当前位置: 首页 > 编程日记 > 正文

day12-事务

day12总结[c1]

今日内容

l  事务

l  连接池

事务

事务概述

为了方便演示事务,我们需要创建一个account表:

CREATE TABLE account(

       id INT PRIMARY KEY AUTO_INCREMENT,

       NAME VARCHAR(30),

       balance NUMERIC(10.2)

);

 

INSERT INTO account(NAME,balance) VALUES('zs', 1000);

INSERT INTO account(NAME,balance) VALUES('ls', 1000);

INSERT INTO account(NAME,balance) VALUES('ww', 1000);

 

SELECT * FROM account;

1 什么是事务[c2]

银行转账!张三转10000块到李四的账户,这其实需要两条SQL语句:

l  给张三的账户减去10000元;

l  给李四的账户加上10000元。

如果在第一条SQL语句执行成功后,在执行第二条SQL语句之前,程序被中断了(可能是抛出了某个异常,也可能是其他什么原因),那么李四的账户没有加上10000元,而张三却减去了10000元。这肯定是不行的!

你现在可能已经知道什么是事务了吧!事务中的多个操作,要么完全成功,要么完全失败!不可能存在成功一半的情况!也就是说给张三的账户减去10000元如果成功了,那么给李四的账户加上10000元的操作也必须是成功的;否则给张三减去10000元,以及给李四加上10000元都是失败的!

2 事务的四大特性(ACID)[c3]

面试!

事务的四大特性是:

l  原子性(Atomicity):事务中所有操作是不可再分割的原子单位。事务中所有操作要么全部执行成功,要么全部执行失败。

l  一致性(Consistency):事务执行后,数据库状态与其它业务规则保持一致。如转账业务,无论事务执行成功与否,参与转账的两个账号余额之和应该是不变的。

l  隔离性(Isolation):隔离性是指在并发操作中,不同事务之间应该隔离开来,使每个并发中的事务不会相互干扰。

l  持久性(Durability):一旦事务提交成功,事务中所有的数据操作都必须被持久化到数据库中,即使提交事务后,数据库马上崩溃,在数据库重启时,也必须能保证通过某种机制恢复数据。

3 MySQL中的事务

在默认情况下,MySQL每执行一条SQL语句,都是一个单独的事务。如果需要在一个事务中包含多条SQL语句,那么需要开启事务和结束事务。

l  开启事务:start transaction

l  结束事务:commitrollback

在执行SQL语句之前,先执行strat transaction,这就开启了一个事务(事务的起点),然后可以去执行多条SQL语句,最后要结束事务,commit表示提交,即事务中的多条SQL语句所做出的影响会持久化到数据库中。或者rollback,表示回滚,即回滚到事务的起点,之前做的所有操作都被撤消了!

下面演示zs给li转账10000元的示例:

START TRANSACTION;

UPDATE account SET balance=balance-10000 WHERE id=1;

UPDATE account SET balance=balance+10000 WHERE id=2;

ROLLBACK[崔4] ;

START TRANSACTION;

UPDATE account SET balance=balance-10000 WHERE id=1;

UPDATE account SET balance=balance+10000 WHERE id=2;

COMMIT[崔5] ;

START TRANSACTION;

UPDATE account SET balance=balance-10000 WHERE id=1;

UPDATE account SET balance=balance+10000 WHERE id=2;

quit[崔6] ;

JDBC事务

1 JDBC中的事务

Connection的三个方法与事务相关:

l  setAutoCommit(boolean):设置是否为自动提交事务,如果true(默认值就是true)表示自动提交,也就是每条执行的SQL语句都是一个单独的事务,如果设置false,那么就相当于开启了事务了;

l  commit():提交结束事务;

l  rollback():回滚结束事务。

public void transfer(boolean b) {

Connection con = null;

PreparedStatement pstmt = null;

try {

con = JdbcUtils.getConnection();

//手动提交

con.setAutoCommit(false);[崔7]

String sql = "update account set balance=balance+? where id=?";

pstmt = con.prepareStatement(sql);

//操作1

pstmt.setDouble(1, -10000);

pstmt.setInt(2, 1);

pstmt.executeUpdate();

// 在两个操作中抛出异常

if(b) {

throw new Exception();

}

[崔8]            //操作2

pstmt.setDouble(1, 10000);

pstmt.setInt(2, 2);

pstmt.executeUpdate();

//提交事务

con.commit();[崔9]

} catch(Exception e) {

//回滚事务

if(con != null) {

try {

con.rollback();[崔10]

} catch(SQLException ex) {}

}

throw new RuntimeException(e);

} finally {

//关闭

JdbcUtils.close(con, pstmt);

}

}

2 保存点(了解)

保存点是JDBC3.0的东西!当要求数据库服务器支持保存点方式的回滚。

校验数据库服务器是否支持保存点!

boolean b = con.getMetaData().supportsSavepoints();

保存点的作用是允许事务回滚到指定的保存点位置。在事务中设置好保存点,然后回滚时可以选择回滚到指定的保存点,而不是回滚整个事务!注意,回滚到指定保存点并没有结束事务!!!只有回滚了整个事务才算是结束事务了!

Connection类的设置保存点,以及回滚到指定保存点方法:

l  设置保存点:Savepoint setSavepoint();

l  回滚到指定保存点:void rollback(Savepoint)。

/*

* 李四对张三说,如果你给我转1W,我就给你转100W。

* ==========================================

*

* 张三给李四转1W(张三减去1W,李四加上1W)

* 设置保存点!

* 李四给张三转100W(李四减去100W,张三加上100W)

* 查看李四余额为负数,那么回滚到保存点。

* 提交事务

*/

@Test

public void fun() {

Connection con = null;

PreparedStatement pstmt = null;

try {

con = JdbcUtils.getConnection();

//手动提交

con.setAutoCommit(false);[崔11]

String sql = "update account set balance=balance+? where name=?";

pstmt = con.prepareStatement(sql);

//操作1(张三减去1W)

pstmt.setDouble(1, -10000);

pstmt.setString(2, "zs");

pstmt.executeUpdate();

[崔12]

//操作2(李四加上1W)

pstmt.setDouble(1, 10000);

pstmt.setString(2, "ls");

pstmt.executeUpdate();

[崔13]

// 设置保存点

Savepoint sp = con.setSavepoint();[崔14]

//操作3(李四减去100W)

pstmt.setDouble(1, -1000000);

pstmt.setString(2, "ls");

pstmt.executeUpdate();

[崔15]

//操作4(张三加上100W)

pstmt.setDouble(1, 1000000);

pstmt.setString(2, "zs");

pstmt.executeUpdate();

[崔16]

//操作5(查看李四余额)

sql = "select balance from account where name=?";

pstmt = con.prepareStatement(sql);

pstmt.setString(1, "ls");

ResultSet rs = pstmt.executeQuery();

rs.next();

double balance = rs.getDouble(1);

[崔17]       //如果李四余额为负数,那么回滚到指定保存点

if(balance < 0) {

con.rollback(sp);[崔18]

System.out.println("张三,你上当了!");

}

//提交事务

con.commit()[崔19] ;

} catch(Exception e) {

//回滚事务

if(con != null) {

try {

con.rollback();

} catch(SQLException ex) {}

}

throw new RuntimeException(e);

} finally {

//关闭

JdbcUtils.close(con, pstmt);

}

}

事务隔离级别

1        事务的并发读问题[c20] [c21]

l  脏读:读取到另一个事务未提交数据;

l  不可重复读:两次读取不一致;

l  幻读(虚读):读到另一事务已提交数据(插入的数据)。

2 并发事务问题

因为并发事务导致的问题大致有5类,其中两类是更新问题,三类是读问题。

l  脏读(dirty read):读到未提交更新数据,即读取到了脏数据;

l  不可重复读(unrepeatable read):对同一记录的两次读取不一致,因为另一事务对该记录做了修改;

l  幻读(phantom read):对同一张表的两次查询不一致,因为另一事务插入了一条记录;

脏读

事务1:张三给李四转账100元

事务2:李四查看自己的账户

l  t1:事务1:开始事务

l  t2:事务1:张三给李四转账100元

l  t3:事务2:开始事务

l  t4:事务2:李四查看自己的账户,看到账户多出100元(脏读)

l  t5:事务2:提交事务

l  t6:事务1:回滚事务,回到转账之前的状态

不可重复读

事务1:酒店查看两次1048号房间状态

事务2:预订1048号房间

l  t1:事务1:开始事务

l  t2:事务1:查看1048号房间状态为空闲

l  t3:事务2:开始事务

l  t4:事务2:预定1048号房间

l  t5:事务2:提交事务

l  t6:事务1:再次查看1048号房间状态为使用

l  t7:事务1:提交事务

对同一记录的两次查询结果不一致!

幻读

事务1:对酒店房间预订记录两次统计

事务2:添加一条预订房间记录

l  t1:事务1:开始事务

l  t2:事务1:统计预订记录100条

l  t3:事务2:开始事务

l  t4:事务2:添加一条预订房间记录

l  t5:事务2:提交事务

l  t6:事务1:再次统计预订记录为101记录

l  t7:事务1:提交

对同一表的两次查询不一致!

不可重复读和幻读的区别:

l  不可重复读是读取到了另一事务的更新;

l  幻读是读取到了另一事务的插入(MySQL中无法测试到幻读);

3 四大隔离级别

4个等级的事务隔离级别,在相同数据环境下,使用相同的输入,执行相同的工作,根据不同的隔离级别,可以导致不同的结果。不同事务隔离级别能够解决的数据并发问题的能力是不同的。

1 SERIALIZABLE(串行化)

l  不会出现任何并发问题,因为它是对同一数据的访问是串行的,非并发访问的;

l  性能最差;

2 REPEATABLE READ(可重复读)

l  防止脏读和不可重复读;

l  性能比SERIALIZABLE好

3 READ COMMITTED(读已提交数据)

l  防止脏读;

l  性能比REPEATABLE READ好

4 READ UNCOMMITTED(读未提交数据)

l  可能出现任何事务并发问题

l  性能最好

MySQL的默认隔离级别为REPEATABLE READ,这是一个很不错的选择吧!

5 MySQL隔离级别

MySQL的默认隔离级别为Repeatable read,可以通过下面语句查看:

seelect @@tx_isolation

也可以通过下面语句来设置当前连接的隔离级别:

set transaction isolationlevel [4先1]

6 JDBC设置隔离级别

con. setTransactionIsolation(int level)

参数可选值如下:

l  Connection.TRANSACTION_READ_UNCOMMITTED;

l  Connection.TRANSACTION_READ_COMMITTED;

l  Connection.TRANSACTION_REPEATABLE_READ;

l  Connection.TRANSACTION_SERIALIZABLE。

事务总结:

l  事务的特性:ACID;

l  事务开始边界与结束边界:开始边界(con.setAutoCommit(false)),结束边界(con.commit()或con.rollback());

l  事务的隔离级别: READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ、SERIALIZABLE。多个事务并发执行时才需要考虑并发事务。

数据库连接池

数据库连接池

1 数据库连接池的概念

用池来管理Connection,这可以重复使用Connection。有了池,所以我们就不用自己来创建Connection,而是通过池来获取Connection对象。当使用完Connection后,调用Connection的close()方法也不会真的关闭Connection,而是把Connection“归还”给池。池就可以再利用这个Connection对象了。

2 JDBC数据库连接池接口(DataSource)

Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商可以让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池!

3 自定义连接池(ItcastPool)

分析:ItcastPool需要有一个List,用来保存连接对象。在ItcastPool的构造器中创建5个连接对象放到List中!当有人调用了ItcastPool的getConnection()时,那么就从List拿出一个返回。当List中没有连接可用时,抛出异常。

我们需要对Connection的close()方法进行增强,所以我们需要自定义ItcastConnection类,对Connection进行装饰!即对close()方法进行增强。因为需要在调用close()方法时把连接“归还”给池,所以ItcastConnection类需要拥有池对象的引用,并且池类还要提供“归还”的方法。

ItcastPool.java

public class ItcastPool implements DataSource [崔22] {

private static Properties props = new Properties();

private List<Connection> list [崔23] = new ArrayList<Connection>();

static [崔24] {

InputStream in = ItcastPool.class.getClassLoader()

.getResourceAsStream("dbconfig.properties");

try {

props.load(in);

Class.forName(props.getProperty("driverClassName"));

} catch (Exception e) {

throw new RuntimeException(e);

}

}

public ItcastPool[崔25] () throws SQLException {

for (int i = 0; i < 5; i++) {

Connection con = DriverManager.getConnection(

props.getProperty("url"), props.getProperty("username"),

props.getProperty("password"));[崔26]

ItcastConnection conWapper = new ItcastConnection(con, this);[崔27] [c28]

list.add(conWapper);[崔29]

}

}

public void add[崔30] (Connection con) {

list.add(con);

}

public Connection getConnection[崔31] () throws SQLException {

if(list.size() > 0) {

return list.remove(0);

}

throw new SQLException("没连接了");

}

......[崔32]

}

ItcastConnection.java

public class ItcastConnection extends ConnectionWrapper[崔33]  {

private ItcastPool pool;

public ItcastConnection(Connection con, ItcastPool pool) {

super(con);

this.pool = pool[崔34] ;

}

@Override

public void close[崔35] [c36] () throws SQLException {

pool.add(this);

}

}

DBCP

1 什么是DBCP?

DBCP是Apache提供的一款开源免费的数据库连接池!

Hibernate3.0之后不再对DBCP提供支持!因为Hibernate声明DBCP有致命的缺欠!DBCP因为Hibernate的这一毁谤很是生气,并且说自己没有缺欠。

2 DBCP的使用

public void fun1() throws SQLException {

BasicDataSource ds = new BasicDataSource();

ds.setUsername("root");

ds.setPassword("123");

ds.setUrl("jdbc:mysql://localhost:3306/mydb1");

ds.setDriverClassName("com.mysql.jdbc.Driver");

[崔37]

ds.setMaxActive(20);[崔38]

ds.setMaxIdle(10);[崔39]

ds.setInitialSize(10)[崔40] ;

ds.setMinIdle(2)[崔41] ;

ds.setMaxWait(1000)[崔42] ;

Connection con = ds.getConnection();

System.out.println(con.getClass().getName());

con.close()[崔43] ;

}

3 DBCP的配置信息

下面是对DBCP的配置介绍:

#基本配置

driverClassName=com.mysql.jdbc.Driver

url=jdbc:mysql://localhost:3306/mydb1

username=root

password=123

#初始化池大小,即一开始池中就会有10个连接对象

默认值为0

initialSize=0

#最大连接数,如果设置maxActive=50时,池中最多可以有50个连接,当然这50个连接中包含被使用的和没被使用的(空闲)

#你是一个包工头,你一共有50个工人,但这50个工人有的当前正在工作,有的正在空闲

#默认值为8,如果设置为非正数,表示没有限制!即无限大

maxActive=8

#最大空闲连接

#当设置maxIdle=30时,你是包工头,你允许最多有20个工人空闲,如果现在有30个空闲工人,那么要开除10个

#默认值为8,如果设置为负数,表示没有限制!即无限大

maxIdle=8

#最小空闲连接

#如果设置minIdel=5时,如果你的工人只有3个空闲,那么你需要再去招2个回来,保证有5个空闲工人

#默认值为0

minIdle=0

#最大等待时间

#当设置maxWait=5000时,现在你的工作都出去工作了,又来了一个工作,需要一个工人。

#这时就要等待有工人回来,如果等待5000毫秒还没回来,那就抛出异常

#没有工人的原因:最多工人数为50,已经有50个工人了,不能再招了,但50人都出去工作了。

#默认值为-1,表示无限期等待,不会抛出异常。

maxWait=-1

#连接属性

#就是原来放在url后面的参数,可以使用connectionProperties来指定

#如果已经在url后面指定了,那么就不用在这里指定了。

#useServerPrepStmts=true,MySQL开启预编译功能

#cachePrepStmts=true,MySQL开启缓存PreparedStatement功能,

#prepStmtCacheSize=50,缓存PreparedStatement的上限

#prepStmtCacheSqlLimit=300,当SQL模板长度大于300时,就不再缓存它

connectionProperties=useUnicode=true;characterEncoding=UTF8;useServerPrepStmts=true;cachePrepStmts=true;prepStmtCacheSize=50;prepStmtCacheSqlLimit=300

#连接的默认提交方式

#默认值为true

defaultAutoCommit=true

#连接是否为只读连接

#Connection有一对方法:setReadOnly(boolean)和isReadOnly()

#如果是只读连接,那么你只能用这个连接来做查询

#指定连接为只读是为了优化!这个优化与并发事务相关!

#如果两个并发事务,对同一行记录做增、删、改操作,是不是一定要隔离它们啊?

#如果两个并发事务,对同一行记录只做查询操作,那么是不是就不用隔离它们了?

#如果没有指定这个属性值,那么是否为只读连接,这就由驱动自己来决定了。即Connection的实现类自己来决定!

defaultReadOnly=false

#指定事务的事务隔离级别

#可选值:NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE

#如果没有指定,那么由驱动中的Connection实现类自己来决定

defaultTransactionIsolation=REPEATABLE_READ

C3P0

1 C3P0简介

C3P0也是开源免费的连接池!C3P0被很多人看好!

2 C3P0的使用

C3P0中池类是:ComboPooledDataSource。

public void fun1() throws PropertyVetoException, SQLException {

ComboPooledDataSource ds = new ComboPooledDataSource();

ds.setJdbcUrl("jdbc:mysql://localhost:3306/mydb1");

ds.setUser("root");

ds.setPassword("123");

ds.setDriverClass("com.mysql.jdbc.Driver");

[崔44]

ds.setAcquireIncrement(5)[崔45] ;

ds.setInitialPoolSize(20)[崔46] ;

ds.setMinPoolSize(2)[崔47] ;

ds.setMaxPoolSize(50)[崔48] ;

Connection con = ds.getConnection();

System.out.println(con);

con.close();

}

c3p0也可以指定配置文件,而且配置文件可以是properties,也可是xml的。当然xml的高级一些了。但是c3p0的配置文件名必须为c3p0-config.xml,并且必须放在类路径下。[c49]

<?xml version="1.0" encoding="UTF-8"?>

<c3p0-config>

<default-config>[崔50]

<property name="jdbcUrl">jdbc:mysql://localhost:3306/mydb1</property>

<property name="driverClass">com.mysql.jdbc.Driver</property>

<property name="user">root</property>

<property name="password">123</property>

<property name="acquireIncrement">3</property>

<property name="initialPoolSize">10</property>

<property name="minPoolSize">2</property>

<property name="maxPoolSize">10</property>

</default-config>

<named-config name="oracle-config">[崔51]

<property name="jdbcUrl">jdbc:mysql://localhost:3306/mydb1</property>

<property name="driverClass">com.mysql.jdbc.Driver</property>

<property name="user">root</property>

<property name="password">123</property>

<property name="acquireIncrement">3</property>

<property name="initialPoolSize">10</property>

<property name="minPoolSize">2</property>

<property name="maxPoolSize">10</property>

</named-config>

</c3p0-config>

c3p0的配置文件中可以配置多个连接信息,可以给每个配置起个名字,这样可以方便的通过配置名称来切换配置信息。上面文件中默认配置为mysql的配置,名为oracle-config的配置也是mysql的配置,呵呵。

public void fun2() throws PropertyVetoException, SQLException {

ComboPooledDataSource ds = new ComboPooledDataSource();[崔52]

Connection con = ds.getConnection();

System.out.println(con);

con.close();

}

public void fun2() throws PropertyVetoException, SQLException {

ComboPooledDataSource ds = new ComboPooledDataSource("orcale-config")[崔53] ;

Connection con = ds.getConnection();

System.out.println(con);

con.close();

}

Tomcat配置连接池[c54]

1 Tomcat配置JNDI资源

JNDI(Java Naming and Directory Interface),Java命名和目录接口。[c55] JNDI的作用就是:在服务器上配置资源,然后通过统一的方式来获取配置的资源。

我们这里要配置的资源当然是连接池了,这样项目中就可以通过统一的方式来获取连接池对象了。

下图是Tomcat文档提供的:

配置JNDI资源需要到<Context>元素中配置<Resource>子元素:

l  name:指定资源的名称,这个名称可以随便给,在获取资源时需要这个名称;

l  factory:用来创建资源的工厂,这个值基本上是固定的,不用修改;

l  type:资源的类型,我们要给出的类型当然是我们连接池的类型了;

l  bar:表示资源的属性,如果资源存在名为bar的属性,那么就配置bar的值。对于DBCP连接池而言[c56] ,你需要配置的不是bar,因为它没有bar这个属性,而是应该去配置url、username等属性。

<Context>

<Resource name="mydbcp"

type="org.apache.tomcat.dbcp.dbcp.BasicDataSource"

factory="org.apache.naming.factory.BeanFactory"

username="root"

password="123"

driverClassName="com.mysql.jdbc.Driver"

url="jdbc:mysql://127.0.0.1/mydb1"

maxIdle="3"

maxWait="5000"

maxActive="5"

initialSize="3"/>

</Context>

<Context>

<Resource name="myc3p0"

type="com.mchange.v2.c3p0.ComboPooledDataSource"

factory="org.apache.naming.factory.BeanFactory"

user="root"

password="123"

classDriver="com.mysql.jdbc.Driver"

jdbcUrl="jdbc:mysql://127.0.0.1/mydb1"

maxPoolSize="20"

minPoolSize ="5"

initialPoolSize="10"

acquireIncrement="2"/>

</Context>

2 获取资源

配置资源的目的当然是为了获取资源了。只要你启动了Tomcat,那么就可以在项目中任何类中通过JNDI获取资源的方式来获取资源了。

下图是Tomcat文档提供的,与上面Tomcat文档提供的配置资源是对应的。

获取资源:

l  Context:javax.naming.Context;

l  InitialContext:javax.naming.InitialContext;

l  lookup(String):获取资源的方法,其中”java:comp/env”是资源的入口(这是固定的名称),获取过来的还是一个Context,这说明需要在获取到的Context上进一步进行获取。”bean/MyBeanFactory”对应<Resource>中配置的name值,这回获取的就是资源对象了。

Context cxt = new InitialContext();

DataSource ds = (DataSource)cxt.lookup("java:/comp/env/mydbcp");

Connection con = ds.getConnection();

System.out.println(con);

con.close();

Context cxt = new InitialContext();

Context envCxt = (Context)cxt.lookup("java:/comp/env");

DataSource ds = (DataSource)env.lookup("mydbcp");[c57]

Connection con = ds.getConnection();

System.out.println(con);

con.close();

上面两种方式是相同的效果。

修改JdbcUtils

因为已经学习了连接池,那么JdbcUtils的获取连接对象的方法也要修改一下了。

JdbcUtils.java

public class JdbcUtils {

private static DataSource dataSource = new ComboPooledDataSource();

public static DataSource getDataSource() {

return dataSource;

}

public static Connection getConnection() {

try {

return dataSource.getConnection();

} catch (Exception e) {

throw new RuntimeException(e);

}

}

}

ThreadLocal

1 ThreadLocal API

ThreadLocal类只有三个方法:

l  void set(T value):保存值;

l  T get():获取值;

l  void remove():移除值。

2 ThreadLocal的内部是Map

ThreadLocal内部其实是个Map来保存数据。虽然在使用ThreadLocal时只给出了值,没有给出键,其实它内部使用了当前线程做为键。

class MyThreadLocal<T> {

private Map<Thread,T> map = new HashMap<Thread,T>();

public void set(T value) {

map.put(Thread.currentThread()[c58] , value);

}

public void remove() {

map.remove(Thread.currentThread());

}

public T get() {

return map.get(Thread.currentThread());

}

}

BaseServlet

1 BaseServlet的作用

在开始客户管理系统之前,我们先写一个工具类:BaseServlet。

我们知道,写一个项目可能会出现N多个Servlet,而且一般一个Servlet只有一个方法(doGet或doPost),如果项目大一些,那么Servlet的数量就会很惊人。

为了避免Servlet的“膨胀”,我们写一个BaseServlet。它的作用是让一个Servlet可以处理多种不同的请求。不同的请求调用Servlet的不同方法。我们写好了BaseServlet后,让其他Servlet继承BaseServlet,例如CustomerServlet继承BaseServlet,然后在CustomerServlet中提供add()、update()、delete()等方法,每个方法对应不同的请求。

2 BaseServlet分析

我们知道,Servlet中处理请求的方法是service()方法,这说明我们需要让service()方法去调用其他方法。例如调用add()、mod()、del()、all()等方法!具体调用哪个方法需要在请求中给出方法名称!然后service()方法通过方法名称来调用指定的方法。

无论是点击超链接,还是提交表单,请求中必须要有method参数,这个参数的值就是要请求的方法名称,这样BaseServlet的service()才能通过方法名称来调用目标方法。例如某个链接如下:

<a href=”/xxx/CustomerServlet?method=add”>添加客户</a>[c59]

3 BaseServlet代码

public class BaseServlet extends HttpServlet {

/*

* 它会根据请求中的m,来决定调用本类的哪个方法

*/

protected void service(HttpServletRequest req, HttpServletResponse res)

throws ServletException, IOException {

req.setCharacterEncoding("UTF-8");

res.setContentType("text/html;charset=utf-8");

// 例如:http://localhost:8080/demo1/xxx?m=add

String methodName = req.getParameter("method");// 它是一个方法名称[c60]

// 当没用指定要调用的方法时,那么默认请求的是execute()方法。[c61]

if(methodName == null || methodName.isEmpty()) {

methodName = "execute";

}

Class c = this.getClass();[c62]

try {

// 通过方法名称获取方法的反射对象[c63]

Method m = c.getMethod(methodName, HttpServletRequest.class,

HttpServletResponse.class);

// 反射方法目标方法,也就是说,如果methodName为add,那么就调用add方法。

String result = (String) m.invoke[c64] (this, req, res);

// 通过返回值完成请求转发

if(result != null && !result.isEmpty()) {

req.getRequestDispatcher(result).forward(req, res);

}

} catch (Exception e) {

throw new ServletException(e);

}

}

}

DBUtils

1 DBUtils简介

DBUtils是Apache Commons组件中的一员,开源免费!

DBUtils是对JDBC的简单封装,但是它还是被很多公司使用!

DBUtils的Jar包:dbutils.jar

2 DBUtils主要类

l  DbUtils:都是静态方法,一系列的close()方法;

l  QueryRunner:

  • update():执行insert、update、delete;
  • query():执行select语句;
  • batch():执行批处理。

3 QueryRunner之更新

QueryRunner的update()方法可以用来执行insert、update、delete语句。

  1. 创建QueryRunner

构造器:QueryRunner();[c65]

  1. update()方法

int update(Connection con, String sql, Object… params)[c66]

@Test

public void fun1() throws SQLException {

QueryRunner qr = new QueryRunner();

String sql = "insert into user values(?,?,?)";

qr.update(JdbcUtils.getConnection(), sql, "u1", "zhangSan", "123");

}

还有另一种方式来使用QueryRunner

  1. 创建QueryRunner

构造器:QueryRunner(DataSource)[c67]

  1. update()方法

int update(String sql, Object… params)

这种方式在创建QueryRunner时传递了连接池对象,那么在调用update()方法时就不用再传递Connection了。

@Test

public void fun2() throws SQLException {

QueryRunner qr = new QueryRunner(JdbcUtils.getDataSource());

String sql = "insert into user values(?,?,?)";

qr.update(sql, "u1", "zhangSan", "123");

}

4 ResultSetHandler

我们知道在执行select语句之后得到的是ResultSet,然后我们还需要对ResultSet进行转换,得到最终我们想要的数据。你可以希望把ResultSet的数据放到一个List中,也可能想把数据放到一个Map中,或是一个Bean中。

DBUtils提供了一个接口ResultSetHandler,它就是用来ResultSet转换成目标类型的工具。你可以自己去实现这个接口,把ResultSet转换成你想要的类型。

DBUtils提供了很多个ResultSetHandler接口的实现,这些实现已经基本够用了,我们通常不用自己去实现ResultSet接口了。

l  MapHandler[c68] :单行处理器!把结果集转换成Map<String,Object>,其中列名为键!

l  MapListHandler:多行处理器!把结果集转换成List<Map<String,Object>>;[c69]

l  BeanHandler:单行处理器!把结果集转换成Bean,该处理器需要Class参数,即Bean的类型;

l  BeanListHandler:多行处理器!把结果集转换成List<Bean>;

l  ColumnListHandler:多行单列处理器!把结果集转换成List<Object>,使用ColumnListHandler时需要指定某一列的名称或编号,例如:new ColumListHandler(“name”)表示把name列的数据放到List中。

l  ScalarHandler:单行单列处理器!把结果集转换成Object。一般用于聚集查询,例如select count(*) from tab_student。

Map处理器

Bean处理器

Column处理器[c70]

Scalar处理器

5 QueryRunner之查询

QueryRunner的查询方法是:

public <T> T query(String sql, ResultSetHandler<T> rh, Object… params)

public <T> T query(Connection con, String sql, ResultSetHandler<T> rh[c71] , Object… params)

query()方法会通过sql语句和params查询出ResultSet,然后通过rh把ResultSet转换成对应的类型再返回。

@Test

public void fun1[c72] () throws SQLException {

DataSource ds = JdbcUtils.getDataSource();

QueryRunner qr = new QueryRunner(ds);

String sql = "select * from tab_student where number=?";

Map<String,Object> map = qr.query(sql, new MapHandler()[崔73] , "S_2000");

System.out.println(map);

}

@Test

public void fun2[c74] () throws SQLException {

DataSource ds = JdbcUtils.getDataSource();

QueryRunner qr = new QueryRunner(ds);

String sql = "select * from tab_student";

List<Map<String,Object>> list = qr.query(sql, new MapListHandler()[崔75] );

for(Map<String,Object> map : list) {

System.out.println(map);

}

}

@Test

public void fun3[c76] () throws SQLException {

DataSource ds = JdbcUtils.getDataSource();

QueryRunner qr = new QueryRunner(ds);

String sql = "select * from tab_student where number=?";

Student stu = qr.query(sql, new BeanHandler<Student>(Student.class)[崔77] , "S_2000");

System.out.println(stu);

}

@Test

public void fun4() throws SQLException {

DataSource ds = JdbcUtils.getDataSource();

QueryRunner qr = new QueryRunner(ds);

String sql = "select * from tab_student";

List<Student> list = qr.query(sql, new BeanListHandler[c78] <Student>(Student.class))[崔79] ;

for(Student stu : list) {

System.out.println(stu);

}

}

@Test

public void fun5[c80] () throws SQLException {

DataSource ds = JdbcUtils.getDataSource();

QueryRunner qr = new QueryRunner(ds);

String sql = "select * from tab_student";

List<Object> list = qr.query(sql, new ColumnListHandler("name"))[崔81] ;

for(Object s : list) {

System.out.println(s);

}

}

@Test

public void fun6[c82] () throws SQLException {

DataSource ds = JdbcUtils.getDataSource();

QueryRunner qr = new QueryRunner(ds);

String sql = "select count(*) from tab_student";

Number number = (Number)qr.query(sql, new ScalarHandler()[崔83] );

int cnt = number.intValue()[崔84] ;

System.out.println(cnt);

}

5 QueryRunner之批处理

QueryRunner还提供了批处理方法:batch()。

我们更新一行记录时需要指定一个Object[]为参数,如果是批处理,那么就要指定Object[][]为参数了。即多个Object[]就是Object[][]了,其中每个Object[]对应一行记录:[c85]

@Test

public void fun10() throws SQLException {

DataSource ds = JdbcUtils.getDataSource();

QueryRunner qr = new QueryRunner(ds);

String sql = "insert into tab_student values(?,?,?,?)";

Object[][] params = new Object[10][];[崔86] //表示 要插入10行记录

for(int i = 0; i < params.length; i++) {

params[i] = new Object[]{"S_300" + i, "name" + i, 30 + i, i%2==0?"男":"女"};

}

qr.batch[崔87] (sql, params);

}


2018.6.23

就是把多件事情当做一件事情来处理。也就是大家同在一条船上,要活一起活,要over一起over !

四大特性,好记不好理解

原子性,不可分割的个体

一致性,和现实生活中的逻辑一直

隔离性,两个并发的事务互不影响

持久性,数据修改后,必须持久化数据库。数据库崩溃,也可恢复

回滚结束,事务执行失败

提交结束,事务执行成功

退出,MySQL会自动回滚事务。

设置为手动提交事务,即开启了事务。

如果出现了异常就回滚结束事务

当两个操作都执行完了,提交结束事务。

当出现异常时,回滚事务。

开始事务

给张三减1万

给李四加1万

设置保存点

给李四减支100万

给张三加上100万

查看李四余额

发现李四余额小于0,回滚到指定还原点!即撤销了李四给张三转账100万的操作

注意,一定要提交事务,因为回滚到指定保存点不会结束事务!保存点之前的操作没有被回滚,只能提交了才能真正把没有回滚的操作执行了。

脏读:读取另一个事物未提交数据

不可重复读:两次读取不一样

幻读:读到另一事务已提交数据

实现DataSource接口

用来存放连接的list

加载配置文件中的配置信息到props属性中

构造器中向list中存放5个连接对象

创建连接对象

对连接对象进行装饰,把池自身的引用传递给连接装饰对象

装饰,重点

把装饰后的连接对象添加到list中

装饰后的连接对象的close()方法会调用本方法“归还”连接。

程序会调用本方法来获取连接对象!本方法会在list中移除一个连接返回给程序使用。

省略了DataSource中其他没有的方法。

ConnectionWrapper是Connection的装饰模板类!这个类要自己提供!

本类需要拥有池对象的引用,用来归还连接。

当程序调用了close()方法时,把当前连接对象添加到池中。

重点

基本配置

最大连接数

最大空闲连接数

初始化连接数

最小空闲连接数

最大等待毫秒数

关闭连接只是把连接归还给池!

基本配置

每次的增量为5

初始化连接数

最少连接数

最多连接数

配置文件,可以有多个连接信息

MySQL 和oracle

默认配置

命名配置

不用定配置文件名称,因为配置文件名必须是c3p0-config.xml,这里使用的是默认配置。

使用名为oracle-config的配置。

连接池特性,可以在tomcat下配置

JNDI,java命名和目录接口,在服务器上配置资源,获取资源

Hebirate3.0不支持DBCP

不太懂

当前线程

原来根据方法名+反射实现

当前要调用的方法参数

默认是execute方法

利用this,调用本类的对象

不太懂,反射里面的方法

反射方法目标方法,也就是说,如果methodName为add,那么就调用add方法。

无参构造

连接,URL,参数数组

有参构造,数据源

  1. 行列的区别
  2. 行是---------------的 map<”name”,”zhangsan”>
  3. 列是||||||||||||||||||| list[1].map(“name”,”lisi”)

不太懂 list里面是一个map,

数组里面是map

不理解

转成你想要的类型

好理解

把一行记录转换成一个Map,其中键为列名称,值为列值

多行,每个map对应一行

把转换集转换成List<Map>,其中每个Map对应一行记录

Bean对象,必须有class参数

把结果集转换成一个Bean对象,在使用BeanHandler时需要指定Class,即Bean的类型

一个bean一行记录

把结果集转换成List<Bean>,其中每个Bean对应一行记录

单行处理器,获取所以那么列数据

多行单例处理器,即获取name列数据

单行单列,一般用于聚合查询

单行单列处理器,一般用于聚合查询,在使用ScalarHandler时可以指定列名,如果不指定,默认为第1列。

对聚合函数的查询结果,有的驱动返回的是Long,有的返回的是BigInteger,所以这里我们把它转换成Number,Number是Long和BigInteger的父类!然后我们再调用Number的intValue()或longValue()方法就OK了。

二维数组,对应多行记录

注意,这里是二维数组,这个二维数组有10个一维数组。

执行批处理

转载于:https://www.cnblogs.com/csslcww/p/9219032.html

相关文章:

ThinkPHP基础概念

OOP 面向对象编程&#xff08;Object Oriented Programming&#xff0c;OOP&#xff0c;面向对象程序设计&#xff09;是一种计算机编程架构。OOP 的一条基本原则是计算机程序是由单个能够起到子程序作用的单元或对象组合而成。OOP 达到了软件工程的三个主要目标&#xff1a;重…

008本周总结报告

这周主要做了下PTA的编程题目的练习和学习和了解了java的多线程&#xff0c;了解了进程和线程的定义&#xff0c;区别&#xff0c;联系等&#xff0c;并知道了多线程的利与弊&#xff0c;并了解了JVM下的多运行机制&#xff08;本质是CPU 对应用程序的快速换&#xff09;&#…

python3.8.5是python3吗_Python 升级到3.8.5

mac osx 安装最新版本的3.8.5 将/usr/local/bin目录下的python3.8和pip3.8复制一份并修改为python和pip。 修改python的路径&#xff0c;之后source文件。 输出requirements.txt到桌面 安装新版本的第三方库&#xff0c;我使用的第三方库很多&#xff0c;更新很慢。头大啊。 验…

不看后悔 如何删除WIN7的100M隐藏分区

http://notebook.it168.com/a2010/1101/1120/000001120453_2.shtml

tomcat下面web应用发布路径配置 ( 即虚拟目录配置 )

https://blog.csdn.net/AnQ17/article/details/52122236转载于:https://www.cnblogs.com/gangpao/p/9223504.html

strcpy +memcpy实现循环右移

#include<stdio.h>#include<assert.h>#include<string.h>char *strcpy(char*strDest,const char*strSrc){assert(strDest!NULL&&strSrc!NULL);char * addr strDest;while( *strSrc!\0)*strDest *strSrc;*strDest \0;return addr;}//循环移动steps…

python查看目录下的文件_Python——查看目录下所有的目录和文件

原博文 2019-05-06 19:31 − 写程序我们经常会遇到需要遍历某一个目录下的所有文件这个操作&#xff0c;然而python有现成的库&#xff0c;只需要2个循环就可以搞定。 1 import os 2 3 def all_path(dirname): 4 5 result []#所有的文件 6 7 for ma... 相关推荐 2019-12-10 14…

负载均衡策略深入剖析

在实际应用中&#xff0c;我们可能不想仅仅是把客户端的服务请求平均地分配给内部服务器&#xff0c;而不管服务器是否宕机。而是想使Pentium III服务器比Pentium II能接受更多的服务请求&#xff0c;一台处理服务请求较少的服务器能分配到更多的服务请求&#xff0c;出现故障的…

js 验证数据类型的4中方法

1.typeof 可以检验基本数据类型 但是引用数据类型&#xff08;复杂数据类型&#xff09;无用&#xff1b; 总结 &#xff1a; typeof 无法识别引用数据类型 包括 bull; 2.instanceof是一个二元运算符&#xff0c;左操作数是一个对象&#xff0c;右操作数是一个构造函数。如…

有关 ecshop 属性 {$goods.goods_attr|nl2br} 标签的赋值问题

1、nl2br() 函数在字符串中的每个新行 (\n) 之前插入 HTML 换行符 (<br />)。 2、 如果要向{$goods.goods_attr|nl2br}赋新值&#xff0c;这个值是保存在数据库中的&#xff0c;用户在商品页(goods.php)选择了商品属性(goods.attr)之后&#xff0c;点击"购买"就…

linux cp 强制覆盖_Linux基本操作教程

Linux基本操作教程点击蓝字关注我们01.Linux系统简介Linux&#xff0c;全称GNU/Linux&#xff0c;是一套免费使用和自由传播的类UNIX操作系统&#xff0c;其内核由林纳斯本纳第克特托瓦兹于1991年第一次释出&#xff0c;它主要受到Minix和Unix思想的启发&#xff0c;是一个基于…

火焰图(Flame Graphs)的安装和基本用法

火焰图&#xff08;Flame Graphs&#xff09; 一、概述&#xff1a; 火焰图&#xff08;flame graph&#xff09;是性能分析的利器&#xff0c;通过它可以快速定位性能瓶颈点。 perf 命令&#xff08;performance 的缩写&#xff09;是 Linux 系统原生提供的性能分析工具&#…

用TCP/IP进行网际互联一

地址解析协议ARP主机知道某个目的主机的IP就可以知道该目的主机的物理地址。改进ARP每个ARP广播分组中都包含有发送方自身的IP和物理地址的绑定&#xff0c;接收方在处理ARP分组时&#xff0c;先在自己的缓存中更新发送方IP到物理地址的绑定信息。ARP是一个隐藏底层网络物理编址…

【learning】矩阵树定理

问题描述 给你一个图&#xff08;有向无向都ok&#xff09;&#xff0c;求这个图的生成树个数    一些概念 度数矩阵&#xff1a;\(a[i][i]degree[i]\)&#xff0c;其他等于\(0\) 入度矩阵&#xff1a;\(a[i][i]in\_degree[i]\)&#xff0c;其他等于\(0\) 出度矩阵&#xff1…

各大知名企业的Research展示

大公司為了要拉開彼此的差距, 除了專注於目前的產品外, 都會為了未來做準備, 而這些研究通常都會做一個 Research 的專區來呈現成果, 如下述列表: Google ResearchYahoo! ResearchThe Facebook ProjectMicrosoft Research - Turning Ideas into Reality微軟亞洲研究院IBM Resea…

解决Eclipse添加新server时无法选择Tomcat7的问题

关闭Eclipse删除WorkSpace目录下/.metadata/.plugins/org.eclipse.core.runtime/.settings目录中的org.eclipse.wst.server.core.prefs和org.eclipse.jst.server.tomcat.core.prefs重启Eclipse转载于:https://www.cnblogs.com/tnsay/p/11466746.html

java 判断object类型_Java学习-方法与多态的学习心得

一 1.什么是方法重写方法的重写或方法的覆盖&#xff08;overriding&#xff09;子类根据需求对从父类继承的方法进行重新编写重写时&#xff0c;可以用super.方法的方式来保留父类的方法构造方法不能被重写 2.方法重写规则(1)方法名相同(2)参数列表相同(3)返回值类型相同或者是…

实习日志(2)2011-12-30

这篇文章并没有给出如何使用ResultSet的具体例子&#xff0c;只是从ResultSet的功能性上进行了详细的讲述。希望这篇文章对大家理解ResultSet能够有所帮助。下面就是这篇文章的具体内容。 结果集(ResultSet)是数据中查询结果返回的一种对象&#xff0c;可以说结果集是一个…

Javascript使用三大家族和事件来DIY动画效果相关笔记(一)

1.offset家族◆offsetWidth和offsetHeight表示盒子真实的宽度高度&#xff0c;这个真实的宽度包括 四周的边框、四周的padding、及定义的宽度高度或内容撑开的高度和宽度&#xff0c;可以用来检测盒子实际的大小&#xff0c;属性也是只读不可写的&#xff0c;返回的是不带单位的…

React 学习

一、搭建webpack4.x环境 1.创建工程文件夹&#xff08;ReactDemo&#xff09; 2.在工程文件夹下&#xff0c;快速初始化项目 npm init -y // 创建一个package.json文件 3.在工程文件夹下&#xff0c;创建源码文件夹&#xff08;src&#xff09;和编译打包文件夹&#xf…

python创建mysql数据库_python 怎么创建create mysql的数据库

展开全部 我采用的是MySQLdb操作的MYSQL数据库。先来一个简单的例2113子吧&#xff1a; import MySQLdb try: connMySQLdb.connect(hostlocalhost,userroot,passwdroot,dbtest,port3306) curconn.cursor() cur.execute(select * from user) cur.close() conn.close() except My…

杂谈---改变个人习惯

在提升编码技术的过程&#xff0c;自己也在生活中学到了很多。发现了自己的很多缺陷&#xff1a;不够勇敢、不够冒险、骄傲的无厘头&#xff0c;还有自己对情绪的掌控远没有自己想象的那么有火候&#xff0c;这段时间也得好好谢谢她&#xff0c;要不然我压根意识不到问题有多严…

ldconcig详解

ldconfig是一个动态链接库管理命令&#xff0c;为了让动态链接库为系统所共享,还需运行动态链接库的管理命令--ldconfigldconfig 命令的用途,主要是在默认搜寻目录(/lib和/usr/lib)以及动态库配置文件/etc/ld.so.conf内所列的目录下,搜索出可共享的动态链接库(格式如前介绍,lib…

第3章—高级装配—条件化的Bean

条件化的Bean 通过活动的profile&#xff0c;我们可以获得不同的Bean。Spring 4提供了一个更通用的基于条件的Bean的创建方式&#xff0c;即使用Conditional注解。 Conditional根据满足某个特定的条件创建一个特定的Bean。比如&#xff0c;当某一个jar包在一个类路径下时&#…

c#委托与事件(二)

这篇博客是在上篇的基础开始讲述了一下委托的一些用法&#xff0c;首先我举一个例子说明了一下前面章节的知识点&#xff0c;接下来我说了将方法作为参数传递的一个案例&#xff0c;接下来实现了一个委托实现冒泡排序的方法&#xff0c;如果你们和我一样正在学习&#xff0c;希…

互联网公司java面试题(一)

1、JDK和JRE区别&#xff1f; JDK是整个JAVA的核心&#xff0c;包括了Java运行环境JRE&#xff0c;一堆Java工具和Java基础的类库。通过JDK开发人员将源码文件(java文件)编译成字节码文件(class文 件)。JRE是Java运行环境&#xff0c;不含开发环境&#xff0c;即没有编译器和调…

python属于哪种类型的语言_Python是什么类型的编程语言,有什么特性

由于近几年人工智能的不断发展&#xff0c;Python也跟着火了&#xff0c;因为Python是深度学习技术的主流应用编程语言。同时它的应用场景很多&#xff0c;被称为“胶水语言”。下面给大家科普一下Python这门神奇的编程语言&#xff0c;以及语言特性&#xff0c;帮大家更清晰的…

Linux下C语言线程池的实现(1)

http://hi.baidu.com/lingiloveyou/blog/item/21e57cf3322a6b40342accc7.html 什么时候需要创建线程池呢&#xff1f;简单的说&#xff0c;如果一个应用需要频繁的创建和销毁线程&#xff0c;而任务执行的时间又非常短&#xff0c;这样线程创建和销毁的带来的开销就不容忽视&am…

一篇简单易懂的原理文章,让你把JVM玩弄与手掌之中

jvm原理 Java虚拟机是整个java平台的基石&#xff0c;是java技术实现硬件无关和操作系统无关的关键环节&#xff0c;是java语言生成极小体积的编译代码的运行平台&#xff0c;是保护用户机器免受恶意代码侵袭的保护屏障。JVM是虚拟机&#xff0c;也是一种规范&#xff0c;他遵循…

python代码画皮卡丘_Python气象绘图实例我们一起画台风(代码+数据)

前段时间袭击中国的超强台风“利奇马”&#xff0c;以及这两天袭击美国的五级飓风“多利安”&#xff0c;让我们感受到了大自然的力量。所以&#xff0c;今天分享一个简单的Python实例&#xff0c;也算是延续前面python气象绘图系列(点击链接1&#xff1b;点击链接2)&#xff0…