Day17
Apache—DBUtils框架
简介


QueryRunner(DataSource ds) 这个,这个可以传递连接池对象

update(Connection conn, String sql, Object param) //这个是当构造函数没有传递连接池且只有一个参数,还有sql语句时用update(Connection conn, String sql, Object[] params) //这个是当构造函数没有传递连接池且有很多个参数,还有sql语句时用

batch(Connection conn, String sql, Object[][] params) 
<?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/day17</property> <property name="user">root</property> <property name="password">root</property> <property name="acquireIncrement">10</property> <property name="initialPoolSize">10</property> <property name="minPoolSize">5</property> <property name="maxPoolSize">20</property> </default-config><!-- This app is massive! --> <named-config name="mysql"> <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">10</property> <property name="initialPoolSize">10</property> <property name="minPoolSize">5</property> <property name="maxPoolSize">20</property><!-- intergalactoApp adopts a different approach to configuring statement caching --> </named-config> <named-config name="oracle"> <property name="acquireIncrement">50</property> <property name="initialPoolSize">100</property> <property name="minPoolSize">50</property> <property name="maxPoolSize">1000</property><!-- intergalactoApp adopts a different approach to configuring statement caching --> <property name="maxStatements">0</property> <property name="maxStatementsPerConnection">5</property> <!-- he's important, but there's only one of him --> <user-overrides user="master-of-the-universe"> <property name="acquireIncrement">1</property> <property name="initialPoolSize">1</property> <property name="minPoolSize">1</property> <property name="maxPoolSize">5</property> <property name="maxStatementsPerConnection">50</property> </user-overrides> </named-config> </c3p0-config>
然后我们将工具包中的配置文件修改成采用c3p0的
/day17/src/cn/itcast/utils/JdbcUtils.java
package cn.itcast.utils; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; import javax.sql.DataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; public class JdbcUtils { private static DataSource ds; private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>(); static{ try { //这里采用c3p0的连接池 ,这里没有指定用c3p0-config.xml中那个配置则默认采用缺省配置 ds = new ComboPooledDataSource(); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } //获取连接池 public static DataSource getDataSource(){ return ds; } public static void startTransaction() throws SQLException{ Connection conn = getConnection(); if(conn!=null){ conn.setAutoCommit(false); } } public static void commitTransaction() throws SQLException{ Connection conn = getConnection(); if(conn!=null){ conn.commit(); } } public static void rollbackTransaction() throws SQLException{ Connection conn = getConnection(); if(conn!=null){ conn.rollback(); } } public static void closeConnection() throws SQLException{ Connection conn = getConnection(); if(conn!=null){ try{ conn.close(); }finally{ tl.remove(); //关闭连接后,切记要记得移除Threadlocal中保存的连接 } } } //加载驱动,获取链接 public static Connection getConnection() throws SQLException{ Connection conn = tl.get(); //得到当前线程上绑定的连接 if(conn==null){ conn = ds.getConnection(); tl.set(conn); //把得到的连接绑定到当前线程 } return conn; } //释放资源 public static void release(Connection conn,Statement st,ResultSet rs){ try{ if(rs!=null) rs.close(); }catch (Exception e) { e.printStackTrace(); rs=null; } try{ if(st!=null) st.close(); }catch (Exception e) { e.printStackTrace(); st=null; } try{ //MyConnection if(conn!=null) conn.close(); }catch (Exception e) { e.printStackTrace(); conn=null; } } }
接着我们创建dao层,在创建dao层之前我们要将要操作的数据库表创建出来
create database day17 character set utf8 collate utf8_general_ci; use day17; create table users( id int primary key, name varchar(40), password varchar(40), email varchar(60), birthday date );

package cn.itcast.demo; import java.sql.SQLException; import java.util.Date; 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 org.junit.Test; import cn.itcast.domain.User; import cn.itcast.utils.JdbcUtils; //使用dbutils完成数据库的crud /* create database day17; use day17; create table users( id int primary key, name varchar(40), password varchar(40), email varchar(60), birthday date ); */ public class Demo1 { //这里测试的是增 @Test public void add() throws SQLException{ //通过DBUtils的QueryRunner方法,然后给它一个连接池 QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "insert into users(id,name,password,email,birthday) values(?,?,?,?,?)"; Object params[] = {1,"aaa","123","aa@sina.com",new Date()}; //这个调用的是DBUtils中的update方法,而且是调用的是传递多个参数的update方法 runner.update(sql, params); } //这里测试的是改 @Test public void update() throws SQLException{ //通过DBUtils的QueryRunner方法,然后给它一个连接池 QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "update users set name=? where id=?"; Object params[] = {"xxx",1}; //这个调用的是DBUtils中的update方法,而且是调用的是传递多个参数的update方法 runner.update(sql, params); } //这里测试的删除 @Test public void delete() throws SQLException{ //通过DBUtils的QueryRunner方法,然后给它一个连接池,这个是我们最常用 QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "delete from users where id=?"; //由于这里传递的参数只有一个,所以这里调用的传递一个参数的update方法runner.update(sql, param) runner.update(sql, 1); } //这个测试的是查找 @Test public void find() throws SQLException{ QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "select * from users where id=?"; //这里我们调用query方法查找,这里只有一个参数,还有要传递一个结果集处理类 //注意这里的返回值还要强转,与此同时还要建立对应的bean对象 User user=(User) runner.query(sql, 1, new BeanHandler(User.class)); System.out.println(user); } //这里是测试得到所有数据 @Test public void getAll() throws SQLException{ //不敢你要对数据库什么操作,首先new一个QueryRunner QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "select * from users"; //由于获取到所有的数据,先存到bean中,然后在存入到list,最后返回list //所以这里传递的结果集处理类是BeanListHandler,并将对应bean类穿进去 List list = (List) runner.query(sql, new BeanListHandler(User.class)); System.out.println(list); } //这里测试批处理 //批量向表中插入10条记录 @Test public void batch() throws SQLException{ QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "insert into users(id,name) values(?,?)"; //要向数据库插入10条数据,这里的二维数组就必须是10 Object params[][] = new Object[10][]; for(int i=0;i<params.length;i++){ //为二维数组中的每个一维数组赋值 params[i] = new Object[]{i+1,"aa"+i}; } //最后批处理执行,将sql语句和二维数组传递进去 runner.batch(sql, params); } }
在查询方法query中 DBUtils的作者写了很多结果集处理器,在学完QueryRunner这个类之后,我们很有必要去学习一下作者写的结果集
我们可以通过ResultSetHandler这个接口去查看到底作者写了多少结果集处理器

ResultSetHandler接口


package cn.itcast.demo; 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.ColumnListHandler; import org.apache.commons.dbutils.handlers.KeyedHandler; import org.apache.commons.dbutils.handlers.ScalarHandler; import org.junit.Test; import cn.itcast.utils.JdbcUtils; //测试dbutils框架的结果集处理器 public class Demo2 { //测试ArrayHandler结果集处理器 @Test public void testArrayHandler() throws SQLException{ QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "select *from users"; //这个ArrayHandler结果集会将结果集查到的第一行数据存到一个数组中去 //并且这里这个ArrayHandler结果集处理器不需要传递bean类参数 //因为它这里只会返回数据 Object result[] = (Object[]) runner.query(sql, new ArrayHandler()); System.out.println(result); } //ColumnListHandler这个结果集处理器比较有意思,它可以将结果集中的某一列 //中的数据存入到list中去 @Test public void testColumnListHandler() throws SQLException{ QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "select *from users"; //这里制定了将id列的数据存入到list当中 List list = (List) runner.query(sql, new ColumnListHandler("id")); System.out.println(list); } //这个是测试KeyedHandler这个结果集处理器 @Test public void testKeyedHandler() throws SQLException{ QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "select *from users"; //这里用了KeyedHandler结果集处理器,它将会把结果集中的每一行数据存入到一个独立的map中 //最后将这些独立的map存入到一个大的map,这个大的map的key名称为指定的列名id //注意这里Map<Integer,Map<String,Object>>中的第二个Map中的泛型,key值为string(因为这里列名) //value值是任意类型的,所以这里是Object Map<Integer,Map<String,Object>> map = (Map) runner.query(sql, new KeyedHandler("id")); //然后我们对这个大的map.endtry迭代,迭代出的是一个个小的map<String,Object> for(Map.Entry<Integer, Map<String,Object>> entry : map.entrySet()){ //map.entryset Map<String,Object> innermap = entry.getValue(); //然后迭代小的map.entry,最后将map中的键和值打印出来 for(Map.Entry<String, Object> innerEntry : innermap.entrySet()){ System.out.println(innerEntry.getKey() + "=" + innerEntry.getValue()); } } } //这个测试ScalarHandler结果集处理器 @Test public void testScalarHandler() throws SQLException{ QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "select count(*) from users"; //ScalarHandler这个结果集处理器可以将指定列的数据存入到一个对象中 //这里还要注意select count(*) from users这个查询的结果是一个long类型而不是int类型 long count = (Long) runner.query(sql, new ScalarHandler(1)); //Long System.out.println(count); } }



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);


package cn.itcast.demo; import java.sql.Connection; import java.sql.SQLException; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanHandler; import org.junit.Test; import cn.itcast.domain.Account; import cn.itcast.utils.JdbcUtils; //使用dbutils框架完成事务管理 public class AccountDao { //dao curd //从a--->b转100元 //由于在实际开发中,在dao层只能有增删改查,如果在dao层添加其它的业务逻辑就违背了 //三层架构的思想,虽然下面这段代码可以实现转账的功能,但是这样做在开发中是不允许的 //所以像这种事务处理在dao层是不允许的,那我们如何处理事务呢? //这里我们任然不违背三层架构的思想,在dao层还是写查找和更新功能,然后再service层实现 //转账 @Test public void transfer() throws SQLException{ Connection conn = null; try{ QueryRunner runner = new QueryRunner(); conn = JdbcUtils.getConnection(); conn.setAutoCommit(false); String sql1 = "update account set money=money-100 where name='aaa'"; runner.update(conn,sql1); //int x = 1/0; String sql2 = "update account set money=money+100 where name='bbb'"; runner.update(conn,sql2); conn.commit(); }finally{ conn.close(); } } }
那么我们在实际开发中该如何实现转帐呢?
在dao层我们任然定义增删改查的方法,我们可以在service层实现转帐的功能
/day17/src/cn/itcast/demo/AccountDao.java
package cn.itcast.demo; import java.sql.Connection; import java.sql.SQLException; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanHandler; import org.junit.Test; import cn.itcast.domain.Account; import cn.itcast.utils.JdbcUtils; //使用dbutils框架完成事务管理 public class AccountDao { //dao curd //从a--->b转100元 //由于在实际开发中,在dao层只能有增删改查,如果在dao层添加其它的业务逻辑就违背了 //三层架构的思想,虽然下面这段代码可以实现转账的功能,但是这样做在开发中是不允许的 //所以像这种事务处理在dao层是不允许的,那我们如何处理事务呢? //这里我们任然不违背三层架构的思想,在dao层还是写查找和更新功能,然后再service层实现转帐 public Account find(int id) throws SQLException{
//这种传递了连接池的QuryRunner会自动关闭连接Connection QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "select * from account where id=?"; return (Account) runner.query(sql,id, new BeanHandler(Account.class)); } public void update(Account account) throws SQLException{ QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "update account set money=? where id=?"; Object params[] = {account.getMoney(),account.getId()}; runner.update(sql, params); } }
/day17/src/cn/itcast/service/AccountService.java
package cn.itcast.service; import java.sql.Connection; import java.sql.SQLException; import cn.itcast.demo.AccountDao; import cn.itcast.domain.Account; import cn.itcast.utils.JdbcUtils; public class AccountService { //ThreadLocal map set() //这里转帐需要将原账户的id和目标账户的id以及转多少钱传递过来 public void transfer(int sourceid,int targetid,double money) throws SQLException{ Connection conn = null; try{ conn = JdbcUtils.getConnection(); conn.setAutoCommit(false); AccountDao dao = new AccountDao(conn); //查找到元账户和目标账户 Account source = dao.find(sourceid); Account target = dao.find(targetid); //将原账户和目标账户的钱更新 source.setMoney(source.getMoney()-money); target.setMoney(target.getMoney()+money); //最后将原账户和目标账户更新到数据库中去 dao.update(source); //update //加入这里出现了异常则这个转帐程序则有安全问题,所以这种没有将原账户和目标账户放在一个事务中执行的程序是不安全的
dao.update(target); //update conn.commit(); }finally{ conn.close(); } } }
在实际开发里面dao层只能出现增上改查的方法,不能有其它的业务逻辑,上面这种转帐方式虽然也可以,但是还是有问题,问题出在原账户和目标账户
更新不再同一个事务中,这样会导致程序出现安全问题,所以我们下面会将上面两段代码改成在同一个事务
/day17/src/cn/itcast/demo/AccountDao.java
package cn.itcast.demo; import java.sql.Connection; import java.sql.SQLException; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanHandler; import org.junit.Test; import cn.itcast.domain.Account; import cn.itcast.utils.JdbcUtils; //使用dbutils框架完成事务管理 public class AccountDao { //dao curd //这个dao通过构造饭方法接受一个Connection private Connection conn; public AccountDao(Connection conn){ this.conn = conn; } //有一个有参数的构造方法就必须有一个无参数的构造方法 public AccountDao() { } public Account find(int id) throws SQLException{ //如果要用事务这里的QueryRunner就不要传递连接池,我们可以在query和update中传入连接 //这样我们就可以手动关闭连接,从而使用事务 QueryRunner runner = new QueryRunner(); String sql = "select * from account where id=?"; return (Account) runner.query(conn,sql,id, new BeanHandler(Account.class)); } public void update(Account account) throws SQLException{ QueryRunner runner = new QueryRunner(); String sql = "update account set money=? where id=?"; Object params[] = {account.getMoney(),account.getId()}; runner.update(conn,sql, params); } }
/day17/src/cn/itcast/service/AccountService.java
package cn.itcast.service; import java.sql.Connection; import java.sql.SQLException; import cn.itcast.demo.AccountDao; import cn.itcast.domain.Account; import cn.itcast.utils.JdbcUtils; public class AccountService { public void transfer(int sourceid,int targetid,double money) throws SQLException{ Connection conn = null; try{ //在转帐之前获取一个连接 conn = JdbcUtils.getConnection(); //关闭自动提交,开启事务 conn.setAutoCommit(false); AccountDao dao = new AccountDao(conn); Account source = dao.find(sourceid); Account target = dao.find(targetid); source.setMoney(source.getMoney()-money); target.setMoney(target.getMoney()+money); dao.update(source); //update dao.update(target); //update //提交事务 conn.commit(); }catch(Exception e){ e.printStackTrace(); JdbcUtils.rollbackTransaction(); }finally{ //关闭连接 conn.close(); } } }
以上两段代码就将转账的过程放到一个事务中去了,这里主要是想dao层传递一个Connection,而其在new QueryRunner这个对象时要用无参数的那个
然后在用它的update和find方法时将连接传递给它们
但是这样写还是不优雅,如果想优雅则会用到ThreadLocal这个类来进行事务管理,从名字来看叫局部线程,这个知识点很抽象
不好学,这个类可以想象成一个map容器,这个类有set方法向里面存数据,而关键字则是当前线程
这个ThreadLocal类相当于一个线程容器,当你向你们存数据的时候,关键字一定是当前线程


/day17/src/cn/itcast/utils/JdbcUtils.java
package cn.itcast.utils; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; import javax.sql.DataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; public class JdbcUtils { private static DataSource ds; //new一个ThreadLocal对象,里面存放的是Connection,这个ThreadLocal相当于一个容器 private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>(); static{ try { //这里采用c3p0的连接池 ,这里没有指定用c3p0-config.xml中那个配置则默认采用缺省配置 ds = new ComboPooledDataSource(); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } public static DataSource getDataSource(){ return ds; } //关闭自动提交,开启事务 public static void startTransaction() throws SQLException{ //注意这个获取的连接是当前线程上的连接 //当启动这个方法时,就获取连接开启事务, //还要注意这里获取连接一开始就是从连接池中获取然后绑定到当前线程上去的 Connection conn = getConnection(); if(conn!=null){ //如果连接不为空则开启事务 conn.setAutoCommit(false); } } //提交事务 public static void commitTransaction() throws SQLException{ Connection conn = getConnection();//注意这个获取的连接时当前线程上的连接 if(conn!=null){ conn.commit(); } } //回滚事务 public static void rollbackTransaction() throws SQLException{ Connection conn = getConnection();//注意这个获取的连接时当前线程上的连接 if(conn!=null){ conn.rollback(); } } //关闭连接 public static void closeConnection() throws SQLException{ Connection conn = getConnection(); //注意这个获取的连接时当前线程上的连接 if(conn!=null){ try{ conn.close(); }finally{ //由于这个理的ThreadLocal是一个静态的容器,所以在一个线程结束的时候一定要移除它里面的数据 //这里一定得记得关闭呀,否则这个ThreadLocal常年类月的积累的Connection可以使内存溢出的 tl.remove(); //关闭连接后,切记要解除当前线程上的的连接 } } } //加载驱动,获取链接 public static Connection getConnection() throws SQLException{ Connection conn = tl.get(); //得到当前线程上绑定的连接 if(conn==null){ //如果当前线程上没有绑定连接则从连接池中获取一个连接 conn = ds.getConnection(); tl.set(conn); //把得到的连接绑定到当前线程 } return conn;//最后将这个连接返回 } //释放资源 public static void release(Connection conn,Statement st,ResultSet rs){ try{ if(rs!=null) rs.close(); }catch (Exception e) { e.printStackTrace(); rs=null; } try{ if(st!=null) st.close(); }catch (Exception e) { e.printStackTrace(); st=null; } try{ //MyConnection if(conn!=null) conn.close(); }catch (Exception e) { e.printStackTrace(); conn=null; } } }
/day17/src/cn/itcast/demo/AccountDao.java
package cn.itcast.demo; import java.sql.Connection; import java.sql.SQLException; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanHandler; import org.junit.Test; import cn.itcast.domain.Account; import cn.itcast.utils.JdbcUtils; //使用dbutils框架完成事务管理 public class AccountDao { //dao curd //这个dao通过构造饭方法接受一个Connection private Connection conn; public AccountDao(Connection conn){ this.conn = conn; } //有一个有参数的构造方法就必须有一个无参数的构造方法 public AccountDao() { } public Account find(int id) throws SQLException{ //如果要用事务这里的QueryRunner就不要传递连接池,我们可以在query和update中传入连接 //这样我们就可以手动关闭连接,从而使用事务 QueryRunner runner = new QueryRunner(); String sql = "select * from account where id=?"; //这里的JdbcUtils.getConnection()是获取到单前线程上绑定的数据库 return (Account) runner.query(JdbcUtils.getConnection(),sql,id, new BeanHandler(Account.class)); } public void update(Account account) throws SQLException{ QueryRunner runner = new QueryRunner(); String sql = "update account set money=? where id=?"; Object params[] = {account.getMoney(),account.getId()}; //这里的JdbcUtils.getConnection()是获取到单前线程上绑定的数据库 runner.update(JdbcUtils.getConnection(),sql, params); } }
/day17/src/cn/itcast/service/AccountService.java
package cn.itcast.service; import java.sql.Connection; import java.sql.SQLException; import cn.itcast.demo.AccountDao; import cn.itcast.domain.Account; import cn.itcast.utils.JdbcUtils; public class AccountService { //ThreadLocal map set() 允许用户在一个线程范围内共享数据 public void transfer(int sourceid,int targetid,double money) throws SQLException{ Connection conn = null; try{ AccountDao dao = new AccountDao(); //获取连接,开启事务 //注意这里一开始获取的连接时从连接池中获取然后绑定到当前线程上去 JdbcUtils.startTransaction(); Account source = dao.find(sourceid); Account target = dao.find(targetid); source.setMoney(source.getMoney()-money); target.setMoney(target.getMoney()+money); dao.update(source); //update dao.update(target); //update //提交事务 JdbcUtils.commitTransaction(); }catch(Exception e){ e.printStackTrace(); //如果抛异常则回滚 JdbcUtils.rollbackTransaction(); }finally{ //关闭连接 JdbcUtils.closeConnection(); } } }
通过上面的代码给我们提供了一种新的思维,那就是在一个线程范围类可以通过ThreadLocal共享数据,
在三层数据机构中如果想将某些共享数据在多个层之间共享,我们就可以在servlet里面用ThreadLocal
将这些共享数据保存起来,当一个用户访问数据库时就是一个独立的线程,所以ThreadLocal中的数据
在这个线程内是共享的

事务管理的的扩展
在实际开发中我们不仅需要将多个dao层放在一个事务里面,有的时候还需要将多个service层放在同一个事务里面

这个时候我们又该怎么做,其实在没有学sprint框架之前则会在servlet之前用了一个过滤器来拦截Connection
然后将Connection存入到TreadLocal中,然后开启线程,最后当还是通过过滤器将事务提交然后关闭连接,晴空
ThreadLocal,这种方案是在没有学sprint的时候采取的方案,其实sprint最核心的框架就是事务管理
Tip:使用Jdbc操作多个表
首先我们将部门和员工的对象设计出来
/day17/src/cn/itcast/domain/Department.java
package cn.itcast.domain; import java.util.HashSet; import java.util.Set; public class Department { private String id; private String name; //部门有一个添加员工的集合 private Set<Employee> employees = new HashSet(); public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Set<Employee> getEmployees() { return employees; } public void setEmployees(Set<Employee> employees) { this.employees = employees; } }
/day17/src/cn/itcast/domain/Employee.java
package cn.itcast.domain; public class Employee { private String id; private String name; private double salary; //员工有所属部门 private Department department; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } public Department getDepartment() { return department; } public void setDepartment(Department department) { this.department = department; } }
然后我们开始写一个demo3业务层,将设置一个开发部,这个开发部添加2个员工
/day17/src/cn/itcast/demo/Demo3.java
package cn.itcast.demo; import java.sql.SQLException; import org.junit.Test; import cn.itcast.dao.DepartmentDao; import cn.itcast.domain.Department; import cn.itcast.domain.Employee; //把一对多的对象存到数据库 public class Demo3 { @Test public void addDepartment() throws SQLException{ Department d = new Department(); d.setId("1"); d.setName("开发部"); Employee e1 = new Employee(); e1.setId("1"); e1.setName("aa"); e1.setSalary(1000); Employee e2 = new Employee(); e2.setId("2"); e2.setName("bb"); e2.setSalary(1000); d.getEmployees().add(e1); d.getEmployees().add(e2); //d DepartmentDao dao = new DepartmentDao(); dao.add(d); } }
然后我们来做部门dao层,将部门对象存入到数据库中,但是在写dao层的同时我们先将表建立出来
create table department ( id varchar(40) primary key, name varchar(40) ); create table employee ( id varchar(40) primary key, name varchar(40), //钱要用decimal,这里大小为10位,保留小数点2位 salary decimal(10,2), //添加外键列 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;


浙公网安备 33010602011771号