数据库操作之Jdbc下

1.事务
1.1事务的四大特性(ACID)
  • 原子性:事务中的所有操作要么全部执行成功,要么执行全部失败。
  • 一致性:事务执行后,数据库状态与其它业务规则保持一致。
  • 隔离性:隔离性是指在并发操作中,不同事务之间应该隔离开来,使每个并发中的事务不会相互干扰。
  • 持久性:一旦事务提交成功,事务中所有的数据操作都必须被持久化到数据库中。即使提交事务后数据库马上崩溃,在数据库重启后,也必须能保证通过某种机制恢复数据。
1.2mysql中操作事务

在控制台中输入语句:start transaction;即开始事务。

在控制台中输入语句:commit transaction;即提交事务。

在控制台中输入语句:rollback;回滚事务,即在此事务中执行的操作全部无效,数据库回到start transaction;之前(前提是使用该语法前没有执行commit transaction;操作)。

1.3Jdbc中操作事务

在Jdbc中处理事务都是通过Connection对象完成的,同一事务中的所有操作,都在使用同一个Connection对象。

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

con.commit();提交并结束事务。

con.rollback();回滚事务。

2.事务的隔离级别
2.1事务的并发读问题
  • 脏读:读取到另一份事务未提交数据,即读到了脏数据。
  • 不可重复读:两次读取不一致。对统一记录的两次读取不一致,因为另一事务对该记录做了修改。
  • 幻读:又叫虚读。对同一张表的两次查询不一致,因为另一事务进行了插入了一条记录的操作。
2.2四大隔离级别(防止上述问题)
  • SERIALIZABLE(串行化):不会出现任何并发问题,因为它是对同一数据的访问是串行的,非并发访问的。性能最差,可能导致死锁。
  • REPEATABLE READ(可重复读)(mysql默认级别):防止脏读和不可重复读,不能处理幻读问题。性能比a的好。
  • READ COMMITTED(读已提交数据)(Oracle默认级别):防止脏读,没有处理不可重复读,也没有处理幻读。性能比上述b好。
  • READ UNCOMMITTED(读未提交数据):可能出现任何事务并发问题。性能最好。但基本没人用。
2.3查看mysql的隔离级别

在控制台中输入语句:select @@tx_isolation;

也可以通过下面命令来设置隔离级别:set transaction isolationlevel[4选1];

2.4在Jdbc中设置隔离级别

con.setTransactionisolation[int lever];

3.数据库连接池

用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、拓机。如下图所示:

Paste_Image.png

数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现的尤为突出.。对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标。数据库连接池正式针对这个问题提出来的。数据库连接池负责分配,管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。如下图所示:

Paste_Image.png

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

  • 1.最小连接数(MinActive):是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费。

  • 2.最大连接数(MaxActive):是连接池能申请的最大连接数,如果数据库连接请求超过次数,后面的数据库连接请求将被加入到等待队列中,这会影响以后的数据库操作。

  • 3.如果最小连接数与最大连接数相差很大:那么最先连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新的数据库连接.不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,他将被放到连接池中等待重复使用或是空间超时后被释放。

3.1池参数(所有池参数都有默认值)

设置初始化大小:connection.setInitialSize();
设置最小空闲连接数:connection.setMinIdle();
设置最大空闲连接数:connection.setMaxIdle();
设置最小连接数:connection.setMinActive();
设置最大连接数:connection.setMaxActive();
设置增量:一次创建的最小单位。
设置最大的等待时间:connection.setMaxWait();

3.2四大连接参数

连接池也是使用Jdbc中的四大连接参数和驱动jar包来完成创建连接对象。

3.3实现的接口

连接池必须实现javax.sql.DataSource接口。

从连接池返回的Connection对象,它的close()方法与众不同。调用它的close()方法不是关闭,而是把连接归还给池。

3.4DBCP数据库连接池

DBCP是Apache软件基金组织下的开源连接池实现,要使用DBCP数据源,需要应用程序应在系统中增加如下两个jar文件:

  • Commons-dbcp.jar:连接池的实现
  • Commons-pool.jar:连接池实现的依赖库
    Demo:
public class Demo{
    public static void main(String[] args)
    {
        /*
         *1.创建连接池对象
         *2.配置四大参数
         *3.配置池参数
         *4.得到连接对象
         */
        BasicDataSource dataSource=new BasicDataSource();
        
        dataSource.setDriverClassName(“com.mysql.jdbc.Driver”);
        dataSource.setUrl(“jdbc:mysql://localhost:3306/mydb”);
        dataSource.setUsername(“root”);
        dataSource.setPassword(123);
        
        dataSource.setMaxActive(20);
        dataSource.setMinIdle(3);
        dataSource.setMaxWait(1000);
        
        Connection con=dataSource.getConnection();
        System.out.println(con);
        
      /*
        *连接池内部使用四大参数创建了连接对象,即mysql驱动提供的Connection
        *连接池使用mysql的连接对象进行了装饰,只对close()方法进行了增强!
        *装饰之后的Connection的close()方法,用来把当前连接归还给池
        */
        con.close();//把连接归还给池。
    }
}

既然谈到装饰,那下面我们就在下文3.7中来谈谈装饰者模式。

3.5c3p0数据库连接池

c3p0,全名叫ComboPooledDataSource;

需要导入的jar包:

连接池的实现:c3p0-0.9.5.2.jar
依赖库:mchange-commons.jar

public class Demo{
    public static void main(String[] args)
    {
        //创建连接池对象
        ComboPooledDataSource dataSource=new ComboPooledDataSource();
        
        //进行四大参数的配置
        dataSource.setDriverClass(“com.mysql.jdbc.Driver”);
        dataSource.setJdbcUrl(“jdbc:mysql://localhost:3306/mydb”);
        dataSource.setUser("root");
        dataSource.setPassword("123");
        
        //池配置
        dataSource.setAcquireIncrement(5);
        dataSource.setInitialPoolSize(20);
        dataSource.setMinPoolSize(2);
        dataSource.setMaxPoolSize(50);
        
        Connection con=dataSource.getConnection();
        System.out.println(con);
        
        con.close();
    }
}
3.5.1c3p0配置文件的使用

配置文件要求:

文件名称:必须叫c3p0-config.xml。
文件的位置:必须在src下。

c3p0配置文件:


Paste_Image.png

写入配置文件后的Demo:

public class Demo{
    public static void main(String[] args)
    {
        //在创建连接池对象时,这个对象就会自动加载配置文件,不用我们来指定。
        ComboPooledDataSource data=new comboPooledDataSource();
        Connection con=data.getConnection();
        System.out.println(con);
    }
}

3.7装饰者模式
将对象增强的手段有:

  • 继承
    缺点:1.增强的内容是死的,不能动。2.被增强的对象也是死的。

  • 装饰者模式
    特点:1.增强的内容是不能修改的。2.被增强的对象可以是任意的。

  • 动态代理(AOP):

下面通过一个简单的例子来对装饰者模式进行讲解

class 咖啡类 {};
class 加奶咖啡 extends 咖啡类 {};
class 加糖咖啡 extends 咖啡类 {};
class 加盐咖啡 extends 咖啡类 {};

咖啡 a=new 加糖咖啡();
咖啡 b=new 加盐咖啡(a);//对a进行装饰,就是给a加盐
咖啡 c=new 加奶咖啡(b);//对b进行装饰,就是给b加奶

装饰者模式在Java API中的IO流中用到的很多。如BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter、ObjectInputStream、ObjectOutputStream这几个都是运用了装饰模式的装饰流。

4.ThreadLocal

早在JDK 1.2的版本中就提供Java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。

4.1Thread API
  • void set(Object value);设置当前线程的线程局部变量的值。
  • Object get();该方法返回当前线程所对应的线程局部变量。
  • void remove();将当前线程局部变量的值删除,目的是为了减少内存的占用。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
4.2ThreadLocal内部结构

ThreadLocal内部用Map来保存数据。虽然在使用上述API时没有给出键,但其实它内部使用了当前线程作为键。内部结构见下面demo:

class ThreadLocal
{
    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());
    }
}
5.dbtils结果集处理器介绍

需要导入的jar包:

  • common-dbutil.jar
  • c3p0.jar
  • mchange-commons.jar

关键要得到QueryRunner对象,然后调用其各种方法。

update()方法:

  • 1.int update(String sql,Object… params) 可执行增删改语句。
  • 2.重载方法int update(Connection con,String sql, Object… params)需要调用者提供Connection,这说明本方法不再管理Connection了。本重载方法支持事务。

query()方法:

  • 1.T query (String sql,ResultSetHandler rsh,Object… params)可执行查询操作。
  • 2.重载方法:T query(Connection con,String sql,ResultSetHandler rsh,Object… params); 本重载方法支持事务。它会先得到ResultSet,然后调用rsh的handle()把rs转换成需要的类型。

ResultSetHandler接口

  • 1.BeanHandler(单行)-->构造器需要一个class类型的参数,用来把一行结果转换成指定类型的javaBean对象。
  • 2.BeanListHandler(多行)—>构造器也是需要一个Class类型的参数,用来把一行结果集转换成一个javabean,哪么多行就是转换成List对象,一堆javabean。
  • 3.MapHandler(单行)—>把一行结果集转换成Map对象。
  • 4.MapListHandler(多行)—>把一行记录转换成一个Map,多行就是多个Map,即List。
  • 5.ScalarHandler(单行单列)->同来用于select count(*)from t_stu语句,结果集是单行单列的,它返回一个Object,就是count(*)的值,为long类型。

dbutil结果处理集原理代码:

package demo;
    import javax.sql.DataSource;
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    /**
     * Created by codingBoy on 16/10/19.
     */
     public class QR<T>
     {
        private DataSource dataSource;

    public QR(DataSource dataSource)
    {
        this.dataSource=dataSource;
    }

    public QR(){
        super();
    }

    public int update(String sql,Object... params)
    {
        Connection con=null;
        PreparedStatement pstmt=null;

        try
        {
            con=dataSource.getConnection();//通过连接池得到连接对象
            pstmt=con.prepareStatement(sql);

            initParams(pstmt,params);//给出参数

            return pstmt.executeUpdate();//调用update执行增、删、该
        }catch (Exception e)
        {
            throw new RuntimeException(e);
        }finally {
            try{
                if (pstmt!=null) pstmt.close();
                if (con!=null) con.close();
            }catch (SQLException e){}
        }
    }

    //给参数赋值
    public void initParams(PreparedStatement pstmt,Object... params) throws SQLException {
        for (int i = 0; i < params.length; i++)
        {
            pstmt.setObject(i+1,params[i]);
        }
    }

    public T query(String sql,RsHandler<T> rh,Object... params) throws SQLException {
        Connection con=null;
        PreparedStatement pstmt=null;
        ResultSet rs=null;

        try
        {
            con=dataSource.getConnection();//通过连接池得到连接对象
            pstmt=con.prepareStatement(sql);

            initParams(pstmt,params);//给出参数

            rs=pstmt.executeQuery();//调用update执行增、删、该

            return rh.handle(rs);
        }catch (Exception e)
        {
            throw new RuntimeException(e);
        }finally {
            
            if (rs!=null) rs.close();
            if (pstmt!=null) pstmt.close();
            if (con!=null) con.close();

        }
    }

    interface RsHandler<T>
    {
        public T handle(ResultSet rs);
    }
    }

这样我们以后对数据库进行增、删、改操作时,只需写以下代码即可:

    1.QueryRunner qr=new QueryRunner(JdbcUtils.getDataSource);//创建QueryRunner对象,并传入连接池对象
    
    2.String sql="insert into user values(?,?,?,?);//给出sql语句模板
    3.Object[] params={参数1,参数2,参数3,参数4};//传入参数
    4.qr.update(sql,params);//调用qr方法。

通过这简单的四步就可以对数据库进行增删改了。
对数据库进行查询操作时,只需写以下代码:

1.QueryRunner qr=new QueryRunner(JdbcUtils.getDataSource);//创建QueryRunner对象,并传入连接池对象
2.String sql="select * from user where id=?";//给出sql语句模板
3. Object[] params={参数};//传入参数
//4. ResultSetHandler<Object> rsh=new ResultSetHandler(){
//      @Override
//      public Object handle(Result rs) throws SQLException{
//          return null;
//      }
//  };
5.Object object=qr.query(sql,new BeanHandler<Object>(Object.class),params);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,924评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,781评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,813评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,264评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,273评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,383评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,800评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,482评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,673评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,497评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,545评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,240评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,802评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,866评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,101评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,673评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,245评论 2 341

推荐阅读更多精彩内容

  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,714评论 6 342
  • JDBC概述 在Java中,数据库存取技术可分为如下几类:JDBC直接访问数据库、JDO技术、第三方O/R工具,如...
    usopp阅读 3,525评论 3 75
  • 不知怎么说起。 晚上跟吉他去哈西耍完回来,萎在床上放歌听。 听着听着眼睛就流出水来了,鼻子酸酸的。 要我说世上就两...
    印颉阅读 207评论 0 1
  • 今天的晨读分享了来自《吸引力是这样炼成的》一书的三个内容,分别是立刻吸引陌生人(换位思考)、朋友就像保险单(维护关...
    十月_浅娴阅读 322评论 5 4
  • 简介: 我 90后……出身不富裕,住在半山腰,住简陋的土木房,下面住着牛,上面住着人,没有车,没有手机,没有...
    农英阅读 167评论 0 0