数据库设计没有传递jdbc

时间:2016-09-08 13:15:27

标签: java design-patterns jdbc java-8

我遇到了一个我在其中一个项目中面临的数据库设计问题。我正在尝试实现一项服务,该服务的一部分是数据库层。它的设置使得我有一个帮助程序类,它们对数据库执行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帮助程序。

简而言之,您可以看到代码通常像多个方法一样重复:

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

2 个答案:

答案 0 :(得分:3)

每当您的代码序列重复但在某些部分只有不同时,您可以使用template method

在你的情况下,我将介绍一个TransactionTemplate类,并为不同的部分使用回调接口。 E.g。

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, classRoomid, conn);
               updateClassroomDBHelper.markUpdated(classRoomid, conn);
               conn.commit();
            }
            catch
            {
               throw new MyCustomException(ErrorCode.Student);
            }
            return null;
        }

}

答案 1 :(得分:2)

您目前面临的两个主要问题是与连接相关的重复任务的锅炉板代码(获取/执行/关闭等) 和跨越方法边界获得相同连接的基础结构。第一个通常使用Template pattern和后者来解决 使用Threadlocal变量传递方法之间的适当连接。很久以前,Java世界已经解决了这些类型的问题 将要求你依赖Spring(JDBC template)等框架,这些框架在过去十年左右具有此功能,或者你需要推出剥离 这个基础设施的版本。如果您对后者感兴趣,那么您可以从Github上共享的类似attmepts中获取提示,如this