以下所有示例都是在工程day17
中。
一、模拟转账
首先我们建立数据库:
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);
注意:这里使用的是day17数据库。
实体类
Account.java
private int id;
private String name;
private float money;
在实际开发中使用JDBC的时候需要用到一个类ThreadLocal,这里我们使用此类改进我们的工具类:
JdbcUtils.java
package cn.itcast.utils;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class JdbcUtils {
private static ComboPooledDataSource ds = null;
private static ThreadLocal<Connection> threadLocal = new ThreadLocal();
static{
ds = new ComboPooledDataSource();
}
public static Connection getConnection() throws SQLException{
Connection connection = threadLocal.get();
if(connection == null){
connection = getDataSource().getConnection();//如果没有绑定链接,就从数据源中获取一个
threadLocal.set(connection);
}
return connection;
}
public static DataSource getDataSource(){
return ds;
}
public static void startTransaction(){
try{
Connection connection = threadLocal.get();
if(connection == null){//从当前线程上获得一个链接,如果没有,则从数据库取得
connection = getConnection();
threadLocal.set(connection);//把connection绑定到当前线程上
}
connection.setAutoCommit(false);
}catch(Exception e){
throw new RuntimeException(e);
}
}
public static void rollback(){
try{
Connection connection = threadLocal.get();
if(connection != null){
connection.rollback();
}
}catch(Exception e){
throw new RuntimeException(e);
}
}
public static void commit(){
try{
Connection connection = threadLocal.get();
if(connection != null){
connection.commit();
}
}catch(Exception e){
throw new RuntimeException(e);
}
}
public static void release(){
try{
Connection connection = threadLocal.get();
if(connection != null){
connection.close();
threadLocal.remove();//解除当前线程上绑定的connection,一定要有
}
}catch(Exception e){
throw new RuntimeException(e);
}
}
}
说明:对于ThreadLocal,我们可以这样理解,这个类就是一个容器。向这个容器存储的对象(存储使用set方法),在当前线程范围内都可以取出来。下面我们通过一个示例进行说明:
Test.java
package junit.test;
public class Test {
public static void main(String[] args) {
Thread currentThread = Thread.currentThread();
System.out.println(currentThread);
ThreadLocal t = new ThreadLocal();
t.set("aaa"); //把某个对象绑定到当前线程上 map(currentThread,"aaa")
String value = (String) t.get();
System.out.println(value);
}
}
说明:我们将这个类用来改进工具类,将当前的Connection绑定到当前线程上,于是就可以保证在进行数据库操作的时候确保所有操作都是使用的同一个Connection。注意各个方法对于Connection的操作。
dao层:AccountDaoImpl.java
package cn.itcast.dao.impl;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import cn.itcast.dao.AccountDao;
import cn.itcast.domain.Account;
import cn.itcast.utils.JdbcUtils;
public class AccountDaoImpl implements AccountDao {
//将操作需要的链接传递进来,自己进行管理
private Connection conn = null;
public AccountDaoImpl(Connection conn){
this.conn = conn;
}
public AccountDaoImpl(){
}
public void update(Account account) throws SQLException{
QueryRunner qr = new QueryRunner();
String sql = "update account set name=?,money=? where id=?";
Object params[] = {account.getName(),account.getMoney(),account.getId()};
qr.update(JdbcUtils.getConnection(),sql, params);
}
public Account find(int id) throws SQLException{
QueryRunner qr = new QueryRunner();
String sql = "select * from account where id=?";
return (Account) qr.query(JdbcUtils.getConnection(),sql, id, new BeanHandler(Account.class));
}
}
说明:在使用Connection的时候我们不能都交给数据库链接池管理,因为一个操作(如update)完了连接池就会帮我们将Connection释放掉,于是这里使用QueryRunner类的时候不要给其传递链接,这样当我们使用update方法传递一个链接的时候在操作结束后就不会将链接释放,注意这个方法传递链接和不传链接时的区别。
业务类:AccountService.java
package cn.itcast.service;
import java.sql.Connection;
import java.sql.SQLException;
import cn.itcast.dao.AccountDao;
import cn.itcast.dao.impl.AccountDaoImpl;
import cn.itcast.domain.Account;
import cn.itcast.utils.JdbcUtils;
public class AccountService {
//其实下面这种方式在开发中也不会使用(被注释掉的),我们一般使用ThreadLocal类,这是一个容器,
//在当前线程范围内都可以取得出来
public void transfer(int sourceid, int targetid, float money) throws SQLException{
Connection connection = null;
try{
JdbcUtils.startTransaction();//获得一个链接,开启一个事务,则下面的代码都是在当前线程上进行的
AccountDao dao = new AccountDaoImpl(connection);
Account source = dao.find(sourceid);
Account target = dao.find(targetid);
source.setMoney(source.getMoney()-money);
target.setMoney(target.getMoney()+money);
dao.update(source);//这里两个操作共享一个链接,我们在dao层中的update方法中就不能给其连接,而应该自己管理
dao.update(target);
JdbcUtils.commit();
}catch(Exception e){
e.printStackTrace();
JdbcUtils.rollback();
}finally{
JdbcUtils.release();
}
}
}
说明:当然这里我们只是让对数据库的操作在同一个事务中完成,但是有时候我们可能还需要多个service在同一个事务中,此时我们就需要使用拦截器,当web请求过来的时候直接拦截,然后获取一个连接给service,这样可以使得所有service在一个事务中,我们将拦截器使用ThreadLocal管理。当然在后面我们一般使用spring进行管理事务。
测试:AccountServiceTest.java
package junit.test;
import java.sql.SQLException;
import org.junit.Test;
import cn.itcast.service.AccountService;
public class AccountServiceTest {
@Test
public void testTransfer() throws SQLException{
AccountService service = new AccountService();
service.transfer(1, 2, 100);
}
}
二、使用JDBC操作多个表(工程day17
)
2.1一对多关系(部门和员工)
相关实体:
Department.java
private String id;
private String name;
private Set<Employee> employees = new HashSet();
Employee.java
private String id;
private String name;
private double salary;
private Department department;
建立数据库表:
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)
);
dao层:DepartmentDaoImpl.java
package cn.itcast.dao.impl;
import java.sql.SQLException;
import java.util.List;
import java.util.Set;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import cn.itcast.dao.DepartmentDao;
import cn.itcast.domain.Department;
import cn.itcast.domain.Employee;
import cn.itcast.utils.JdbcUtils;
public class DepartmentDaoImpl implements DepartmentDao {
//session.add(department);
public void add(Department department) throws SQLException{
QueryRunner qr = new QueryRunner(JdbcUtils.getDataSource());
//1.取出department对象的基本信息存在 department表
String sql = "insert into department(id,name) values(?,?)";
Object params[] = {department.getId(),department.getName()};
qr.update(sql, params);
//2. 取出department对象中所有的员工信息,存在员工表
sql = "insert into employee(id,name,salary,department_id) values(?,?,?,?)";
Set<Employee> set = department.getEmployees();
Object params2[][] = new Object[set.size()][];
int index = 0;
for(Employee e : set){
params2[index++] = new Object[]{e.getId(),e.getName(),e.getSalary(),department.getId()};
}
qr.batch(sql, params2);//使用批处理
}
public Department find(String id) throws SQLException{
QueryRunner qr = new QueryRunner(JdbcUtils.getDataSource());
//1.查出部门表的信息,存在department对象中
String sql = "select * from department where id=?";
Department d = (Department) qr.query(sql, id, new BeanHandler(Department.class));
/*注意:我们在查询的时候对于多的一方,我们能够不进行查找则尽量
不要显示,因为多的一方可能太多导致内存崩溃
在我们这种情况下显示部门是不需要显示员工的,但是在订单中我们
需要显示其下的完整信息,此时则需要显示,于是一定要显示的话
则一定要进行分页,同时能不显示则不显示*/
//2.查出部门下的所有员工信息,存在department对象维护的员工对象中
sql = "select id,name,salary from employee where department_id=?";
List list = (List) qr.query(sql, id, new BeanListHandler(Employee.class));
d.getEmployees().addAll(list);//这里可以一次性添加到set集合中
return d;
}
}
测试:DepartmentService.java
package cn.itcast.test;
import java.sql.SQLException;
import java.util.UUID;
import cn.itcast.dao.DepartmentDao;
import cn.itcast.dao.impl.DepartmentDaoImpl;
import cn.itcast.domain.Department;
import cn.itcast.domain.Employee;
public class DepartmentService {
DepartmentDao dao = new DepartmentDaoImpl();
public void add() throws SQLException{
Department d = new Department();
d.setId(UUID.randomUUID().toString());
d.setName("开发部");
Employee e1 = new Employee();
e1.setId(UUID.randomUUID().toString());
e1.setName("aaa");
e1.setSalary(300);
Employee e2 = new Employee();
e2.setId(UUID.randomUUID().toString());
e2.setName("bbb");
e2.setSalary(400);
d.getEmployees().add(e1);
d.getEmployees().add(e2);
dao.add(d);
}
}
2.2多对多(老师和学生)
相关实体类:
Teacher.java
private String id;
private String name;
private double salary;
private Set<Student> students = new HashSet<Student>();
Student.java
private String id;
private String name;
private Set<Teacher> teachers = new HashSet();
建立数据库表:
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)
);
说明:对于多对多这种关系,我们一般单独建立一张表来保存实体类之间的关系。
dao层:TeacherDaoImpl.java
package cn.itcast.dao.impl;
import java.sql.SQLException;
import java.util.List;
import java.util.Set;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import cn.itcast.dao.TeacherDao;
import cn.itcast.domain.Student;
import cn.itcast.domain.Teacher;
import cn.itcast.utils.JdbcUtils;
public class TeacherDaoImpl implements TeacherDao {
public void add(Teacher t) throws SQLException{
QueryRunner qr = new QueryRunner(JdbcUtils.getDataSource());
//1.取出老师的基本信息,存在老师表
String sql = "insert into teacher(id,name,salary) values(?,?,?)";
Object params[] = {t.getId(),t.getName(),t.getSalary()};
qr.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()};
qr.update(sql, params);
//3.在中间表中说明老师和学生的关系
sql = "insert into teacher_student(teacher_id,student_id) values(?,?)";
params = new Object[]{t.getId(),s.getId()};
qr.update(sql, params);
}
}
public Teacher find(String id) throws SQLException{
QueryRunner qr = new QueryRunner(JdbcUtils.getDataSource());
//1.从老师表中找出老师的基本信息
String sql = "select * from teacher where id=?";
Teacher t = (Teacher) qr.query(sql, id, new BeanHandler(Teacher.class));
//2.从中间表和学生表中得到老师所有的学生(取多的一方的数据时,一定要长心眼,能不取尽量不要取,即使要取,数据量只要一大,一定要分页)
sql = "select s.id,s.name from teacher_student ts,student s where teacher_id=? and ts.student_id=s.id";
List list = (List) qr.query(sql, id, new BeanListHandler(Student.class));
t.getStudents().addAll(list);
return t;
}
}
测试:BusinessService.java
@Test
public void addTeacher() throws SQLException{
Teacher t = new Teacher();
t.setId("1");
t.setName("老张");
t.setSalary(1000000);
Student s1 = new Student();
s1.setId("1");
s1.setName("aaa");
Student s2 = new Student();
s2.setId("2");
s2.setName("bbb");
t.getStudents().add(s1);
t.getStudents().add(s2);
tdao.add(t);
}
@Test
public void findTeacher() throws SQLException{
Teacher t = tdao.find("1");
System.out.println(t);
}
2.3一对一(人和身份证)
相关实体类:
Person.java
private String id;
private String name;
private Idcard idcard;
IdCard.java
private String id;
private String address;
private Person person;
建立数据库表:
create table person
(
id varchar(40) primary key,
name varchar(40)
);
create table idcard
(
id varchar(40) primary key,
address varchar(100),
constraint id_FK foreign key(id) references person(id)
);
说明:对于这种一对一关系,我们这里选择在身份证这边维护关系,当然也可以在人这边维护关系。
dao层:PersonDaoImpl.java
package cn.itcast.dao.impl;
import java.sql.SQLException;
import org.apache.commons.dbutils.QueryRunner;
import cn.itcast.dao.PersonDao;
import cn.itcast.domain.Person;
import cn.itcast.utils.JdbcUtils;
public class PersonDaoImpl implements PersonDao {
public void add(Person p) throws SQLException{
QueryRunner qr = new QueryRunner(JdbcUtils.getDataSource());
//取出person的信息存在person表
String sql = "insert into person(id,name) values(?,?)";
Object params[] = {p.getId(),p.getName()};
qr.update(sql, params);
//取出身份证的信息存在身份证表
sql = "insert into idcard(id,address) values(?,?)";
params = new Object[]{p.getId(),p.getIdcard().getAddress()};
qr.update(sql, params);
}
}
测试:
@Test
public void addPerson() throws SQLException{
Person p = new Person();
p.setId("1");
p.setName("aaa");
Idcard idcard = new Idcard();
idcard.setId("1");
idcard.setAddress("广东");
p.setIdcard(idcard);
pdao.add(p);
}
最后:以上的例子只是说明了基本的原理,不能作为实际开发的例子,都比较简单,这里就不多说了。