JDBC--自定义JDBC框架+Apache—DBUtils框架+事务管理+操作多表
本文查阅方法:
??? 1、查阅目录 —— 查阅本文目录,确定想要查阅的目录标题
??? 2、快捷“查找” —— 在当前浏览器页面,按键 “Ctrl+F” 按键组合,开启浏览器的查找功能,
???????????? 在查找搜索框中 输入需要查阅的 目录标题,便可以直接到达 标题内容 的位置。
??? 3、学习小结 —— 文中的学习小结内容,是笔者在学习之后总结出的,开发时可直接参考其进行应用开发的内容, 进一步加快了本文的查阅 速度。(水平有限,仅供参考。)
?
?
?
?
?
本文目录
?
??????学习小结
?
??????1、自定义JDBC框架?——数据库元数据:DataBaseMetaData
?
??????2、自定义JDBC框架?——数据库元数据:DataBaseMetaData
?
??????3、自定义JDBC框架?——结果集元数据:?ResultSetMetaData
?
??????4、使用元数据简化JDBC代码
?
????????????(1)?万能更新
?
????????????(2)?万能查询
?
??????5、Apache—DBUtils框架简介
?
??????6、DbUtils类?介绍
?
??????7、QueryRunner类?介绍
?
??????8、QueryRunner类的主要方法
?
??????9、ResultSetHandler接口?介绍
?
??????10、ResultSetHandler?接口的实现类
?
??????11、JDBC应用的事务管理(ThreadLocal类)?
?
??????12、JDBC应用的事务管理——采用跨层跨层传递方法参数
?
??????13、JDBC应用的事务管理——?ThreadLocal?绑定连接
?
??????14、使用JDBC操作多个表
?
??????15、使用JDBC操作多个表——?“一对多”关系?
?
??????16、使用JDBC操作多个表——?多对多关系
?
??????17、数据库端——表关系间的级联操作
?
?
?
相关学习
?
JDBC?学习笔记(一)——?基础知识?+?分页技术
??????链接地址:http://even2012.iteye.com/blog/1886946
?
JDBC?学习笔记(二)——?大数据+存储过程+批处理+事务
??????链接地址:http://even2012.iteye.com/blog/1886950
?
JDBC?学习笔记(三)——?数据源(数据库连接池):DBCP数据源、C3P0?数据源以及自定义数据源技术
??????链接地址:http://even2012.iteye.com/blog/1886953
?
JDBC?学习笔记(四)——?自定义JDBC框架+Apache—DBUtils框架+事务管理+操作多表??
??????链接地址:http://even2012.iteye.com/blog/1886956
?
?
?学习小结
?
?
?
?
?
?
?
?
?
?
?
?
1、自定义JDBC框架?——数据库元数据:DataBaseMetaData
?
元数据:数据库、表、列的定义信息。
?
DataBaseMetaData?? connection.getDatabaseMetaData()????
?
获得代表DataBaseMetaData 对象元数据的DataBaseMetaData 对象。
?
????DataBaseMetaData对象中的方法:
?
????????(1) getURL():返回一个String类对象,代表数据库的URL。
?
????????(2) getUserName():返回连接当前数据库管理系统的用户名。
?
????????(3) getDatabaseProductName():返回数据库的产品名称。
?
????????(4) getDatabaseProductVersion():返回数据库的版本号。
?
????????(5) getDriverName():返回驱动驱动程序的名称。
?
????????(6) getDriverVersion():返回驱动程序的版本号。
?
????????(7) isReadOnly():返回一个boolean值,指示数据库是否只允许读操作。
?
?
?
Demo样例:?获取数据库的元数据?
?
??public?void?test1()?throws?SQLException{
?
????????Connection?conn?=?JdbcUtils_C3P0.getConnection();
?
????????DatabaseMetaData?meta?=?conn.getMetaData();
?
????????System.out.println(meta.getDatabaseProductName());
?
????????System.out.println(meta.getDatabaseMajorVersion());
?
????????System.out.println(meta.getDatabaseMinorVersion());
?
??}?
?
?
?
?
?
?
2、自定义JDBC框架?——数据库元数据:DataBaseMetaData
?
ParameterMetaData?? PreparedStatement . getParameterMetaData()
?
获得代表PreparedStatement元数据的ParameterMetaData对象。
?
例如:SQL语句 “ Select * from user where name=? And password=?? ” 中的两个“ ?” 问号。
?
ParameterMetaData对象 中的方法:
?
????????(1) getParameterCount()????--获得指定参数的个数
?
????????(2) getParameterType(int param)????--?获得指定参数的sql类型(Mysql数据库不支持该方法,会报异常。)
?
?
?
Demo样例:参数元数据
?
??????public?void?test2()?throws?SQLException{
?
????????????Connection?conn?=?JdbcUtils_C3P0.getConnection();
?
????????????String?sql?=?"insert?into?user(id,name)?values(?,?)";
?
?????????
?
????????????PreparedStatement?st?=?conn.prepareStatement(sql);
?
????????????ParameterMetaData?meta?=?st.getParameterMetaData();
?
?????????
?
????????????System.out.println(meta.getParameterCount());
?
????????????System.out.println(meta.getParameterType(1));?
?
??????}
?
?
?
?
?
?
3、自定义JDBC框架?——结果集元数据: ResultSetMetaData
?
ResultSetMetaData?? ResultSet. getMetaData()
?
获得代表ResultSet对象元数据的ResultSetMetaData对象。
?
????ResultSetMetaData对象中的方法
?
????????????(1) getColumnCount()????--?返回resultset对象的列数
?
????????????(2) getColumnName(int column)????--?获得指定列的名称
?
????????????(3) getColumnTypeName(int column)????--?获得指定列的类型
?
?
?
Demo样例: 结果集元数据
?
??????public?void?test3()?throws?SQLException{
?
????????????Connection?conn?=?JdbcUtils_C3P0.getConnection();
?
????????????String?sql?=?"select?*?from?account";
?
????????????PreparedStatement?st?=?conn.prepareStatement(sql);
?
????????????ResultSet?rs?=?st.executeQuery();
?
?????????
?
????????????ResultSetMetaData??meta?=?rs.getMetaData();
?
????????????System.out.println(meta.getColumnCount());
?
????????????System.out.println(meta.getColumnName(1));
?
????????????System.out.println(meta.getColumnName(2));
?
????????????System.out.println(meta.getColumnName(3));????
?
??????}
?
?
?
?
?
??
?
4、使用元数据简化JDBC代码
?
业务背景:系统中所有实体对象都涉及到基本的CRUD操作:
?
????????(1) 万能更新
?
????????所有实体的CUD操作代码基本相同,仅仅发送给数据库的SQL语句不同而已,因此可以把CUD操作的所有相同代码抽取到工具类的一个update方法中,并定义参数接收变化的SQL语句。
?
?
?
Demo样例1:万能更新的方法内容部分
?
????public?static?void?update(String?sql,Object?params[])?throws?SQLException{
?
????????Connection?conn?=?null;
?
????????PreparedStatement?st?=?null;
?
????????ResultSet?rs?=?null;
?
????????try{
?
??????????????conn?=?getConnection();
?
??????????????st?=?conn.prepareStatement(sql);
?
??????????????for(int?i=0;i<params.length;i++){
?
????????????????????st.setObject(i+1,params[i]);
?
??????????????}
?
??????????????st.executeUpdate();
?
????????}finally{
?
??????????????release(conn,?st,?rs);
?
????????}
?
??}
?
?
?
Demo样例2:万能更新方法的调用代码
?
????public?class?CustomerDaoImpl?implements?CustomerDao?{?
?
??????????public?void?add(Customer?c){
?
????????????????try{
?
??????????????????????String?sql?=?"insert?into?customer(id,name,gender,birthday,cellphone,email,preference,type,description)?values(?,?,?,?,?,?,?,?,?)";
?
??????????????????????Object?params[]?=?{c.getId(),c.getName(),c.getGender(),c.getBirthday(),c.getCellphone(),c.getEmail(),c.getPreference(),c.getType(),c.getDescription()};
?
??????????????????????JdbcUtils.update(sql,?params);
?
????????????????}catch?(Exception?e)?{
?
??????????????????????throw?new?DaoException(e);
?
????????????????}
?
??????????}
?
?????
?
??????????public?void?update(Customer?c){???//id
?
????????????????try{
?
??????????????????????String?sql?=?"update?customer?set?name=?,gender=?,birthday=?,cellphone=?,email=?,preference=?,type=?,description=???where?id=?";
?
??????????????????????Object?params[]?=?{c.getName(),c.getGender(),c.getBirthday(),c.getCellphone(),c.getEmail(),c.getPreference(),c.getType(),c.getDescription(),c.getId()};
?
??????????????????????JdbcUtils.update(sql,?params);
?
????????????????}catch?(Exception?e)?{
?
??????????????????????throw?new?DaoException(e);
?
????????????????}?
?
??????????}
?
?????
?
??????????public?void?delete(String?id){
?
????????????????try{
?
??????????????????????String?sql?=?"delete?from?customer?where?id=?";
?
??????????????????????Object?params[]?=?{id};
?
??????????????????????JdbcUtils.update(sql,?params);
?
????????????????}catch?(Exception?e)?{
?
??????????????????????throw?new?DaoException(e);
?
????????????????}?
?
??????????}?
?
????}
?
??
?
????????(2)?万能查询
?
????????实体的R操作,除SQL语句不同之外,根据操作的实体不同,对ResultSet的映射也各不相同,因此可义一个query方法,除以参数形式接收变化的SQL语句外,可以使用策略模式由qurey方法的调用者决定如何把ResultSet中的数据映射到实体对象中。
?
?????????备注:关于自定义万能查询的代码,涉及到自定义处理器等代码,上面所述的数据库元数据的各种知识也都应用到其中,故有些复杂,不便学习。有万能查询需求的请学习Apache—DBUtils框架 中的查询方法部分,相对来说只要会调用即可,学习成本会小一些。
?
?
?
?
?
?
?
5、Apache—DBUtils框架简介
?
????????commons-dbutils?是?Apache?组织提供的一个开源?JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。因此dbutils成为很多不喜欢hibernate的公司的首选。
?
????????工具类:????org.apache.commons.dbutils.DbUtils。???
?
?
?
????????API介绍:
?
???????????(1) ?org.apache.commons.dbutils.QueryRunner?
?
???????????(2)? org.apache.commons.dbutils.ResultSetHandler
?
?????????
?
?
?
?
6、DbUtils类?介绍
?
????????DbUtils?:提供如关闭连接、装载JDBC驱动程序等常规工作的工具类,里面的所有方法都是静态的。主要方法如下:
?
????????(1) public?static?void?close(…)?throws?java.sql.SQLException: DbUtils类提供了三个重载的关闭方法。这些方法检查所提供的参数是不是NULL,如果不是的话,它们就关闭Connection、Statement和ResultSet。
?
????????(2) public?static?void?closeQuietly(…):?这一类方法不仅能在Connection、Statement和ResultSet为NULL情况下避免关闭,还能隐藏一些在程序中抛出的SQLException。
?
????????(3) public?static?void?commitAndCloseQuietly(Connection?conn):?用来提交连接,然后关闭连接,并且在关闭连接时不抛出SQL异常。?
?
????????(4) public?static?boolean?loadDriver(java.lang.String?driverClassName):这一方装载并注册JDBC驱动程序,如果成功就返回true。使用该方法,你不需要捕捉这个异常ClassNotFoundException。
?
?
?
?
?
?
7、QueryRunner类 介绍
?
????????该类简单化了SQL查询,它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量。
?
????????QueryRunner类提供了两个构造方法:
?
????????????(1) 默认的构造方法:
?
????????????????????QueryRunner()?
?
????????????(2) 需要一个?javax.sql.DataSource?来作参数的构造方法。
?
????????????????????QueryRunner(DataSource?ds)?
?
?
?
?
?
?
8、QueryRunner类的主要方法
?
????????(1) public?Object?query(Connection?conn,?String?sql,?Object[]?params,?ResultSetHandler?rsh)?throws?SQLException:执行一个查询操作,在这个查询中,对象数组中的每个元素值被用来作为查询语句的置换参数。该方法会自行处理?PreparedStatement?和?ResultSet?的创建和关闭。
?
????????(2) public?Object?query(String?sql,?Object[]?params,?ResultSetHandler?rsh)?throws?SQLException: 几乎与第一种方法一样;唯一的不同在于它不将数据库连接提供给方法,并且它是从提供给构造方法的数据源(DataSource)?或使用的setDataSource?方法中重新获得?Connection。?????????
?
????????(3) public?Object?query(Connection?conn,?String?sql,?ResultSetHandler?rsh)?throws?SQLException?:?执行一个不需要置换参数的查询操作。?????????
?
????????(4) public?int?update(Connection?conn,?String?sql,?Object[]?params)?throws?SQLException:用来执行一个更新(插入、更新或删除)操作。????????
?
????????(5) public?int?update(Connection?conn,?String?sql)?throws?SQLException:用来执行一个不需要置换参数的更新操作。
?
?
?
Demo样例:使用dbutils完成数据库的crud?
?
public?class?Demo1?{?
?
??/*
?
???create?database?day17;
?
???use?day17;
?
???create?table?users(
?
????id?int?primary?key,
?
????name?varchar(40),
?
????password?varchar(40),
?
????email?varchar(60),
?
????birthday?date
?
??);
?
???*/?
?
??@Test
?
??public?void?insert()?throws?SQLException{
?
????QueryRunner?runner?=?new?QueryRunner(JdbcUtils.getDataSource());
?
????String?sql?=?"insert?into?users(id,name,password,email,birthday)?values(?,?,?,?,?)";
?
????Object?params[]?=?{2,"bbb","123","aa@sina.com",new?Date()};
?
????runner.update(sql,?params);
?
??}
?
?
?
??@Test
?
??public?void?update()?throws?SQLException{
?
????QueryRunner?runner?=?new?QueryRunner(JdbcUtils.getDataSource());
?
????String?sql?=?"update?users?set?email=??where?id=?";
?
????Object?params[]?=?{"aaaaaa@sina.com",1};
?
????runner.update(sql,?params);
?
??}
?
?
?
??@Test
?
??public?void?delete()?throws?SQLException{
?
????QueryRunner?runner?=?new?QueryRunner(JdbcUtils.getDataSource());
?
????String?sql?=?"delete?from?users?where?id=?";
?
????runner.update(sql,?1);
?
??}
?
?
?
??@Test
?
??public?void?find()?throws?SQLException{
?
????QueryRunner?runner?=?new?QueryRunner(JdbcUtils.getDataSource());
?
????String?sql?=?"select?*?from?users?where?id=?";
?
????User?user?=?(User)?runner.query(sql,?1,?new?BeanHandler(User.class));
?
????System.out.println(user.getEmail());
?
??}
?
?
?
?
?
??@Test
?
??public?void?getAll()?throws?Exception{
?
????QueryRunner?runner?=?new?QueryRunner(JdbcUtils.getDataSource());
?
????String?sql?=?"select?*?from?users";
?
????List?list?=?(List)?runner.query(sql,?new?BeanListHandler(User.class));
?
????System.out.println(list);
?
??}
?
?
?
??@Test
?
??public?void?batch()?throws?SQLException{
?
????QueryRunner?runner?=?new?QueryRunner(JdbcUtils.getDataSource());
?
????String?sql?=??"insert?into?users(id,name,password,email,birthday)?values(?,?,?,?,?)";
?
????Object?params[][]?=?new?Object[3][5];
?
????for(int?i=0;i<params.length;i++){??//3
?
??????params[i]?=?new?Object[]{i+1,"aa"+i,"123",i?+?"@sina.com",new?Date()};
?
????}
?
????runner.batch(sql,?params);
?
??}
?
}
?
?
?
?
?
??
?
9、ResultSetHandler接口?介绍
?
????????该接口用于处理?java.sql.ResultSet,将数据按要求转换为另一种形式。
?
????????ResultSetHandler?接口提供了一个单独的方法:Object?handle?(java.sql.ResultSet?.rs)。
?
????????
?
?
?
?
10、ResultSetHandler?接口的实现类
?
??????? (1)?ArrayHandler( ):把结果集中的第一行数据转成对象数组。
?
????????(2) ArrayListHandler( ):把结果集中的每一行数据都转成一个数组,再存放到List中。
?
????????(3) BeanHandler(Class?type)?:将结果集中的第一行数据封装到一个对应的JavaBean实例中。
?
????????(4) BeanListHandler(Class?type)?:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
?
????????(5) ColumnListHandler(int?columnIndex?/?String?columnName):将结果集中某一列的数据存放到List中。
?
?????? ?(6)?KeyedHandler( int?columnIndex?/?String?columnName ):将结果集中的每一行数据都封装到一个Map里,再把这些map再存到一个map里,并将其columnName的值作为指定的key。
?
??????? (7)?MapHandler( ):将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
?
????????(8) MapListHandler( ):将结果集中的每一行数据都封装到一个Map里,然后再存放到List
?
?????? ?(9)??ScalarHandler( ):将结果集中的某一列?装入到一个对象中。
?
?
?
Demo样例:测试dbutils的各个结果集处理器
?
public?class?Demo2?{???
?
??@Test
?
??public?void?test1()?throws?SQLException{
?
????QueryRunner?runner?=?new?QueryRunner(JdbcUtils.getDataSource());
?
????String?sql?=?"select?*?from?users?where?id=?";
?
????Object?result[]?=?(Object[])?runner.query(sql,1,?new?ArrayHandler());
?
????System.out.println(result[0]);
?
????System.out.println(result[1]);
?
??}??
?
?
?
??@Test
?
??public?void?test2()?throws?SQLException{
?
????QueryRunner?runner?=?new?QueryRunner(JdbcUtils.getDataSource());
?
????String?sql?=?"select?*?from?users";
?
????List?list?=?(List)?runner.query(sql,?new?ArrayListHandler());
?
????System.out.println(list);
?
??}
?
?
?
??@Test
?
??public?void?test3()?throws?SQLException{
?
????QueryRunner?runner?=?new?QueryRunner(JdbcUtils.getDataSource());
?
????String?sql?=?"select?*?from?users";
?
????List?list?=?(List)?runner.query(sql,?new?ColumnListHandler1("name"));
?
????System.out.println(list);
?
??}??
?
?
?
??@Test
?
??public?void?test4()?throws?SQLException{
?
????QueryRunner?runner?=?new?QueryRunner(JdbcUtils.getDataSource());
?
????String?sql?=?"select?*?from?users";
?
????Map<Integer,Map<String,Object>>?map?=?(Map)?runner.query(sql,?new?KeyedHandler("id"));
?
????for(Map.Entry<Integer,Map<String,Object>>?me?:?map.entrySet()){
?
??????int?id?=?me.getKey();
?
??????for(Map.Entry<String,?Object>?entry?:?me.getValue().entrySet()){
?
????????String?name?=?entry.getKey();
?
????????Object?value?=?entry.getValue();
?
????????System.out.println(name?+?"="?+?value);
?
??????}
?
????}
?
??}
?
?
?
??@Test??//获取总记录数。
?
??public?void?test5()?throws?SQLException{
?
????QueryRunner?runner?=?new?QueryRunner(JdbcUtils.getDataSource());
?
????String?sql?=?"select?count(*)?from?users";
?
????/*?方式一:
?
????Object?result[]?=?(Object[])?runner.query(sql,?new?ArrayHandler());
?
????long?totalrecord?=?(Long)result[0];
?
????int?num?=?(int)totalrecord;
?
????System.out.println(num);
?
????int?totalrecord?=?((Long)result[0]).intValue();
?
????*/
?
?
?
????//方式二:
?
????int?totalrecord?=?((Long)runner.query(sql,?new?ScalarHandler(1))).intValue();
?
????System.out.println(totalrecord);
?
??}
?
}
?
?
?
//自定义
?
class?ColumnListHandler1?implements?ResultSetHandler{?
?
??private?String?columnName;
?
??public?ColumnListHandler1(String?columnName){
?
????this.columnName?=?columnName;
?
??}
?
??public?Object?handle(ResultSet?rs)?throws?SQLException?{
?
????List?list?=?new?ArrayList();
?
????while(rs.next()){
?
??????list.add(rs.getObject(columnName));
?
????}
?
????return?list;
?
??}
?
}
?
?
?
?
??
?
11、JDBC应用的事务管理(ThreadLocal类)?
?
JDBC?应用的事务管理——Service层和Dao层事务的传递。
?
????方式一:跨层传递方法参数——在Service层创建开启事务的连接,并传递到Dao层,最后在Service层提交事务;
?
????方式二:ThreadLocal 绑定连接——使用ThreadLocal进行事务管理——ThreadLocal可以实现在线程范围内实现数据共享。
?
?????方式三:使用Spring进行事务管理;(在Spring 博文中讲解。)
?
?????
?
????????????????????
?
?
?
?
?
?
12、JDBC应用的事务管理——采用跨层跨层传递方法参数
?
思想:在Service层创建开启事务的连接,并传递到Dao层,最后在Service层提交事务;
?
?
?
Demo样例1:Service层(Dao层中只要在方法中参数中接收该 连接参数 就好了)
?
public?class?BusinessService?{??
?
??/*??
?
??create?table?account(
?
????id?int?primary?key?auto_increment,
?
????name?varchar(40),
?
????money?float
?
??)character?set?utf8?collate?utf8_general_ci;
?
?
?
??insert?into?account(name,money)?values('aaa',1000);
?
??insert?into?account(name,money)?values('bbb',1000);
?
??insert?into?account(name,money)?values('ccc',1000);?
?
??*/??
?
?
?
??public?void?transfer1(int?sourceid,int?targetid,double?money)?throws?SQLException{
?
?
?
????Connection?conn?=?null;
?
????try{
?
??????//?获取连接并开启事务。
?
??????conn?=?JdbcUtils.getConnection();
?
??????conn.setAutoCommit(false);
?
?
?
??????//?将开启事务的连接传递到各层。
?
??????AccountDao?dao?=?new?AccountDao(conn);
?
?
?
??????Account?a?=?dao.find(sourceid);???//select
?
??????Account?b?=?dao.find(targetid);???//select
?
?
?
??????a.setMoney(a.getMoney()-money);??
?
??????b.setMoney(b.getMoney()+money);???
?
?
?
??????dao.update(a);?//update??????
?
??????dao.update(b);//update
?
?
?
??????//?提交事务。
?
??????conn.commit();
?
????}finally{
?
??????//?关闭连接。
?
??????if(conn!=null)?conn.close();
?
????}
?
??}
?
}
?
?
?
?
?
?
?
?
?
?
?
13、JDBC应用的事务管理—— ThreadLocal 绑定连接
?
思想:在Service层将开启事务的连接绑定到ThreadLocal中,在当前线程所途径的其他各层从ThreadLocal中获取连接并进行操作,最后线程返回至Service层时,再提交事务,移除绑定的链接。
?
?
?
?
Demo样例1:将 使用ThreadLocal 绑定连接 的代码封装成工具类。
?
public?class?JdbcUtils?{
?
??private?static?DataSource?ds;
?
?
?
??//?为保证各层的类所使用的ThreadLocal是同一个,建议将其设定成静态的,但是一定要记得使用后要移出绑定在上面的对象。
?
??private?static?ThreadLocal<Connection>?tl?=?new?ThreadLocal<Connection>();??// 其实就是一个Map集合
?
?
?
??static{
?
????try{
?
??????Properties?prop?=?new?Properties();
?
??????InputStream?in?=?JdbcUtils.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
?
??????prop.load(in);
?
??????BasicDataSourceFactory?factory?=?new?BasicDataSourceFactory();
?
??????ds?=?factory.createDataSource(prop);
?
????}catch?(Exception?e)?{
?
??????throw?new?ExceptionInInitializerError(e);
?
????}
?
??}
?
?
?
??public?static?DataSource?getDataSource(){
?
????return?ds;
?
??}
?
?
?
??//?备注:该获取连接的方法,仅当使用ThreadLocal来管理事务连接的情况,因为向静态对象ThreadLocal中绑定了对象,所以当我们不需要管理事务的普通获取连接的方法,就不要用此方法。应该用普通的获取连接的方法。
?
??public?static?Connection?getConnection()?throws?SQLException{
?
????try{
?
??????//得到当前线程上绑定的连接
?
??????Connection?conn?=?tl.get();
?
??????if(conn==null){??//代表线程上没有绑定连接
?
????????conn?=?ds.getConnection();
?
????????tl.set(conn);
?
??????}
?
??????return?conn;
?
????}catch?(Exception?e)?{
?
??????throw?new?RuntimeException(e);
?
????}
?
??}?
?
?
?
??public?static?void?startTransaction(){
?
????try{
?
??????//得到当前线程上绑定连接开启事务
?
??????Connection?conn?=?tl.get();
?
??????if(conn==null){??//代表线程上没有绑定连接
?
????????conn?=?ds.getConnection();
?
????????tl.set(conn);
?
??????}
?
??????conn.setAutoCommit(false);
?
????}catch?(Exception?e)?{
?
??????throw?new?RuntimeException(e);
?
????}
?
??}?
?
?
?
??public?static?void?commitTransaction(){
?
????try{
?
??????Connection?conn?=?tl.get();
?
??????if(conn!=null){
?
????????conn.commit();
?
??????}
?
????}catch?(Exception?e)?{
?
??????throw?new?RuntimeException(e);
?
????}
?
??}
?
?
?
??public?static?void?closeConnection(){
?
????try{
?
??????Connection?conn?=?tl.get();
?
??????if(conn!=null){
?
????????conn.close();
?
??????}
?
????}catch?(Exception?e)?{
?
??????throw?new?RuntimeException(e);
?
????}finally{
?
??????tl.remove();???//千万注意,解除当前线程上绑定的链接(从threadlocal容器中移除对应当前线程的链接)
?
????}
?
??}
?
}
?
?
?
Demo样例2: 采用 ThreadLocal 绑定连接 来管理事务的 Service层的代码。
?
public?class?BusinessService?{??
?
??/*??
?
??create?table?account(
?
????id?int?primary?key?auto_increment,
?
????name?varchar(40),
?
????money?float
?
??)character?set?utf8?collate?utf8_general_ci;
?
?
?
??insert?into?account(name,money)?values('aaa',1000);
?
??insert?into?account(name,money)?values('bbb',1000);
?
??insert?into?account(name,money)?values('ccc',1000);?
?
??*/??
?
??//用上ThreadLocal的事务管理
?
??public?void?transfer2(int?sourceid,int?targetid,double?money)?throws?SQLException{
?
?
?
????try{
?
??????JdbcUtils.startTransaction();
?
??????AccountDao?dao?=?new?AccountDao();
?
??????Account?a?=?dao.find(sourceid);???//select
?
??????Account?b?=?dao.find(targetid);???//select
?
??????a.setMoney(a.getMoney()-money);??
?
??????b.setMoney(b.getMoney()+money);???
?
??????dao.update(a);?//update
?
??????dao.update(b);//update
?
??????JdbcUtils.commitTransaction();
?
????}finally{
?
??????JdbcUtils.closeConnection();
?
????}
?
??}
?
}
?
?
?
??
?
14、使用JDBC操作多个表
?
????????(1) 使用JDBC操作多表的步骤
?
????????????????(a)??明确对象的属性,及之间的关联关系。
?
????????????????(b)??明确表关系,?创建数据库及表;
?
????????????????(c)? 编码Dao层的代码(重点是增删改查时涉及到的级联操作。)
?
????????????????(d)? 编码Service层的代码(重点是 复杂对象? 的级联操作。)
?
????????
?
????????(2)? O-R Mapping 映射的注意事项
?
????????????????(a) 不管java的对象存在何种关系,反映到关系型数据库中,都是使用外键表示纪录(即对象)的关联关系。
?
????????????????(b) 设计java对象如涉及到多个对象相互引用,要尽量避免使用一对多,或多对多关系,而应使用多对一描述对象之间的关系(或使用延迟加载的方式)。以避免查询出了所有“一对多”中?“多”的数据,容易造成内存溢出
?
????????????????(c) 特殊情况下(比如订单--订单项)必须设计成“一对多”关系时,当“多”的一方数据较少时,可以使用级联查询,但若是“多”的一方数据量较大时,则建议使用?“分页方式”查询。?
?
????????
?
?????????(3) ?“一对多”多表关联关系的设计方法:
?
????????????????(a) 先将每张表各自的基本属性信息列好;
?
????????????????(b) 再将“一对多”?多的一方中设定外键列(并添加外键约束,以维护两表之间的关系)。
?
?????????
?
????????(4) 常用O-R?Mapping映射工具
?
????????????????(a) Hibernate
?
?????????????? ?(b)?Ibatis
?
????????????????(c) Commons?DbUtils(只是对JDBC简单封装)
?
?????????
?
?
?
?
?
?
?
15、使用JDBC操作多个表—— 一对多关系 (例如:部门和员工)
?

?
?
?
?
?
?
Demo样例1:Dao层的代码
?
public?class?DepartmentDao?{?
?
??/*
?
????多表设计原则
?
????1、现将各表各自的基本属性信息列好;
?
????2、再将“一对多”?多的一方中设定外键列(并添加外键约束,以维护两表之间的关系)。
?
?
?
????create?table?department
?
????(
?
??????id?varchar(40)?primary?key,
?
??????name?varchar(40)
?
????);?
?
????create?table?employee
?
????(
?
??????id?varchar(40)?primary?key,
?
??????name?varchar(40),
?
??????salary?double,
?
??????department_id?varchar(40),
?
??????constraint?department_id_FK?foreign?key(department_id)?references?department(id)
?
????);
?
?
?
????alter?table?employee?drop?foreign?key?department_id_FK;
?
????alter?table?employee?add?constraint?department_id_FK?foreign?key(department_id)?references?department(id)?on?delete?set?null;
?
?
?
????alter?table?employee?drop?foreign?key?department_id_FK;
?
????alter?table?employee?add?constraint?department_id_FK?foreign?key(department_id)?references?department(id)?on?delete?cascade;?
?
???*/
?
?
?
??public?void?add(Department?d)?throws?SQLException{?
?
????QueryRunner?runner?=?new?QueryRunner(JdbcUtils.getDataSource());
?
?
?
????//1.把department对象的数据插入到department表
?
????String?sql?=?"insert?into?department(id,name)?values(?,?)";
?
????Object?params[]?=?{d.getId(),d.getName()};
?
????runner.update(sql,?params);
?
?
?
????//2.把department对象中维护的所有员工插入到员工表
?
????Set<Employee>?set?=?d.getEmployees();
?
????for(Employee?e?:?set){
?
??????sql?=?"insert?into?employee(id,name,salary,department_id)?values(?,?,?,?)";
?
??????params?=?new?Object[]{e.getId(),e.getName(),e.getSalary(),d.getId()};
?
??????runner.update(sql,?params);
?
????}
?
?
?
????//3.更新员工表的外键列,说明员工的部门(本例中的ID可以实现给定,固就不需要进行更新外键列操作;但若是涉及到获取 自动生成主键 的案例时,则需要 涉及到更新外键操作)。
?
????
?
??}
?
?
?
?
?
??//该方法查询出了所有“一对多”中?“多”的数据,容易造成内存溢出。当“多”的一方数据较少时,可以使用该级联查询,但若是“多”的一方数据量较大时,则建议使用?“分页方式”查询。
?
??public?Department?find(String?id)?throws?SQLException{
?
?
?
????QueryRunner?runner?=?new?QueryRunner(JdbcUtils.getDataSource());
?
?
?
????//1.找部门表,查出部门的基本信息
?
????String?sql?=?"select?*?from?department?where?id=?";
?
????Department?d?=?(Department)?runner.query(sql,?id,?new?BeanHandler(Department.class));
?
?
?
????//2.找员工表,找出部门下面所有员工
?
????sql?=?"select?*?from?employee?where?department_id=?";
?
????List?list?=?(List)?runner.query(sql,?id,?new?BeanListHandler(Employee.class));
?
?
?
????d.getEmployees().addAll(list);????// 注:set集合的addAll() 是将所有的值逐个取出来,再逐一存入到Set集合中;而set集合的add()方法则是替换一Set集合的引用。
?
?
?
????return?d;
?
??}
?
??//111
?
??public?void?delete(String?id)?throws?SQLException{
?
????QueryRunner?runner?=?new?QueryRunner(JdbcUtils.getDataSource());
?
????String?sql=?"delete?from?department?where?id=?";
?
????runner.update(sql,?id);
?
??}
?
}
?
?
?
Demo样例2:Service层的代码?
?
public?class?BService?{?
?
@Test
?
??public?void?add()?throws?SQLException{?
?
????Department?d?=?new?Department();
?
????d.setId("111");
?
????d.setName("开发部");
?
?
?
????Employee?e1?=?new?Employee();
?
????e1.setId("1");
?
????e1.setName("aa");
?
????e1.setSalary(10000);?
?
?
?
????Employee?e2?=?new?Employee();
?
????e2.setId("2");
?
????e2.setName("bb");
?
????e2.setSalary(10000);
?
??
?
????d.getEmployees().add(e1);
?
????d.getEmployees().add(e2);?
?
?
?
????DepartmentDao?dao?=?new?DepartmentDao();
?
????dao.add(d);
?
??}
?
?
?
??@Test
?
??public?void?find()?throws?SQLException{
?
????DepartmentDao?dao?=?new?DepartmentDao();
?
????Department?d?=?dao.find("111");
?
????System.out.println(d);
?
??}
?
?
?
??@Test
?
??public?void?delete()?throws?SQLException{
?
????DepartmentDao?dao?=?new?DepartmentDao();
?
????dao.delete("111");
?
??}
?
?}
?
?
?
?
?
?
16、使用JDBC操作多个表—— 多对多关系 (老师和学生)
?

?
?
?
?
Demo样例1:Dao层的代码
?
public?class?TeacherDao?{?
?
??/*
?
??create?table?teacher
?
??(
?
????id?varchar(40)?primary?key,
?
????name?varchar(40),
?
????salary?double
?
??)?;
?
?
?
??create?table?student
?
??(
?
????id?varchar(40)?primary?key,
?
????name?varchar(40)
?
??);
?
?
?
???create?table?teacher_student
?
???(
?
?????teacher_id?varchar(40),
?
?????student_id?varchar(40),
?
?????primary?key(teacher_id,student_id),
?
?????constraint?teacher_id_FK?foreign?key(teacher_id)?references?teacher(id),?
?
?????constraint?student_id_FK?foreign?key(student_id)?references?student(id)
?
???);
?
?
?
???alter?table?teacher_student?drop?foreign?key?teacher_id_FK;
?
???alter?table?teacher_student?add?constraint?teacher_id_FK?foreign?key(teacher_id)?references?teacher(id)?on?delete?cascade;??
?
?
?
???alter?table?teacher_student?drop?foreign?key?student_id_FK;
?
???alter?table?teacher_student?add?constraint?student_id_FK?foreign?key(student_id)?references?student(id)?on?delete?cascade;
?
??*/
?
?
?
??public?void?add(Teacher?t)?throws?SQLException?{
?
?
?
????QueryRunner?runner?=?new?QueryRunner(JdbcUtils.getDataSource());
?
?
?
????//1`.取出老师存老师表
?
????String?sql?=?"insert?into?teacher(id,name,salary)?values(?,?,?)";
?
????Object?params[]?=?{t.getId(),t.getName(),t.getSalary()};
?
????runner.update(sql,?params);
?
?
?
?
?
????//2.取出老师所有学生的数据,存学生表
?
????Set<Student>?set?=?t.getStudents();
?
????for(Student?s?:?set){
?
??????sql?=?"insert?into?student(id,name)?values(?,?)";
?
??????params?=?new?Object[]{s.getId(),s.getName()};
?
??????runner.update(sql,?params);
?
?
?
??????//3.更新中间表,说明老师和学生的关系
?
??????sql?=?"insert?into?teacher_student(teacher_id,student_id)?values(?,?)";
?
??????params?=?new?Object[]{t.getId(),s.getId()};
?
??????runner.update(sql,?params);
?
????}
?
??}
?
?
?
??public?Teacher?find(String?id)?throws?SQLException{
?
?
?
????QueryRunner?runner?=?new?QueryRunner(JdbcUtils.getDataSource());
?
?
?
????//1.找老师表,找出老师的基本信息
?
????String?sql?=?"select?*?from?teacher?where?id=?";
?
????Teacher?t?=?(Teacher)?runner.query(sql,?id,?new?BeanHandler(Teacher.class));
?
?
?
????//2.找出老师的所有学生????()
?
????//sql?=?"select?s.*?from?teacher_student?ts,student?s?where?ts.teacher_id=??and?ts.student_id=s.id";
?
????sql?=?"select?s.*?from?teacher_student?ts,student?s?where?ts.teacher_id=??and?ts.student_id=s.id";
?
????List?list?=?(List)?runner.query(sql,?id,?new?BeanListHandler(Student.class));
?
?
?
?
?
????t.getStudents().addAll(list);
?
????return?t;
?
??}
?
?
?
??public?void?delete(String?id){
?
????QueryRunner?runner?=?new?QueryRunner(JdbcUtils.getDataSource());
?
????String?sql?=?"delete?from?teacher?where?id=?";
?
?
?
??}
?
}
?
?
?
Demo样例2:Service层的代码?
?
public?class?BService?{
?
@Test
?
?public?void?addTeacher()?throws?SQLException{
?
?
?
??Teacher?t?=?new?Teacher();
?
??t.setId("1");
?
??t.setName("老张");
?
??t.setSalary(100000);
?
?
?
??Student?s1?=?new?Student();
?
??s1.setId("1");
?
??s1.setName("aa");
?
?
?
??Student?s2?=?new?Student();
?
??s2.setId("2");
?
??s2.setName("bb");
?
?
?
??t.getStudents().add(s1);
?
??t.getStudents().add(s2);?
?
?
?
??TeacherDao?dao?=?new?TeacherDao();
?
??dao.add(t);
?
?}
?
?
?
?@Test
?
?public?void?findTeacher()?throws?SQLException{
?
??TeacherDao?dao?=?new?TeacherDao();
?
??Teacher?t?=?dao.find("1");
?
??System.out.println(t);
?
?}
?
?
?
}
?
?
?
?
?
??
?
17、数据库端——表关系间的级联操作
?
表关系间的级联操作:
?
????REFERENCES?tbl_name?[(index_col_name,...)]
?
???????????????[MATCH?FULL?|?MATCH?PARTIAL?|?MATCH?SIMPLE]
?
???????????????[ON?DELETE?reference_option]??(级联删除)??
?
???????????????[ON?UPDATE?reference_option]??(级联修改)
?
?
?
reference_option的可选值:
?
????RESTRICT?|?CASCADE(删除)?|?SET?NULL(置空)?|?NO?ACTION?
?
?
?
Demo:给表添加外键约束——包含级联删除(置空)的关系
?
?? alter?table?employee?add?constraint?department_id_FK?foreign?key(department_id)?references?department(id)?on?delete?set?null?;?
?
???alter?table?employee?add?constraint?department_id_FK?foreign?key(department_id)?references?department(id)?on?delete?set?null;
?
????alter?table?employee?add?constraint?department_id_FK?foreign?key(department_id)?references?department(id)?on?delete?cascade;?
?