我有一个数据库设计问题,我面临着我的一个项目.我试图实现一个服务,而这个服务的一部分是一个db层.它的设置是这样的,我有一个帮助类,执行get / update方法到数据库和它们之上的层,作为一个看门人.例如:
public class GetStudentDBHelper { public List<Student> get(List<Integer> ids) { Conn getConnection... // run sql query and construct returning Student objects } public List<Student> get(List<Classroom> byClassroom) { // get all students in passed in classrooms // run sql query and construct returning Student objects } } public class StudentJanitor { public GetStudentDBHelper getStudentDBHelper; public UpdateStudentDBHelper updateStudentDBHelper; public UpdateClassroomDBHelper updateClassroomDBHelper; public List<Student> getStudents(List<Integer> ids) { return getStudentDBHelper.get(ids); } public void saveStudents(List<Students> students,int classRoomid) { Connection conn = Pool.getConnection(); // assume this gives a jdbc conn.autocommit(false); try { try { updateStudentDBHelper.saveForClassroom(students,classRoomid,conn); updateClassroomDBHelper.markUpdated(classRoomid,conn); conn.commit(); } catch { throw new MyCustomException(ErrorCode.Student); } } catch (sqlException c) { conn.rollback(); } finally { conn.close(); } } public class ClassroomJanitor{ public void saveClassRoon(List<Classrooms> classrooms) { Connection conn = Pool.getConnection()// assume this gives a jdbc conn.autocommit(false); try { try { updateClassroomDBHelper.save(classrooms,conn); updateStudentDBHelper.save(classrooms.stream().map(Classroom::getStudents).collect(Collections.toList()),conn); conn.commit(); } catch { throw new MyCustomException(ErrorCode.ClassRoom); } } catch (sqlException c) { conn.rollback(); } finally { conn.close(); } }... public class GetClassroomDBHelper{}... public class UpdateClassroomDBHelper{}...
更新db类都会组成多个其他更新程序,以防他们需要更新其他表中的值(即保存学生意味着我必须触摸学生所属的课堂表,以更新其上次更新的时间).
我遇到的问题是更新db类,如果我正在触摸多个表,以便拥有交易和回滚功能,我必须从我的Janitor类传递一个连接.看到上面我的意思.有没有更好的方法来做到这一点?这种类型的try,catch,传入conn到db helpers,必须为我的管理器中的任何多事务操作完成.
Connection conn = Pool.getConnection()// assume this gives a jdbc conn.autocommit(false); try { try { //do some business logic requiring Connection conn } catch { throw new MyCustomException(ErrorCode); } } catch (sqlException c) { conn.rollback(); } finally { conn.close(); }
解决方法
每当你有一个代码序列是重复的,但它只在一些部分不同,你可以使用一个
template method.
在你的情况下,我将介绍一个TransactionTemplate类,并为不同的部分使用回调接口.例如.
public class TransactionTemplate { private DataSource dataSource; public TransactionTemplate(DataSource dataSource) { this.dataSource = Objects.requireNonNull(dataSource); } public <T> T execute(TransactionCallback<T> transactionCallback) throws Exception { Connection conn = dataSource.getConnection();// assume this gives a jdbc try { conn.setAutoCommit(false); T result = transactionCallback.doInTransaction(conn); conn.commit(); return result; } catch (Exception e) { conn.rollback(); throw e; } finally { conn.close(); } } }
回调界面看起来像这样
public interface TransactionCallback<T> { public T doInTransaction(Connection conn) throws Exception; }
正如您可以看到TransactionTemplate管理事务,而TransactionCallback实现了在一个事务中必须完成的逻辑.
那么你的客户端代码就会这样
public class StudentJanitor { private TransactionTemplate transactionTemplate; StudentJanitor(DataSource dataSource) { transactionTemplate = new TransactionTemplate(dataSource); } public void saveStudents(List<Students> students,int classRoomid) { SaveStudentsTransaction saveStudentsTransaction = new SaveStudentsTransaction(students,classRoomid); transactionTemplate.execute(saveStudentsTransaction); } }
逻辑放在TransactionCallback中
public class SaveStudentsTransaction implements TransactionCallback<Void> { public GetStudentDBHelper getStudentDBHelper; public UpdateStudentDBHelper updateStudentDBHelper; public UpdateClassroomDBHelper updateClassroomDBHelper; private List<Students> students; private int classRoomid; public SaveStudentsTransaction(List<Students> students,int classRoomid) { this.students = students; this.classRoomid = classRoomid; } @Override public Void doInTransaction(Connection conn) throws Exception { try { updateStudentDBHelper.saveForClassroom(students,conn); updateClassroomDBHelper.markUpdated(classRoomid,conn); conn.commit(); } catch { throw new MyCustomException(ErrorCode.Student); } return null; } }