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;
敬请评论
(1)若您觉得本文 有用处 —— 请留言评论,以坚定其他 IT童鞋 阅读本文的信心。
(2)若您觉得本文 没用处 —— 请留言评论,笔者将会改进不足,以便为大家整理更加好用的笔记。