JavaWeb-17 (JDBC编程进阶2)

JavaWeb-17

JDBC编程进阶2

一、自定义框架

习惯:搞工具类尽量写多些注释

开始制定框架

项目架构:


c3p0-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day15</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </default-config>

    <named-config name="mysql">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day15</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </named-config>


    <named-config name="oracle">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day16</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </named-config>
</c3p0-config>

Account.java

package com.itheima.domain;
import java.io.Serializable;

public class Account implements Serializable {

    private int id;
    private String name ;
    private float money;

    public Account(int id, String name, float money) {
        super();
        this.id = id;
        this.name = name;
        this.money = money;
    }

    public Account() {
        super();
    }


    @Override
    public String toString() {
        return "Account [id=" + id + ", name=" + name + ", money=" + money
                + "]";
    }

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public float getMoney() {
        return money;
    }
    public void setMoney(float money) {
        this.money = money;
    }
}

AccountDao.java

package com.itheima.dao;

import java.util.List;

import com.itheima.domain.Account;

public interface AccountDao {

    public void addAccount(Account ac);

    public Account getAccountById(int id);

    public List<Account> findAll();
}

AccountDaoImpl.java

package com.itheima.dao.impl;

import java.util.List;

import com.itheima.dao.AccountDao;
import com.itheima.dbassist.BeanHandler;
import com.itheima.dbassist.BeanListHandler;
import com.itheima.dbassist.DBAssist;
import com.itheima.domain.Account;
import com.itheima.utils.C3P0Util;

public class AccountDaoImpl implements AccountDao {
    private DBAssist db = new DBAssist(C3P0Util.getDataSource());

    public void addAccount(Account ac) {
        db.update("insert into account values(?,?,?)",new Object[]{ac.getId(),ac.getName(),ac.getMoney()});
    }

    @Override
    public Account getAccountById(int id) {
        return (Account)db.query("select * from account where id=?", new BeanHandler(Account.class), new Object[]{id});
    }

    @Override
    public List<Account> findAll() {
        return (List<Account>)db.query("select * from account", new BeanListHandler(Account.class), null);
    }
}

C3P0Util.java

package com.itheima.utils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0Util {

    private static DataSource ds =  new ComboPooledDataSource("mysql");

    /**
     * 获取连接
     * @return
     * @throws Exception
     */
    public static Connection getConnection() throws Exception{
        return ds.getConnection();
    }

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

    /**
     * 关闭资源
     * 
     * @param rs
     * @param st
     * @param con
     */
    public static void release(ResultSet rs, Statement st, Connection con) {
        // 6.关闭资源
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            rs = null;
        }
        if (st != null) {
            try {
                st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            st = null;
        }
        if (con != null) {
            try {
                con.close();//不用担心了,肯定还回池中
            } catch (SQLException e) {
                e.printStackTrace();
            }
            con = null;//线程池中也要回收
        }

    }
}

DBAssist.java

package com.itheima.dbassist;

import java.sql.Connection;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import javax.sql.DataSource;



public class DBAssist {
    private DataSource ds;

    public DBAssist(DataSource ds) {
        super();
        this.ds = ds;
    }

    /**
     * CUD操作
     * @param sql
     * @param params
     */
    public void update(String sql,Object []params){
        Connection con = null;
        PreparedStatement st = null;

        try {
            con = ds.getConnection();
            st = con.prepareStatement(sql);
            //1.得到参数的元数据
            ParameterMetaData pmd =st.getParameterMetaData();
            //2.得到参数的个数,?的个数
            int count = pmd.getParameterCount();

            if(count>0){
                //说明有?号
                if(params==null || params.length==0){
                    throw new IllegalArgumentException("参数不能为空");
                }else if(count!=params.length){
                    throw new IllegalArgumentException("参数个数不匹配");
                }

                //3.?号赋值
                for (int i = 0; i < count; i++) {
                    st.setObject(i+1, params[i]);
                }
            }

            st.executeUpdate();

        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    public Object query(String sql,ResultSetHandler handler,Object params[]){
        Connection con = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        try {
            con = ds.getConnection();
            st = con.prepareStatement(sql);
            //1.得到参数的元数据
            ParameterMetaData pmd =st.getParameterMetaData();
            //2.得到参数的个数,?的个数
            int count = pmd.getParameterCount();

            if(count>0){
                //说明有?号
                if(params==null || params.length==0){
                    throw new IllegalArgumentException("参数不能为空");
                }else if(count!=params.length){
                    throw new IllegalArgumentException("参数个数不匹配");
                }

                //3.?号赋值
                for (int i = 0; i < count; i++) {
                    st.setObject(i+1, params[i]);
                }
            }

            //4.执行查询
            rs = st.executeQuery();

            //5.分而治之思想
            return handler.handle(rs);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("查询有误");
        }
    }
}

ResultSetHandler.java

package com.itheima.dbassist;

import java.sql.ResultSet;

public interface ResultSetHandler {

    /**
     * 将结果集转成一个对象
     * @param rs
     * @return
     */
    public Object handle(ResultSet rs );
}

BeanHandler.java

package com.itheima.dbassist;

import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;



public class BeanHandler implements ResultSetHandler {

    private Class clz ;

    public BeanHandler(Class clz) {
        super();
        this.clz = clz;
    }

    @Override
    public Object handle(ResultSet rs) {
        Object obj = null;
        //1.如果结果集不为空
        try {
            if(rs.next()){
                obj = clz.newInstance();//使用反射生成一个对象
                //2.得到结果集元数据
                ResultSetMetaData rsmd = rs.getMetaData();
                int count = rsmd.getColumnCount();//得到列数
                for (int i = 0; i <count; i++) {
                    //3.取列名(约定:列名与属性名相同)
                    String columnName = rsmd.getColumnName(i+1);//得到列名
                    Object columnValue = rs.getObject(columnName);//取列的值

                    //反射出属性
                    Field f = clz.getDeclaredField(columnName);
                    f.setAccessible(true);//强暴破解
                    f.set(obj, columnValue);//属性的值来自于列值
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }

}

BeanListHandler.java

package com.itheima.dbassist;

import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class BeanListHandler implements ResultSetHandler {
   private Class clz ;

    public BeanListHandler(Class clz) {
        super();
        this.clz = clz;
    }

    @Override
    public Object handle(ResultSet rs) {
        List list = new ArrayList();
        try {
            while(rs.next()){
                Object obj = clz.newInstance();//使用反射生成一个对象
                //2.得到结果集元数据
                ResultSetMetaData rsmd = rs.getMetaData();
                int count = rsmd.getColumnCount();//得到列数
                for (int i = 0; i <count; i++) {
                    //3.取列名(约定:列名与属性名相同)
                    String columnName = rsmd.getColumnName(i+1);//得到列名
                    Object columnValue = rs.getObject(columnName);//取列的值

                    //反射出属性
                    Field f = clz.getDeclaredField(columnName);
                    f.setAccessible(true);//强暴破解
                    f.set(obj, columnValue);//属性的值来自于列值
                }

                list.add(obj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }

}

JTest.java

package com.itheima.test;

import java.util.List;

import org.junit.Test;

import com.itheima.dao.AccountDao;
import com.itheima.dao.impl.AccountDaoImpl;
import com.itheima.domain.Account;

public class JTest {

    @Test
    public void testAdd(){
        AccountDao dao = new AccountDaoImpl();
        Account ac = new Account(4,"郭心瑜",10000000);
        dao.addAccount(ac);
    }

    @Test
    public void testQueryOne(){
        AccountDao dao = new AccountDaoImpl();
        Account ac = dao.getAccountById(4);
        System.out.println(ac);
    }

    @Test
    public void testQueryALL(){
        AccountDao dao = new AccountDaoImpl();
        List<Account> list = dao.findAll();
        for(Account ac:list)
            System.out.println(ac);
    }
}

结果:


AccountDao.java首先从数据处理层下手:

因为这个框架是用来操作SQL数据库中Account类的,所以我们就先定义一个数据处理层的处理Account表的框架接口,设计好了数据处理要使用到的方法名和传入的参数。

即设计AccountDao接口:里面包括

public void addAccount(Account ac);
public Account getAccountById(int id);
public List<Account> findAll();

DBAssist.java:

然后设计给数据处理层的主要类AccountDaoImpl.java提供连接资源、通过连接资源获取数据库元信息等功能的功能类DBAssist.java,该类设计了构造函数,把开源连接资源C3P0Util工具包类传入进来,使用其提供的连接资源,并使用其连接资源去反射出数据库的元信息,并返回给数据处理层的主要类AccountDaoImpl.java使用。

ResultSetHandler.java接口:

同时在DBAssist.java里处理从AccountDaoImpl.java发来的处理查询结果的ResultSet类的对象时,需要更多的处理方法,并决定是把接收到的ResultSet类的对象反射出的结果封装成Java对象还是保存一系列的Java对象的集合并返回给其接口的实现类,这是一种分而治之的思想,就是不断地把复杂的要处理的问题通过接口分发出去,让其接口的实现类来完成后并利用Java多态的机制把结果返回。这样可以让整个项目的耦合性大大降低!!

BeanHandler.java和BeanListHandler.java:

通过分而治之的思想,其两个Java类分别解决把ResultSet的查询结果对象反射成Java对象或保存Java对象集合并返回,最终是提供给DBAssist.java去使用。

AccountDaoImpl.java:

在前期准备完成后,AccountDaoImpl.java就可以使用DBAssist.java所提供的强大资源来为程序员提供封装好的数据处理层的方法去处理数据库中的数据。效率大大提高。,这就是自定义框架的流程。

二、dbutils框架(由自定义框架过渡)

dbutils使用经验:结合包里的文档进行编程:因为框架提供的方法的参数类型需要查询文档

项目架构:


AccountDao.java

package com.itheima.dao;

import java.util.List;

import com.itheima.domain.Account;

public interface AccountDao {

    public void addAccount(Account ac);

    public Account getAccountById(int id);

    public List<Account> findAll();


    //批量
    public void addAccounts(List<Account> list);
}

AccountDaoImpl.java

package com.itheima.dao.impl;

import java.sql.SQLException;
import java.util.List;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import com.itheima.dao.AccountDao;
import com.itheima.domain.Account;
import com.itheima.utils.C3P0Util;

public class AccountDaoImpl implements AccountDao {
    private QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());

    public void addAccount(Account ac) {
        try {
            qr.update("insert into account values(?,?,?)",ac.getId(),ac.getName(),ac.getMoney());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public Account getAccountById(int id) {
        try {
            return qr.query("select * from account where id=?", new BeanHandler<Account>(Account.class),id);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public List<Account> findAll() {
        try {
            return qr.query("select * from account", new BeanListHandler<Account>(Account.class));
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void addAccounts(List<Account> list) {
        try {
            Object [][] params = new Object[list.size()][];//定义二维数组时,代表行的下标必须给值,list.size()代表有多少个对象,就有多少行
            for (int i = 0; i < params.length; i++) {
                //遍历每一行,并且将对象赋值到具体的行,对象的属性赋值给当前行的某一列
                Account ac = list.get(i);//取第i个对象
                params[i]= new Object[]{ac.getId(),ac.getName(),ac.getMoney()};//第i行中的每一列赋值(值来自于对象的每个属性)
            }
            int []results= qr.batch("insert into account values(?,?,?)",params);

            //查看每条语句受影响的行数
            for(int i:results){
                System.out.println(i);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }

}

Account.java

package com.itheima.domain;

import java.io.Serializable;

public class Account implements Serializable {

    private int id;
    private String name ;
    private float money;

    public Account(int id, String name, float money) {
        super();
        this.id = id;
        this.name = name;
        this.money = money;
    }

    public Account() {
        super();
    }


    @Override
    public String toString() {
        return "Account [id=" + id + ", name=" + name + ", money=" + money
                + "]";
    }

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public float getMoney() {
        return money;
    }
    public void setMoney(float money) {
        this.money = money;
    }
}

C3P0Util.java

package com.itheima.utils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0Util {

    private static DataSource ds =  new ComboPooledDataSource("mysql");

    /**
     * 获取连接
     * @return
     * @throws Exception
     */
    public static Connection getConnection() throws Exception{
        return ds.getConnection();
    }

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

    /**
     * 关闭资源
     * 
     * @param rs
     * @param st
     * @param con
     */
    public static void release(ResultSet rs, Statement st, Connection con) {
        // 6.关闭资源
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            rs = null;
        }
        if (st != null) {
            try {
                st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            st = null;
        }
        if (con != null) {
            try {
                con.close();//不用担心了,肯定还回池中
            } catch (SQLException e) {
                e.printStackTrace();
            }
            con = null;//线程池中也要回收
        }

    }
}

c3p0-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day15</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </default-config>

    <named-config name="mysql">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day15</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </named-config>


    <named-config name="oracle">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day16</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </named-config>
</c3p0-config>

JTest.java

package com.itheima.test;

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

import com.itheima.dao.AccountDao;
import com.itheima.dao.impl.AccountDaoImpl;
import com.itheima.domain.Account;

public class JTest {

    @Test
    public void testAdd(){
        AccountDao dao = new AccountDaoImpl();
        Account ac = new Account(5,"尚也",10000000);
        dao.addAccount(ac);
    }

    @Test
    public void testQueryOne(){
        AccountDao dao = new AccountDaoImpl();
        Account ac = dao.getAccountById(5);
        System.out.println(ac);
    }

    @Test
    public void testQueryALL(){
        AccountDao dao = new AccountDaoImpl();
        List<Account> list = dao.findAll();
        for(Account ac:list)
            System.out.println(ac);
    }

    @Test
    public void testBatch(){
        AccountDao dao = new AccountDaoImpl();
        List<Account> list = new ArrayList<Account>();
        list.add(new Account(6,"于家宝",-100));
        list.add(new Account(7,"王杰",200));

        dao.addAccounts(list);
    }
}

结果:


最常用:BeanHandler、BeanListHandler

ResultSetHandlerTest.java

package com.itheima.test;

import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ArrayHandler;
import org.apache.commons.dbutils.handlers.ArrayListHandler;
import org.apache.commons.dbutils.handlers.ColumnListHandler;
import org.apache.commons.dbutils.handlers.KeyedHandler;
import org.apache.commons.dbutils.handlers.MapHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import org.junit.Test;

import com.itheima.utils.C3P0Util;

public class ResultSetHandlerTest {
    private QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
    @Test
    //将结果集中的第一行数据转成一个Object数组,数组的元素是第一行的所有列
    public void testArrayHandler(){
        try {
            Object [] objs = qr.query("select * from account where id=4", new ArrayHandler());
            for(Object obj:objs){
                System.out.println(obj);
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    @Test
    //将结果集中的所有行封装到List集合中,每一行封装到Object[]
    public void testArrayListHandler(){
        try {
            List<Object[]> list = qr.query("select * from account", new ArrayListHandler());
            for(Object []objs :list){
                for(Object obj:objs){
                    System.out.println(obj);
                }
                System.out.println("-------------------------------");
            }

        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    @Test
    //将结果集中某一列的所有数据封装到List集合,如果没有指定列名,默认是第一列
    public void testColumnListHandler(){
        try {
            List list = qr.query("select * from account", new ColumnListHandler("name"));
            for(Object obj :list){
                    System.out.println(obj);
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    @Test
    //将结果集中每一行封装到内层的Map(Key:列名,Value:列值),再将内层的Map对象放入到外层的Map集合(外层Map:   Key:是指定的某个列   Value:就是内层的Map对象)
    public void testKeyedHandler(){
        try {
            Map<Object ,Map<String,Object>> maps =  qr.query("select * from account", new KeyedHandler("id"));

            for(Map.Entry<Object,Map<String,Object>> map :maps.entrySet()){
                Map<String,Object> mm = map.getValue();//取内层Map
                for(Map.Entry<String, Object> m:mm.entrySet()){
                    //取当前行的每一列
                    System.out.println(m.getKey()+","+m.getValue());
                }
                System.out.println("-------------------------");
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    @Test
    //将结果集中第一行中的数据封装到Map中,key:列名  value:列值
    public void testMapHandler(){
        try {
            Map<String,Object> map=  qr.query("select * from account where id=4", new MapHandler());

            for(Map.Entry<String, Object> mm:map.entrySet()){
                System.out.println(mm.getKey()+","+mm.getValue());
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    @Test
    //将结果集中每一行放入Map中(key:列名,value:列值),将所有行形成的Map对象放入List集合中
    public void testMapListHandler(){
        try {
            List<Map<String,Object>> list=  qr.query("select * from account", new MapListHandler());
            for(Map<String,Object> map:list){//遍历行
                for(Map.Entry<String, Object> mm:map.entrySet()){//遍历行中的列
                    System.out.println(mm.getKey()+","+mm.getValue());
                }
                System.out.println("-----------------------");
            }

        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    @Test
    //ScalarHanlder 默认它是将结果集中的第一行的第一列封装成对象  ,它始终只能取结果集的第一行,默认是第一列,也可是指定的列
    //经常用于在统计时取数据
    public void testScalarHandler(){
        try {
            /*Integer count = ((Long)qr.query("select count(*) from account", new ScalarHandler())).intValue();
            System.out.println(count);*/
            Object obj = qr.query("select * from account where id=4", new ScalarHandler("name"));
            System.out.println(obj);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }


}

实现AccountDao.java--->AccountDaoImpl.java(在AccountDaoImpl里怎么使用dbutils框架?)

这在教你如何使用别人的开源框架:他们的方法不用我们去处理具体有多少个参数了,他们提供的是满足可变参数的接口

ResultSetHandlerTest类

测试ArrayHandler的方法:只去结果集的第一行的属!!


测试ArrayListHandler的方法:可以把结果集的所有行都放入到list里


测试ColumnListHandler方法:将结果集的某一列的所有数据封装到list集合里


测试KeyedHandler方法:看老师的笔记里的图:复杂的结构!


测试MapHandler的方法:


测试MapListHandler的方法:将结果集中的每一行放入Map中(key:列名,value:列值),将所有行携程的Map对象放入List集合中


测试ScalarHandler的方法(非常有用):经常用于在统计时取数据,一行一列的数据:默认它是将结果集中的第一行的第一列封装成对象,列可以指定,但行始终只能取一行!!!


三、事务在开发中的使用

情况:事务控制在Dao(不合理,太啰嗦)

实际开发中事务到底怎么控制的?

接下来写一个转帐的功能!

项目架构:


AccountDao.java

package com.itheima.dao;

public interface AccountDao {

    /**
     * 转账   aaa(-100)  ---------->bbb(+100)
     */
    public void transfor(String sourceName,String targetName,float money);
}

Account.java

package com.itheima.domain;

import java.io.Serializable;

public class Account implements Serializable {

    private int id;
    private String name ;
    private float money;

    public Account(int id, String name, float money) {
        super();
        this.id = id;
        this.name = name;
        this.money = money;
    }

    public Account() {
        super();
    }


    @Override
    public String toString() {
        return "Account [id=" + id + ", name=" + name + ", money=" + money
                + "]";
    }

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public float getMoney() {
        return money;
    }
    public void setMoney(float money) {
        this.money = money;
    }
}

C3P0Util.java

package com.itheima.utils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0Util {

    private static DataSource ds =  new ComboPooledDataSource("mysql");

    /**
     * 获取连接
     * @return
     * @throws Exception
     */
    public static Connection getConnection() throws Exception{
        return ds.getConnection();
    }

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

    /**
     * 关闭资源
     * 
     * @param rs
     * @param st
     * @param con
     */
    public static void release(ResultSet rs, Statement st, Connection con) {
        // 6.关闭资源
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            rs = null;
        }
        if (st != null) {
            try {
                st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            st = null;
        }
        if (con != null) {
            try {
                con.close();//不用担心了,肯定还回池中
            } catch (SQLException e) {
                e.printStackTrace();
            }
            con = null;//线程池中也要回收
        }

    }
}

c3p0-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day15</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </default-config>

    <named-config name="mysql">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day15</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </named-config>


    <named-config name="oracle">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day16</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </named-config>
</c3p0-config>

AccountService.java

package com.itheima.service;

public interface AccountService {
    public void transfor(String sourceName,String targetName,float money);
}

AccountDaoImpl.java

package com.itheima.dao.impl;

import java.sql.Connection;
import java.sql.SQLException;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;

import com.itheima.dao.AccountDao;
import com.itheima.domain.Account;
import com.itheima.utils.C3P0Util;
//问题?在数据访问层中出现了大量的业务逻辑代码,而按照DAO模式,它主要就是实现一些基本的CRUD
public class AccountDaoImpl implements AccountDao {
    private QueryRunner qr = new QueryRunner();
    public void transfor(String sourceName, String targetName, float money) {
         Connection con = null;
        //1.根据账号查询出Account对象
        try {
          con = C3P0Util.getConnection();
          con.setAutoCommit(false);//事务开启
          Account   sourceAccount = qr.query(con,"select * from account where name=?", new BeanHandler<Account>(Account.class),sourceName);
          Account   targetAccount = qr.query(con,"select * from account where name=?", new BeanHandler<Account>(Account.class),targetName);

          sourceAccount.setMoney(sourceAccount.getMoney()-money);
          targetAccount.setMoney(targetAccount.getMoney()+money);

          //更新到数据库
          qr.update(con,"update account set money=? where name=?",sourceAccount.getMoney(),sourceAccount.getName());
          int i=1/0;
          qr.update(con,"update account set money=? where name=?",targetAccount.getMoney(),targetAccount.getName());

          con.commit();//提交事务

        } catch (Exception e) {
            e.printStackTrace();
            if(con!=null){
                try {
                    con.rollback();//事务回滚
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }

}

AccountServiceImpl.java

package com.itheima.service.impl;

import com.itheima.dao.AccountDao;
import com.itheima.dao.impl.AccountDaoImpl;
import com.itheima.service.AccountService;

public class AccountServiceImpl implements AccountService {
    private AccountDao dao = new AccountDaoImpl();
    public void transfor(String sourceName, String targetName, float money) {
        dao.transfor(sourceName, targetName, money);
    }

}

Client.java

package com.itheima.test;

import com.itheima.service.AccountService;
import com.itheima.service.impl.AccountServiceImpl;

public class Client {

    /**
     * @param args
     */
    public static void main(String[] args) {
        AccountService as = new AccountServiceImpl();
        as.transfor("aaa", "bbb", 100);
    }

}

结果:


注意!(数据处理层的)Dao夹杂着业务逻辑:按人的思维来操作工具,不符合DAO的模式

问题:在数据访问层中出现了大量的业务逻辑代码,而按照DAO模式:

主要就是实现一些基本的CRUD。所以不合理

事务控制在Service层(业务处理层)中的问题

项目架构:


AccountDao.java

package com.itheima.dao;

import com.itheima.domain.Account;

public interface AccountDao {

    /**
     * 根据账户名查询出账户
     * @param name
     * @return
     */
    public Account getAccountByName(String name);

    /**
     * 更新一个账户
     * @param ac
     */
    public void updateAccount(Account ac);
}

AccountDaoImpl.java

package com.itheima.dao.impl;

import java.sql.Connection;
import java.sql.SQLException;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;

import com.itheima.dao.AccountDao;
import com.itheima.domain.Account;
import com.itheima.utils.C3P0Util;
//只实现最基本的CRUD
public class AccountDaoImpl implements AccountDao {
    private QueryRunner qr = new QueryRunner();
    private Connection con ;
    public AccountDaoImpl(Connection con) {
        super();
        this.con = con;
    }

    public Account getAccountByName(String name) {
        try {
            return qr.query(con,"select * from account where name=?", new BeanHandler<Account>(Account.class),name);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public void updateAccount(Account ac) {
        try {
            qr.update(con,"update account set money=? where name=?",ac.getMoney(),ac.getName());
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }



}

Account.java

package com.itheima.domain;

import java.io.Serializable;

public class Account implements Serializable {

    private int id;
    private String name ;
    private float money;

    public Account(int id, String name, float money) {
        super();
        this.id = id;
        this.name = name;
        this.money = money;
    }

    public Account() {
        super();
    }


    @Override
    public String toString() {
        return "Account [id=" + id + ", name=" + name + ", money=" + money
                + "]";
    }

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public float getMoney() {
        return money;
    }
    public void setMoney(float money) {
        this.money = money;
    }
}

AccountService.java

package com.itheima.service;

public interface AccountService {
    public void transfor(String sourceName,String targetName,float money);
}

AccoutServiceImpl.java

package com.itheima.service.impl;

import java.sql.Connection;
import java.sql.SQLException;

import com.itheima.dao.AccountDao;
import com.itheima.dao.impl.AccountDaoImpl;
import com.itheima.domain.Account;
import com.itheima.service.AccountService;
import com.itheima.utils.C3P0Util;
//问题? 在业务层中出现了大量的数据访问层的代码,比如:Connection    事务开户,提交,回滚代码
public class AccountServiceImpl implements AccountService {

    public void transfor(String sourceName, String targetName, float money) {
        Connection con =null;
        try {
             con = C3P0Util.getConnection();
             con.setAutoCommit(false);//开事务
             AccountDao dao = new AccountDaoImpl(con);
             //1.查询账户
            Account sourceAccount =  dao.getAccountByName(sourceName);
            Account targetAccount =  dao.getAccountByName(targetName);

            //2.修改账户
            sourceAccount.setMoney(sourceAccount.getMoney()-money);
            targetAccount.setMoney(targetAccount.getMoney()+money);

            //3.更新到数据库
            dao.updateAccount(sourceAccount);
            int i=1/0;
            dao.updateAccount(targetAccount);

            //4.事务提交
            con.commit();
            con.setAutoCommit(true);
        } catch (Exception e) {
            e.printStackTrace();
            if(con!=null){
                try {
                    con.rollback();//回滚
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
            }
        }finally{
            C3P0Util.release(null, null, con);
        }
    }

}

Client.java

package com.itheima.test;

import com.itheima.service.AccountService;
import com.itheima.service.impl.AccountServiceImpl;

public class Client {

    /**
     * @param args
     */
    public static void main(String[] args) {
        AccountService as = new AccountServiceImpl();
        as.transfor("aaa", "bbb", 100);
    }

}

C3P0Util.java

package com.itheima.utils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0Util {

    private static DataSource ds =  new ComboPooledDataSource("mysql");

    /**
     * 获取连接
     * @return
     * @throws Exception
     */
    public static Connection getConnection() throws Exception{
        return ds.getConnection();
    }

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

    /**
     * 关闭资源
     * 
     * @param rs
     * @param st
     * @param con
     */
    public static void release(ResultSet rs, Statement st, Connection con) {
        // 6.关闭资源
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            rs = null;
        }
        if (st != null) {
            try {
                st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            st = null;
        }
        if (con != null) {
            try {
                con.close();//不用担心了,肯定还回池中
            } catch (SQLException e) {
                e.printStackTrace();
            }
            con = null;//线程池中也要回收
        }

    }
}

c3p0-config.xml.

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day15</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </default-config>

    <named-config name="mysql">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day15</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </named-config>


    <named-config name="oracle">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day16</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </named-config>
</c3p0-config>.

结果:


Dao要存放纯粹的方法。那么业务逻辑放在哪里?

所以更改之前的Dao+DaoImpl

注意Connection的连接资源是否一致性。 所以在AccountDaoImpl里所用到的纯粹方法里的con资源要设置成一致的

把之前的Dao中的业务逻辑转到Serviceimpl上 在transfor()方法里进行业务逻辑处理

问题:在业务层中出现了大量的数据访问层的代码,比如:Connection 、事务开户、提交、回滚代码,怎么办?


ThreadLocal重点!! 


ThreadLocal该类提供了线程局部变量。

马上可以理解线程局部变量存在于某一个线程内
ThreadLocal内部有一个Map,Map的key是当前线程对象
只有本ThreadLocal能提取出来,谁放进去谁才有权利取
通过set()方法放进去;

查看JDK:ThreadLocal:发现方法很少:set()、get()

项目架构:


MyThread1.java

package com.itheima.test;

public class MyThread1 extends Thread {

    private ThreadLocal<String> ttL ;

    public MyThread1(ThreadLocal<String> ttL) {
        this.ttL = ttL;
    }

    public void run() {
        System.out.println("另一个线程取值:"+ttL.get());
    }
}

ThreadDemo1.java

package com.itheima.test;

public class ThreadDemo1 {

    public static void main(String[] args) {
         ThreadLocal<String> tL = new ThreadLocal<String>();//定义了一个线程局部变量
         tL.set("东方不败");

         MyThread1 th = new MyThread1(tL);
         th.start();

         System.out.println(tL.get());
    }

}

结果:


以上实验验证了threadlocal的可靠性:

可以用来在ServiceImpl里使用threadlocal来存储con资源

判断是个流程下来是否是同一个线程?

只要看流程里有没有开启令一个线程。

所以在ServiceImpl这main方法里定义一个内部线程threadlocal,用内部线程threadlocal去维护con资源,那么就不会被别的线程拿到threadlocal里的con资源,以此来保证项目con资源的唯一性。

引入AOP解决事务:真实事例中的事务控制

搞一个专门的事务管理类

项目架构:


AccountDao.java

package com.itheima.dao;

import com.itheima.domain.Account;

public interface AccountDao {

    /**
     * 根据账户名查询出账户
     * @param name
     * @return
     */
    public Account getAccountByName(String name);

    /**
     * 更新一个账户
     * @param ac
     */
    public void updateAccount(Account ac);
}

AccountDaoImpl.java

package com.itheima.dao.impl;

import java.sql.Connection;
import java.sql.SQLException;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;

import com.itheima.dao.AccountDao;
import com.itheima.domain.Account;
import com.itheima.utils.C3P0Util;
import com.itheima.utils.TransactionManager;
//只实现最基本的CRUD
public class AccountDaoImpl implements AccountDao {
    private QueryRunner qr = new QueryRunner();

    public Account getAccountByName(String name) {
        try {
            return qr.query(TransactionManager.getConnection(),"select * from account where name=?", new BeanHandler<Account>(Account.class),name);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public void updateAccount(Account ac) {
        try {
            qr.update(TransactionManager.getConnection(),"update account set money=? where name=?",ac.getMoney(),ac.getName());
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

Account.java

package com.itheima.domain;

import java.io.Serializable;

public class Account implements Serializable {

    private int id;
    private String name ;
    private float money;

    public Account(int id, String name, float money) {
        super();
        this.id = id;
        this.name = name;
        this.money = money;
    }

    public Account() {
        super();
    }


    @Override
    public String toString() {
        return "Account [id=" + id + ", name=" + name + ", money=" + money
                + "]";
    }

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public float getMoney() {
        return money;
    }
    public void setMoney(float money) {
        this.money = money;
    }
}

AccountService.java

package com.itheima.service;

public interface AccountService {
    public void transfor(String sourceName,String targetName,float money);
}

AccountServiceImpl.java
package com.itheima.service.impl;

import java.sql.Connection;
import java.sql.SQLException;

import com.itheima.dao.AccountDao;
import com.itheima.dao.impl.AccountDaoImpl;
import com.itheima.domain.Account;
import com.itheima.service.AccountService;
import com.itheima.utils.C3P0Util;
import com.itheima.utils.TransactionManager;
//存在问题?   现在通过TransactionManager解决了Connection共享的问题,但是业务层中依然有事务控制的代码
public class AccountServiceImpl implements AccountService {

    public void transfor(String sourceName, String targetName, float money) {
        try {

            TransactionManager.startTransaction();//开事务
            AccountDao dao = new AccountDaoImpl();
             //1.查询账户
            Account sourceAccount =  dao.getAccountByName(sourceName);
            Account targetAccount =  dao.getAccountByName(targetName);

            //2.修改账户
            sourceAccount.setMoney(sourceAccount.getMoney()-money);
            targetAccount.setMoney(targetAccount.getMoney()+money);

            //3.更新到数据库
            dao.updateAccount(sourceAccount);
            int i=1/0;
            dao.updateAccount(targetAccount);

            TransactionManager.commit();//提交事务

        } catch (Exception e) {
            e.printStackTrace();
            try {
                TransactionManager.rollback();//回滚事务
            } catch (Exception e1) {
                e1.printStackTrace();
            }
        }
    }

}

Client.java

package com.itheima.test;

import com.itheima.service.AccountService;
import com.itheima.service.impl.AccountServiceImpl;

public class Client {

    /**
     * @param args
     */
    public static void main(String[] args) {
        AccountService as = new AccountServiceImpl();
        as.transfor("aaa", "bbb", 100);
    }

}

C3P0Util.java

package com.itheima.utils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0Util {

    private static DataSource ds =  new ComboPooledDataSource("mysql");

    /**
     * 获取连接
     * @return
     * @throws Exception
     */
    public static Connection getConnection() throws Exception{
        return ds.getConnection();
    }

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

    /**
     * 关闭资源
     * 
     * @param rs
     * @param st
     * @param con
     */
    public static void release(ResultSet rs, Statement st, Connection con) {
        // 6.关闭资源
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            rs = null;
        }
        if (st != null) {
            try {
                st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            st = null;
        }
        if (con != null) {
            try {
                con.close();//不用担心了,肯定还回池中
            } catch (SQLException e) {
                e.printStackTrace();
            }
            con = null;//线程池中也要回收
        }

    }
}

TransactionManager.java

package com.itheima.utils;

import java.sql.Connection;
import java.sql.SQLException;

//用于实现事务管理类
public class TransactionManager {

    private static ThreadLocal<Connection> tL = new ThreadLocal<Connection>();

    //从池中取一个连接------------------>放入到ThreadLocal------>(实现当前线程内部共享同一个Connection)
    public static Connection getConnection(){
        Connection con = tL.get();//从线程局部变量中取一个连接
        if(con==null){
            //从池中取一个连接
            try {
                con = C3P0Util.getConnection();
                tL.set(con);//将一个连接放入线程局部变量中
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return con;
    }

    public static void startTransaction() throws Exception{
        Connection con = getConnection();
        con.setAutoCommit(false);
    }

    public static void commit() throws Exception{
        Connection con = getConnection();
        con.commit();
    }
    public static void rollback() throws Exception{
        Connection con = getConnection();
        con.rollback();
    }
}

c3p0-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day15</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </default-config>

    <named-config name="mysql">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day15</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </named-config>


    <named-config name="oracle">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day16</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </named-config>
</c3p0-config>

结果:


问题:虽然测试没问题:但是也只是包装成了方法给别的类调用,也就是説业务层依然有事务控制的代码,那么怎么改进?继续去掉AccountServiceImpl里的事务管理方法。

新思想:AOP

BeanFactory:

//AOP:面向切面编程:Aspect-Oriented-Progeam
也叫面向编程

生活中的类比例子:换脑袋!!

事务管理了我们已经准备好了,但问题是什么时候合适的时候切入执行!!!
所以切面编程解决了该问题:!!

什么是切面编程:

就是现在系统中分离中某些方面的功能集中实现,然后在系统运行时,又将这些集中实现的方面代码植入到现有的系统中常用于事务控制,日志控制,积分、权限功能。

动态代理

//AOP的内部实现技术(原理):动态代理!!
BeanFactory{
    public AccountService getAccountService(){
        AccountService as = new AccountServiceImpl();
        //引入动态代理:以便植入方面代码
        .....(as.getClass().getClassLoader(),as.getClass().getInterfaces(),
            new InvocationHandler(){
                invoke(....)
                {    
                    ...//前置通知
                        。。。
                    ...//后置通知
                    ...//异常通知
                }
            }
        );
    }
}

这样业务层就没有任何事务管理类的代码。
直接在Client中new对象直接newBeanFactory中的getAccountService
以上就是AOP

因此业务逻辑层就处理业务,需要加入什么机制就使用AOP思想中的动态代理加入一些自己的机制:例如事务控制,日志控制,积分、权限功能。

BeanFactory也称为容器,是专门用于提供对象的(Spring框架也提供有类似的Spring容器,在容器中植入方面代码(如声明式事务代码))

项目架构:


AccountDao.java

package com.itheima.dao;

import com.itheima.domain.Account;

public interface AccountDao {

    /**
     * 根据账户名查询出账户
     * @param name
     * @return
     */
    public Account getAccountByName(String name);

    /**
     * 更新一个账户
     * @param ac
     */
    public void updateAccount(Account ac);
}

AccountDaoImpl.java

package com.itheima.dao.impl;

import java.sql.Connection;
import java.sql.SQLException;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;

import com.itheima.dao.AccountDao;
import com.itheima.domain.Account;
import com.itheima.utils.C3P0Util;
import com.itheima.utils.TransactionManager;
//只实现最基本的CRUD
public class AccountDaoImpl implements AccountDao {
    private QueryRunner qr = new QueryRunner();

    public Account getAccountByName(String name) {
        try {
            return qr.query(TransactionManager.getConnection(),"select * from account where name=?", new BeanHandler<Account>(Account.class),name);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public void updateAccount(Account ac) {
        try {
            qr.update(TransactionManager.getConnection(),"update account set money=? where name=?",ac.getMoney(),ac.getName());
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

Account.java

package com.itheima.domain;

import java.io.Serializable;

public class Account implements Serializable {

    private int id;
    private String name ;
    private float money;

    public Account(int id, String name, float money) {
        super();
        this.id = id;
        this.name = name;
        this.money = money;
    }

    public Account() {
        super();
    }


    @Override
    public String toString() {
        return "Account [id=" + id + ", name=" + name + ", money=" + money
                + "]";
    }

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public float getMoney() {
        return money;
    }
    public void setMoney(float money) {
        this.money = money;
    }
}

AccountService.java

package com.itheima.service;

public interface AccountService {
    public void transfor(String sourceName,String targetName,float money);
}

AccountServiceImpl.java

package com.itheima.service.impl;

import java.sql.Connection;
import java.sql.SQLException;

import com.itheima.dao.AccountDao;
import com.itheima.dao.impl.AccountDaoImpl;
import com.itheima.domain.Account;
import com.itheima.service.AccountService;
import com.itheima.utils.C3P0Util;
import com.itheima.utils.TransactionManager;

public class AccountServiceImpl implements AccountService {
    private AccountDao dao = new AccountDaoImpl();
    public void transfor(String sourceName, String targetName, float money) {

             //1.查询账户
            Account sourceAccount =  dao.getAccountByName(sourceName);
            Account targetAccount =  dao.getAccountByName(targetName);

            //2.修改账户
            sourceAccount.setMoney(sourceAccount.getMoney()-money);
            targetAccount.setMoney(targetAccount.getMoney()+money);

            //3.更新到数据库
            dao.updateAccount(sourceAccount);
            int i=1/0;
            dao.updateAccount(targetAccount);

    }

}

Client.java

package com.itheima.test;

import com.itheima.service.AccountService;
import com.itheima.service.impl.AccountServiceImpl;
import com.itheima.utils.BeanFactory;

public class Client {

    /**
     * @param args
     */
    public static void main(String[] args) {
        AccountService as = BeanFactory.getAccountService();
        as.transfor("aaa", "bbb", 100);
    }

}

BeanFactory.java

package com.itheima.utils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import com.itheima.service.AccountService;
import com.itheima.service.impl.AccountServiceImpl;

//AOP:Aspect-Oriented Program   面向切面编程 (面向方面编程)
    //什么是面向切面编程:就是先在系统中分离中某些方面的功能集中实现,然后在系统运行时,
    //又将这些集中实现的方面代码织入到现有的系统中    常用于  事务控制,日志功能,积分,权限功能

//AOP的内部实现原理:动态代理模式   
//BeanFactory也称为容器,是专门用于提供对象的  (Spring框架也提供有类似的Spring容器  ,在容器中织入方面代码(如声明式事务控制代码))
public class BeanFactory {

        public static AccountService getAccountService(){
            final AccountService as = new AccountServiceImpl();
            //引入动态代理,以便织入方面代码
            AccountService proxyAs = (AccountService)Proxy.newProxyInstance(as.getClass().getClassLoader(), as.getClass().getInterfaces(), 
                    new InvocationHandler() {
                        public Object invoke(Object proxy, Method method, Object[] args)
                                throws Throwable {
                            Object obj =null;
                            try {
                                TransactionManager.startTransaction();  //前置通知  spring框架的前置通知
                                 obj= method.invoke(as, args);
                                TransactionManager.commit();//后置通知
                            } catch (Exception e) {
                                e.printStackTrace();
                                TransactionManager.rollback();//异常通知
                            }
                            return obj;
                        }
                    });
            return proxyAs;
        }
}

C3P0Util.java

package com.itheima.utils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0Util {

    private static DataSource ds =  new ComboPooledDataSource("mysql");

    /**
     * 获取连接
     * @return
     * @throws Exception
     */
    public static Connection getConnection() throws Exception{
        return ds.getConnection();
    }

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

    /**
     * 关闭资源
     * 
     * @param rs
     * @param st
     * @param con
     */
    public static void release(ResultSet rs, Statement st, Connection con) {
        // 6.关闭资源
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            rs = null;
        }
        if (st != null) {
            try {
                st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            st = null;
        }
        if (con != null) {
            try {
                con.close();//不用担心了,肯定还回池中
            } catch (SQLException e) {
                e.printStackTrace();
            }
            con = null;//线程池中也要回收
        }

    }
}

TransactionManager.java

package com.itheima.utils;

import java.sql.Connection;
import java.sql.SQLException;

//用于实现事务管理类
public class TransactionManager {

    private static ThreadLocal<Connection> tL = new ThreadLocal<Connection>();

    //从池中取一个连接------------------>放入到ThreadLocal------>(实现当前线程内部共享同一个Connection)
    public static Connection getConnection(){
        Connection con = tL.get();//从线程局部变量中取一个连接
        if(con==null){
            //从池中取一个连接
            try {
                con = C3P0Util.getConnection();
                tL.set(con);//将一个连接放入线程局部变量中
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return con;
    }

    public static void startTransaction() throws Exception{
        Connection con = getConnection();
        con.setAutoCommit(false);
    }

    public static void commit() throws Exception{
        Connection con = getConnection();
        con.commit();
    }
    public static void rollback() throws Exception{
        Connection con = getConnection();
        con.rollback();
    }
}

c3p0-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day15</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </default-config>

    <named-config name="mysql">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day15</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </named-config>


    <named-config name="oracle">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day16</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </named-config>
</c3p0-config>

结果:


四、使用dbutils框架实现多表关联操作

现实例子:处理大项目中的数据,页面中一个版块中的所有帖子的增删查改。

一个客户可以下多个订单
一个订单只能让一个客户下
以上是一对多关系

在sql表中通过外键建立表间关系

但在java中实现不了外键的功能。

那么怎么解决?

在Customer里面加一个集合<order>以此来模拟外键功能
在Order里加上一个Customer类的属性。以此来关联上Customer的关系

代码中红姐下了两个订单

在TeacherDaoTest中使用添加customer时,会带上customer相对订单的信息。

如何实现以上说明法?

项目架构:


CustomerDaoImpl.java

package com.itheima.dao.impl;

import java.sql.SQLException;
import java.util.List;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import com.itheima.domain.Customer;
import com.itheima.domain.Orders;
import com.itheima.utils.C3P0Util;

public class CustomerDaoImpl {
   private QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
   public void addCustomer(Customer c){
       try {
         //添加一个顾客
        qr.update("insert into customer values(?,?)",c.getId(),c.getName());
        if(c.getOrders()!=null && c.getOrders().size()>0){
            //说明有订单
            List<Orders> orders = c.getOrders();//得到顾客的所有订单
            for(Orders o :orders){
                //最后一个参数代表是哪个客户下的订单,所以用c.getId()取顾客编号
                qr.update("insert into orders values(?,?,?,?)",o.getId(),o.getNum(),o.getMoney(),c.getId());
            }
        }
    } catch (SQLException e) {
        e.printStackTrace();
    }
   }

  public Customer findByCustomerId(int id,boolean isOrphan){
      try {
            Customer c = qr.query("select * from customer where id =?",new BeanHandler<Customer>(Customer.class), id);

            if(!isOrphan){
                 //2.查询该客户是否有订单
                List<Orders> list = qr.query("select * from orders where customer_id=?",new BeanListHandler<Orders>(Orders.class), id);
                if(list!=null && list.size()>0){
                    c.setOrders(list);//设置该客户所下的所有订单
                }
            }
            return c;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
  }
}

Customer.java

package com.itheima.domain;

import java.util.ArrayList;
import java.util.List;
/*
create table customer(
    id int primary key,
    name varchar(100)
);
create table orders(
    id int primary key,
    num varchar(100),
    money float(8,0),
    customer_id int,
    constraint customer_id_fk foreign key(customer_id) references customer(id)
);

 */
public class Customer {
    private int id;
    private String name;
    private List<Orders> orders = new ArrayList<Orders>();
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public List<Orders> getOrders() {
        return orders;
    }
    public void setOrders(List<Orders> orders) {
        this.orders = orders;
    }
    @Override
    public String toString() {
        return "Customer [id=" + id + ", name=" + name + "]";
    }

}

Order.java

package com.itheima.domain;

public class Orders {
    private int id;
    private String num;
    private float money;
    private Customer customer;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getNum() {
        return num;
    }
    public void setNum(String num) {
        this.num = num;
    }
    public float getMoney() {
        return money;
    }
    public void setMoney(float money) {
        this.money = money;
    }
    public Customer getCustomer() {
        return customer;
    }
    public void setCustomer(Customer customer) {
        this.customer = customer;
    }
    @Override
    public String toString() {
        return "Orders [id=" + id + ", num=" + num + ", money=" + money + "]";
    }

}

TeacherDaoTest.java

package com.itheima.test;

import java.util.List;

import org.junit.Test;

import com.itheima.dao.impl.CustomerDaoImpl;
import com.itheima.domain.Customer;
import com.itheima.domain.Orders;

public class TeacherDaoTest {
    private CustomerDaoImpl dao = new CustomerDaoImpl();
    @Test
    public void testAdd(){
        Customer c = new Customer();
        c.setId(1);
        c.setName("红姐");

        Orders o1 = new Orders();
        o1.setId(1);
        o1.setNum("201401");
        o1.setMoney(1000);

        //建立关系
        c.getOrders().add(o1);

        Orders o2 = new Orders();
        o2.setId(2);
        o2.setNum("201402");
        o2.setMoney(2000);

        //建立关系
        c.getOrders().add(o2);

        dao.addCustomer(c);
    }
    @Test
    public void testQuery(){
        try {
            Customer c = dao.findByCustomerId(1,true);//级联加载
            System.out.println("客户信息:"+c);
            List<Orders> os = c.getOrders();
            System.out.println("该客户下的订单:");
            for(Orders o:os)
                System.out.println(o);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

C3P0Util.java

package com.itheima.utils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0Util {

    private static DataSource ds =  new ComboPooledDataSource("mysql");

    /**
     * 获取连接
     * @return
     * @throws Exception
     */
    public static Connection getConnection() throws Exception{
        return ds.getConnection();
    }

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

    /**
     * 关闭资源
     * 
     * @param rs
     * @param st
     * @param con
     */
    public static void release(ResultSet rs, Statement st, Connection con) {
        // 6.关闭资源
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            rs = null;
        }
        if (st != null) {
            try {
                st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            st = null;
        }
        if (con != null) {
            try {
                con.close();//不用担心了,肯定还回池中
            } catch (SQLException e) {
                e.printStackTrace();
            }
            con = null;//线程池中也要回收
        }

    }
}

c3p0-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day15</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </default-config>

    <named-config name="mysql">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day15</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </named-config>


    <named-config name="oracle">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day16</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </named-config>
</c3p0-config>

结果:


TeacherDaoImpl.java

package com.itheima.dao.impl;

import java.sql.SQLException;
import java.util.List;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import com.itheima.domain.Customer;
import com.itheima.domain.Orders;
import com.itheima.domain.Student;
import com.itheima.domain.Teacher;
import com.itheima.utils.C3P0Util;

public class TeacherDaoImpl {
     private QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());

    public void addTeacher(Teacher t){
        try {
            qr.update("insert into teacher values(?,?,?)",t.getId(),t.getName(),t.getMoney());
            //2.先得到当前这个老师所带的所有学生
            List<Student> list = t.getStudents();
            for(Student stu :list){
                //3.判断该学生在数据库是否已存在,如果不存在,则向学生表中添加一个学生信息
                Student s  = qr.query("select * from student where id=?",new BeanHandler<Student>(Student.class), stu.getId());//查询这个学生在student表中是否存在
                if(s==null){
                    //说明这个学生在student表中没有
                    qr.update("insert into student values(?,?,?)",stu.getId(),stu.getName(),stu.getGrade());
                }

                //4.中间表中一定要添加老师和学生的关系
                qr.update("insert into teacher_student values(?,?)",t.getId(),stu.getId());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Teacher findTeacherById(int id){
        try {
            Teacher t = qr.query("select * from teacher where id =?",new BeanHandler<Teacher>(Teacher.class), id);
                 //2.查询该客户是否有订单
                List<Student> list = qr.query("select * from student where id in (select s_id from teacher_student where t_id=?)",new BeanListHandler<Student>(Student.class), id);
                if(list!=null && list.size()>0){
                    t.setStudents(list);
                }
            return t;
           } catch (SQLException e) {
            throw new RuntimeException(e);
           }
    }
}

Student.java

package com.itheima.domain;

import java.util.ArrayList;
import java.util.List;

public class Student {
    private int id;
    private String name;
    private String grade;
    private List<Teacher> teachers = new ArrayList<Teacher>();
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getGrade() {
        return grade;
    }
    public void setGrade(String grade) {
        this.grade = grade;
    }
    public List<Teacher> getTeachers() {
        return teachers;
    }
    public void setTeachers(List<Teacher> teachers) {
        this.teachers = teachers;
    }
    @Override
    public String toString() {
        return "Student [id=" + id + ", name=" + name + ", grade=" + grade
                + "]";
    }

}

Teacher.java

package com.itheima.domain;

import java.util.ArrayList;
import java.util.List;
/*
create table teacher(
    id int primary key,
    name varchar(100),
    money float(8,2)
);
create table student(
    id int primary key,
    name varchar(100),
    grade varchar(100)
);
create table teacher_student(
    t_id int,
    s_id int,
    primary key(t_id,s_id),
    constraint t_id_fk foreign key(t_id) references teacher(id),
    constraint s_id_fk foreign key(s_id) references student(id)
);
 */
public class Teacher {
    private int id;// Integer
    private String name;
    private float money;
    private List<Student> students = new ArrayList<Student>();
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public float getMoney() {
        return money;
    }
    public void setMoney(float money) {
        this.money = money;
    }
    public List<Student> getStudents() {
        return students;
    }
    public void setStudents(List<Student> students) {
        this.students = students;
    }
    @Override
    public String toString() {
        return "Teacher [id=" + id + ", name=" + name + ", money=" + money
                + "]";
    }

}

TeacherDaoImplTest.java

package com.itheima.test;

import org.junit.Test;

import com.itheima.dao.impl.TeacherDaoImpl;
import com.itheima.domain.Student;
import com.itheima.domain.Teacher;

public class TeacherDaoImplTest {
    private TeacherDaoImpl dao = new TeacherDaoImpl();
    @Test
    public void addTest(){
        Teacher t1= new Teacher();
        t1.setId(1);
        t1.setName("NB");
        t1.setMoney(10000);

        Teacher t2= new Teacher();
        t2.setId(2);
        t2.setName("WYJ");
        t2.setMoney(10000);

        Student s1 = new Student();
        s1.setId(1);
        s1.setName("AJ");
        s1.setGrade("A");

        Student s2 = new Student();
        s2.setId(2);
        s2.setName("CJ");
        s2.setGrade("A");

        //建立关系
        t1.getStudents().add(s1);
        t1.getStudents().add(s2);

        t2.getStudents().add(s1);
        t2.getStudents().add(s2);

        dao.addTeacher(t1);
        dao.addTeacher(t2);

    }
    @Test
    public void testQuery(){
        Teacher t = dao.findTeacherById(2);
        System.out.println(t);
        System.out.println("他教过的学员:");
        for(Student s:t.getStudents())
            System.out.println(s);
    }
}

结果:



资料下载

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值