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