新增新的老师,分配学生。
逆向生成实体类。 idea自带的数据源。
public class StudentDaoImpl implements StudentDao { private Connection connection; private PreparedStatement ps; private ResultSet rs; private String sql; @Override public ListfindStudentByPage(int page, int size) { connection = DBUtil.getConn(); sql = "SELECT * FROM tb_student LIMIT ?,?"; List studentList = new ArrayList<>(10); try { ps = connection.prepareStatement(sql); ps.setInt(1, (page - 1) * size); ps.setInt(2, size); rs = ps.executeQuery(); while (rs.next()) { studentList.add(getStudentInstance(rs)); } } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DBUtil.close(connection,ps,rs); } return studentList; } private Student getStudentInstance(ResultSet rs) throws SQLException { Student student = new Student(); student.setId(rs.getInt("id")); student.setStuName(rs.getString("stu_name")); student.setStuScore(rs.getDouble("stu_score")); student.setStuAge(rs.getInt("stu_age")); return null; } }
老师关联学生: 1. 分页查询学生表信息 tb_student 2. 新增老师的记录 insert into tb_teacher (teacher_name) values (?) 3. 新增中间表的记录-----> 维护老师和学生的关系 insert into tb_teacher_student (tid,sid) values (?,?),(?,?)...(?,?); tid: 老师id ---->刚刚新增的老师的id sid: 学生的id idList
新增老师的dao
@Override public int addTeacher(Teacher teacher) { connection = DBUtil.getConn(); sql = "INSERT INTO tb_teacher (teacher_name) VALUES (?)"; int result = 0; try { ps = connection.prepareStatement(sql); ps.setString(1, teacher.getTeacherName()); result = ps.executeUpdate(); } catch (SQLException throwables) { throwables.printStackTrace(); } finally { DBUtil.close(connection, ps, rs); } return result; }
更新中间表记录
//insert into tb_teacher_student (tid,sid) values (?,?),(?,?)...(?,?); @Override public int addTeacherAndStudent(int tid, Setids) { connection = DBUtil.getConn(); //动态拼接sql ? StringBuilder builder = new StringBuilder("insert into tb_teacher_student (tid,sid) values "); int size = ids.size(); for (int i = 1; i <= size; i++) { builder.append("(?,?)"); if (i == size) { break; } builder.append(","); } int result = 0; //insert into tb_teacher_student (tid,sid) values (?,?),(?,?),(?,?) try { ps = connection.prepareStatement(builder.toString()); //占位符赋值 第奇数个占位符肯定就是tid int count = 1; for (Integer id : ids) { ps.setInt(count++, tid); ps.setInt(count++, id); } //执行sql result = ps.executeUpdate(); } catch (SQLException throwables) { throwables.printStackTrace(); } finally { DBUtil.close(connection, ps, rs); } return result; }
修改新增老师的dao----> 获得自增的id
@Override public int addTeacher(Teacher teacher) { connection = DBUtil.getConn(); sql = "INSERT INTO tb_teacher (teacher_name) VALUES (?)"; int autoId = 0; try { //在mysql里面 维护id自增? SELECT LAST_INSERT_ID() DQL----> 结果 rs //1.告诉mysql的服务 把id返回过来----> 将sql语句发送数据库的时候 就要告诉mysql服务器 Statement.RETURN_GENERATED_KEYS //2. 等价于mysql的服务在update之后 自动执行SELECT LAST_INSERT_ID()这条sql ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); ps.setString(1, teacher.getTeacherName()); ps.executeUpdate();//执行sql语句 将数据持久化保存数据库 //3.在update之后 获得自增的id的数据 由于是一条select语句 结果还是在rs里面 rs = ps.getGeneratedKeys(); if (rs.next()) { autoId = rs.getInt(1); } } catch (SQLException throwables) { throwables.printStackTrace(); } finally { DBUtil.close(connection, ps, rs); } return autoId; }
问题:
//调用新增老师----> 调用2个方法 新增老师+新增中间表 //对于其他调用者而言 不是很优雅 //只需要调用1个新增老师的方法即可---->就能实现同时新增老师+新增中间表 //使用service
public class TeacherServiceImpl implements TeacherService { private static TeacherDao teacherDao = new TeacherDaoImpl(); @Override public void addTeacher(Teacher teacher, SetidSet) { int tid = teacherDao.addTeacher(teacher); teacherDao.addTeacherAndStudent(tid, idSet); System.out.println("success"); } }
张三借给李四1000元。
update tb_student set money = money+1000 where id = 2; update tb_student set money = money-1000 where id = 1;
是属于数据库的事务。 任意DBMs中存在事务特性。 // 概念: 就是一系列(DML)操作。这一系列操作 要么全部成功 要不全部失败。 //不可能出现部分成功 以及部分失败的情况 如果出现了 就是没有满足数据库事务的特性。 ACID: A: 原子性。一个不可分割的工作单位。要么全部成功 要不全部失败。 Atomic C: 一致性。 Consistency 事务开始前的数据 与事务提交之后的数据 必须要一致。 张三给李四1000 3000 500 提交之后: 2000 1500 I: 隔离性。isolation 并发事务 多个客户端同时操作这一行记录 事务之间是相互隔离的。 但是可能会对数据产生不同的影响。 D:durability 持久性、 事务只要提交 数据可以持久化保存的。
-- 数据库里面事务一般都是默认自动提交的。 -- 查询mysql的事务是否是自动提交 -- SHOW VARIABLES like '%commit%'; -- OFF FALSE/ ON TRUE -- 可以将事务自动改为手动操作 -- SET autocommit=1; -- ok的数据还在虚拟表中 将事务自动提交给关闭了 只能手动提交事务 -- BEGIN; -- 手动开启事务 -- insert INTO demo (name,money) VALUES ('11',1000); -- insert INTO demo (name,money) VALUES ('22',1000); -- SELECT * FROM demo; -- COMMIT; -- ROLLBACK
private static void zhuanZhang() { //张三-1000 1 3000 //李四+1000 2 500 //转账这一个功能: 里面涉及了2条dml //要么全部成功 要么全部失败 //需要将这一系列操作 都放到一个“事务”中进行处理 //最根本的原因在与: mysql服务里面 事务是自动提交的。 //先查询 省略 //调用这2个方法 应该在一个事务内执行 //手动控制事务 //在jdbc里面 默认调用mysql服务 事务也是自动提交的 //jdbc: 事务操作与Connection 多个功能操作必须在同一个连接下 //一个连接 就是一个事务 //在正常开发中 dao层一般都不会有try...catch //一般都使用throws //获得连接对象 Connection connection = DBUtil.getConn(); try { //1. 将自动提交事务变为手动提交事务 connection.setAutoCommit(false); StudentDao studentDao = new StudentDaoImpl(connection); studentDao.updateMoneyById(2, new BigDecimal("1500")); //System.out.println(3 / 0); studentDao.updateMoneyById(1, new BigDecimal("1000")); //2. 提交事务 connection.commit(); connection.close(); } catch (Exception e) { e.printStackTrace(); //3. 出现任意异常 回滚事务--->回到原点 try { connection.rollback(); connection.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } }
@Override public void updateMoneyById(int sid, BigDecimal money) throws Exception { sql = "update tb_student set money = ? where id = ?"; ps = connection.prepareStatement(sql); ps.setBigDecimal(1, money); ps.setInt(2, sid); ps.executeUpdate(); }
public StudentDaoImpl(Connection connection){ this.connection = connection; }
开启事务 应该在service层开启
@Override public void addTeacher(Teacher teacher, SetidSet) { //在service里面调用了多个dao //在service编写很多与业务相关的代码 //程序运行期间 ----> 运行时异常 //应该在一个事务内执行多个dao的方法 Connection connection = DBUtil.getConn(); try { //1. 将事务由自动提交改为手动提交 connection.setAutoCommit(false); TeacherDao teacherDao = new TeacherDaoImpl(connection); int tid = teacherDao.addTeacher(teacher);//insert //System.out.println(3 / 0);//出现异常当前程序终止了 teacherDao.addTeacherAndStudent(tid, idSet);//insert //2.提交事务 connection.commit(); System.out.println("新增老师成功"); } catch (Exception e) { e.printStackTrace(); System.out.println("新增老师失败"); try { //3.回滚事务 connection.rollback(); } catch (SQLException throwables) { throwables.printStackTrace(); } } finally { try { connection.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } //一个功能里面 执行多条DML语句 insert update delete //转账 //张三给李四转账 }
并发的事务环境中,数据库事务在不同的隔离级别机制下 ,会对数据产生不同的影响。
1. 数据库里面 存在哪些数据库隔离级别机制? mysql的隔离级别机制是什么? read uncommitted 读未提交(一个事务读到了另外一个事务未提交的数据) read committed 读已提交 oracle sqlserver 默认的机制 repeatable read 可重复读 mysql 默认的机制 SERIALIZABLE 串行化 2. 会对数据产生哪些影响? 该如何解决? 不同的数据库隔离级别机制 产生影响是否一致? 1. 脏读(事务A读到了事务b未提交的数据) 2. 不可重复读(在一个事务里面 多次读取 先后读取到的数据是不一致的 update) 3. 幻读(在一个事务里面 执行多次查询 出现了很多上一次查询里面没有的数据 insert)
脏读 | 不可重复读 | 幻读 | |
---|---|---|---|
read uncommitted | y | y | y |
read committed | n | y | y |
repeatable read | n | n | y |
SERIALIZABLE(表锁) | n | n | n |