JDBC部分

mysql内置数据库:

  • information_schema数据库:
    其中保存着关于MySQL服务器所维护的所有其他数据库的信息。如数据库名,数据库的表,表栏的数据类型与访问权限等。

  • performance_schema 数据库:
    存储引擎:命名PERFORMANCE_SCHEMA ,主要用于收集数据库服务器性能参数。

  • mysql 数据库:
    mysql库是系统库,里面保存有账户信息,权限信息,存储过程,event,时区等信息。
    test 数据库:
    这个是安装时候创建的一个测试数据库,和它的名字一样,是一个完全的空数据库,没有任何表,可以删除。

连接池:
 Sun公司在jdbc2.0规范中提供了一个新的接口javax.sql.DataSource来实现连接池。

数据库连接池的实现步骤:
  1. 我们自定义的数据库连接池需要实现javax.sql.DataSource这个接口。这是sun公司规定。
备注: DriverManager 是JDBC 1.0规范,可以创建连接,但是不支持连接池
DataSource是JDBC 2.0规范,支持连接池
我们使用linkedList来存放连接。当做连接池。
  2. 在dataSource的构造方法中初始化数据库连接池。向池子中创建一定数量的数据库连接对象。
  3. 重写DataSource这个接口中的getConnection方法。注意:这个方法是从我们数据库连接池中获取数据,从连接池中获取之后需要将池子中的对象给删掉
  4. 提供释放资源的方法。注意:这个释放资源是将连接放回连接池中,而不是关闭连接

/**
 * 学习内容: 自定义连接池
 * 1.实现javax.sql.DataSource接口
 * 2.创建容器LinkedList
 * 3.在构造方法中创建一些连接
 * 4.实现DataSource接口中的getConnection方法,从容器中获取
 * 5.将使用完毕的连接放回容器
 */
public class MyDataSource implements DataSource {
            //arraylist 查询快,增删慢,LinkedList增删快,连接主要做增删
            private static LinkedList<Connection> pool=new LinkedList<Connection>();
            public MyDataSource() {
                        super();
                        //向容器添加一些连接
                        for(int i=0;i<10;i++){
                                    Connection connection = JdbcUtil.getConnection();
                                    pool.addFirst(connection);
                        }
                        System.out.println("创建连接,默认往连接池中添加了"+pool.size()+"个连接");
            }
            @Override
            public   Connection getConnection(){
                        if(pool.isEmpty()){
                                    for(int i=0;i<3;i++){
                                                Connection connection = JdbcUtil.getConnection();
                                                pool.addFirst(connection);
                                    }           
                        }
                        final Connection connection = pool.removeLast();
                        System.out.println("从连接池中获取连接,当前还有"+pool.size()+"个连接");
                        ClassLoader loader=connection.getClass().getClassLoader(); 
                        Class<?>[] interfaces=connection.getClass().getInterfaces();
                        InvocationHandler h=new InvocationHandler() {
                                    @Override
                                    public Object invoke(Object proxy, Method method, Object[] args)
                                                            throws Throwable {
                                                if("close".equals(method.getName())){
                                                            //增强close方法,将连接放回连接池
                                                            backToPool(connection);
                                                            System.out.println("执行了动态代理的方法,将连接放回了连接池,当前还有"+pool.size()+"个连接");
                                                            return null;
                                                }
                                                //如果不是close方法,不做处理,让被代理类本身执行方法即可
                                                return method.invoke(connection, args);
                                    }
                        };
                        //返回连接的代理类
                        Connection proxyConn  = (Connection) Proxy.newProxyInstance(loader, interfaces, h);
                        return proxyConn;
            }
            //定义一个将连接放回容器的方法
            public  void backToPool(Connection connection){
                        pool.addFirst(connection);
                        System.out.println("将连接放回连接池,当前还有"+pool.size()+"个连接");
            }
}

            /**
             * 2个问题需要优化:
             *
             * 1.dataSource是一个接口,接口接收实现类,来解耦,提高扩展性
             * MyDataSource dataSource = new MyDataSource();改为DataSource dataSource = new MyDataSource()
             *
             * 2.dataSource是sun规定的规范,我们不应该自己定义一些不同的api,程序员不用额外地记忆新的api,程序员习惯了使用close方法去释放连接,
             *         那么数据库连接池也应该调用连接的close方法来将连接放回连接
             */
            public static void main(String[] args){
                        DataSource dataSource = null;
                        Connection connection = null;
                        PreparedStatement prepareStatement = null;
                        ResultSet resultSet = null;
                        try {
                                    dataSource = new MyDataSource();
                                    connection = dataSource.getConnection();
                                    String sql="select * from User";
                                    prepareStatement = connection.prepareStatement(sql);
                                    resultSet = prepareStatement.executeQuery();
                                    while(resultSet.next()){
                                                int id = resultSet.getInt("id");
                                                String username = resultSet.getString("username");
                                                String password = resultSet.getString("password");
                                                int age = resultSet.getInt("age");
                                                System.out.println(id+username+password+age);
                                    }
                        } catch (SQLException e) {
                                    e.printStackTrace();
                        }finally{
                                    JdbcUtil.release(resultSet, prepareStatement, null);
                        }
            }

Dbutils三个核心类功能介绍

  • QueryRunner中提供对sql语句操作的API.
  • ResultSetHandler接口,用于定义select操作后,怎样封装结果集.
  • DbUtils类,它就是一个工具类,定义了关闭资源与事务处理的方法

      java.lang.ThreadLocal 该类提供了线程局部 (thread-local) 变量,用于在当前线程中共享数据。ThreadLocal工具类底层就是一个Map,key存放的当前线程,value存放需要共享的数据。如下图,在整个程序运行中,我们有个Map变量,每条线程又有属于自己的id,也就是thread.currentThread。所以我们可以向map中存储一个值,key就是当前线程的id,值就是我们需要存储的值。这样,我们在整个线程中的任何一个地方,我们可以通过当前线程的id从map中获取的值都是同一个。这样,我们在一条线程中,我们在service层和dao层从map中就能获取同一个connection对象了。

事务 (ThreadLocal版本转账案例)

            public class ThreadLocalC3p0JdbcUtil {
                        //ComboPooledDataSource构造方法默认会去找src/c3p0-config.xml 这个配置文件
                        //之前的获取连接是采用DriverManager类实现的 静态代码块读取配置文件 没有使用开源数据源
                        //这里的获取连接是采用开源连接池获取数据源 改写了原来获取连接的方法
                        private static DataSource datasource=new ComboPooledDataSource();

                        //创建一个位置存放线程的Connection
                        private static ThreadLocal<Connection> local=new ThreadLocal<Connection>();

                        //获取数据源
                        public static DataSource getDataSource(){
                                    return datasource;
                        }

                        //注册和获取连接
                        public static Connection getConnection(){
                                    //从指定的位置获取连接
                                    Connection connection = local.get();
                                    //第一次获取肯定是null,还没存放
                                    if(connection==null){
                                                try {
                                                            connection=datasource.getConnection();
                                                            //第一次将连接放置到指定位置
                                                            local.set(connection);
                                                            System.out.println("第一次创建,放进入到ThreadLocal中:"+connection);
                                                } catch (SQLException e) {
                                                            e.printStackTrace();
                                                }
                                    }else{
                                                System.out.println("不是第一次创建 直接从ThreadLocal中获取:"+connection);
                                    }
                                    return connection;
                        }

                        //资源释放
                        //如果使用DbUtils包中QueryRunner类来crud的时候是不需要释放连接的 也就是说下面的方法不需要使用 如果没有使用DbUtils那么还是需要使用下面的方法释放资源的
                        public static void release(ResultSet resultSet,Statement statement, Connection connection){
                                    if (resultSet != null) {
                                                try {
                                                            resultSet.close();
                                                } catch (SQLException e) {
                                                            // TODO Auto-generated catch block
                                                            e.printStackTrace();
                                                }
                                                resultSet = null;
                                    }
                                    if (statement != null) {
                                                try {
                                                            statement.close();
                                                } catch (SQLException e) {
                                                            // TODO Auto-generated catch block
                                                            e.printStackTrace();
                                                }
                                                statement = null;
                                    }

                                    if (connection != null) {

                                                try {
                                                            connection.close();
                                                } catch (SQLException e) {
                                                            // TODO Auto-generated catch block
                                                            e.printStackTrace();
                                                }
                                                connection = null;
                                    }
                        }

                        //释放threadlocal的值
                        public static void remove() {
                                    //remove时有没有直接将其他的线程的连接删除???
                                    local.remove();
                        }
}
public class ThreadLocalWebApp {
            public static void main(String[] args) {
                        //转账业务
                        //jack给rose转账100
                        String outer = "jack";
                        String inner = "rose";
                        double money = 100;

                        ThreadLocalAccountService service = new ThreadLocalAccountService();
                        boolean result = service.tranfer(outer,inner,money);

//                    new Thread(){
//                                @Override
//                                public void run() {
//                                            String outer = "jack";
//                                            String inner = "rose";
//                                            double money = 100;
//                                            
//                                            ThreadLocalAccountService service = new ThreadLocalAccountService();
//                                            boolean result = service.tranfer(outer,inner,money);
//                                }
//                    }.start();

                        if (result) {
                                    System.out.println("转账成功");
                        }else{
                                    System.out.println("转账失败");
                        }
            }
}
public class ThreadLocalAccountService {
            public boolean tranfer(String outer, String inner, double money) {
                        ThreadLocalAccountDao dao=new ThreadLocalAccountDao();
                        //这里获取的连接是当前线程的连接
                        Connection connection = ThreadLocalC3p0JdbcUtil.getConnection();
                        try {
                                    //使用连接开启事务(注意,如果sql要被事务进行管理,那么sql必须与开启事务的连接是同一个)
                                    //问题:当前开启事务的连接与sql执行的连接是同一个么???
                                    connection.setAutoCommit(false);//关闭自动提交,开启事务
                                    dao.outMoney(outer,money);
                                    //转账流水等业务操作
                                    int i = 1/0;
                                    dao.inMoney(inner, money);
                                    //业务成功,提交事务
                                    //connection.commit();
                                    DbUtils.commitAndClose(connection);
                                    return true;
                        } catch (Exception e) {
                                    e.printStackTrace();
                                    DbUtils.rollbackAndCloseQuietly(connection);
                        }finally{
                                    ThreadLocalC3p0JdbcUtil.remove();
                        }
                        return false;
            }
}
public class ThreadLocalAccountDao {
            //dao层用来让对应的账户减钱
            public int outMoney(String outer, double money) throws Exception {
                        QueryRunner runner = new QueryRunner();
                        String sql="update account set money=money-? where name=?";
                        //从指定的位置去获取连接
                        Connection connection = ThreadLocalC3p0JdbcUtil.getConnection();
                        int updatecount = runner.update(connection,sql, money,outer);
                        return updatecount;
            }
            //dao层用来让对应的账户加钱
            public int inMoney(String inner, double money) throws Exception {
                        QueryRunner runner = new QueryRunner(C3p0JdbcUtil.getDataSource());
                        String sql="update account set money=money+? where name=?";
                        //从指定的位置去获取连接
                        Connection connection = ThreadLocalC3p0JdbcUtil.getConnection();
                        int updatecount = runner.update(connection,sql, money,inner);
                        return updatecount;
            }
}

事务的特性 ACID
数据库的事务必须具备ACID特性,ACID是指 Atomicity(原子性)、Consistensy(一致性)、Isolation(隔离型)和Durability(持久性)的英文缩写。

原子性(Atomicity)

事务包装的一组sql,要么都执行成功,要么都失败。这些操作是不可分割的。

一致性(Consistency)

  数据库的数据状态是一致的。
事务的成功与失败,最终数据库的数据都是符合实际生活的业务逻辑。一致性绝大多数依赖业务逻辑和原子性。

持久性:(Durability)

事务成功提交之后,对于数据库的改变是永久的。哪怕数据库发生异常,重启之后数据亦然存在。

隔离性(Isolation)

一个事务的成功或者失败对于其他的事务是没有影响。2个事务应该相互独立。

事务的隔离级别
如果不考虑事务的隔离性,由于事务的并发,将会出现以下问题:
  1、脏读 – 最严重,杜绝发生
  2、不可重复读
  3、幻读(虚读)

隔离级别:解决问题
* 数据库规范规定了4种隔离级别,分别用于描述两个事务并发的所有情况。

1. read uncommitted 读未提交,一个事务读到另一个事务没有提交的数据。

    1. 存在:3个问题(脏读、不可重复读、虚读)。
    2. 解决:0个问题
2. read committed 读已提交,一个事务读到另一个事务已经提交的数据。

    1. 存在:2个问题(不可重复读、虚读)。
    2. 解决:1个问题(脏读)
3. repeatable read:可重复读,在一个事务中读到的数据始终保持一致,无论另一个事务是否提交。

    1. 存在:1个问题(虚读)。
    2. 解决:2个问题(脏读、不可重复读)
4. serializable 串行化,同时只能执行一个事务,相当于事务中的单线程。

    1. 存在:0个问题。
    2. 解决:3个问题(脏读、不可重复读、虚读)


* 安全和性能对比

    * 安全性:serializable > repeatable read > read committed > read uncommitted
    * 性能 : serializable < repeatable read < read committed < read uncommitted
* 常见数据库的默认隔离级别:

    * MySql:repeatable read
    * Oracle:read committed

在mysql数据库中,底层对于幻读做了优化,演示不了。

猜你喜欢

转载自blog.csdn.net/huming1250446609/article/details/82432515