【问题标题】:Reducing code duplication in hibernate sessions减少休眠会话中的代码重复
【发布时间】:2017-08-27 03:19:06
【问题描述】:

我正在使用 Jersey 和 Hibernate 以及 Postgres 作为后端开发 REST Web 服务。我有 3 个用于模型、资源和服务的包。对于服务中的每种方法,我都在复制代码:

SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
Session session = sessionFactory.openSession();
Transaction transaction = null;
try {
    transaction = session.beginTransaction();
    ***SOME UNIQUE OBJECT***  = (***SOME UNIQUE OBJECT TYPE***) session.save(***SOME UNIQUE OBJECT***);
    session.getTransaction().commit();
} catch (HibernateException e) {
    if (transaction != null) {
        transaction.rollback();
    }
} finally {
    session.close();
}
return ***SOME UNIQUE OBJECT***;

有人可以建议我如何在不复制此代码的情况下做到这一点。我正在考虑为实体模型类编写一个接口。然后将该接口传递给上面的代码。不确定这是否是最好的方法。

【问题讨论】:

  • 使用 DAO 设计模式。所有持久性方法和数据库交互都应该只通过您的数据访问类发生。您的服务类应该调用数据访问类的方法。只有数据访问对象应该调用像 session.save() 这样的持久化方法 在 DAO 类中创建一个方法,并且可以从不同的服务类中多次引用它。还要使您的所有服务类方法(那些通过 DAO 类持久化)具有事务性。在方法签名之上使用@transactional。
  • @RahulRaj 你能展示一个例子或包含一些资源的链接吗?我找到了这个tutorialspoint.com/design_pattern/…
  • 可以Generic Types解决你的问题。只需将此块提取为save 方法即可。
  • 对 DAO 类使用 @Repository 注解,对所有持久化的服务类方法使用 @Transactional
  • @RahulRaj 如果你能举个例子会很有帮助。

标签: java hibernate design-patterns


【解决方案1】:

在任何设计模式中,都非常鼓励和提倡使用接口。它将让您在未来添加不同的实现/功能减少依赖,这不过是抽象。

一个例子:

public interface StudentDao {
   public void save(Student student);
}

我们有一个实现类

@Repository
public interface StudentDaoImpl implements StudentDao {

   @Autowired  
   SessionFactory sessionFactory; 

   public void save(Student student){
      Session currentSession = sessionFactory.getCurrentSession();  
      currentSession.save(student);    
      currentSession.close();
   }
}

以服务类为例:

@Service
public class StudentService {

  @Autowired
  StudentDao studentDao; 

  @Transactional  
  public void save(Student student){
      studentDao.save(student);
  }


}

完整的解释将取决于您的项目和要求。不过,我为您解释了一个基于注释的简单示例。

【讨论】:

  • 这是很有帮助的设计模式实习生。但这并不能解决我原来的问题。现在想象一下,我有另一个实体Teacher。然后我需要用TeacherDaoTeacherDaoImpl 重复此操作,最终我将在StudentDaoImplTeacherDaoImpl 中复制大量代码...
  • 请记住,当您遵循设计模式时,您可能最终会通过创建更多类/方法来编写更多代码(而不是重复代码),但考虑到这是设计应用程序的最首选方式该应用程序会根据未来的要求进行更改。它易于维护和更改,任何开发人员都易于理解流程。您在这里没有添加任何重复的代码,只是在 dao 上添加一个方法,并通过调用它们在服务类中多次使用它。你能详细说明这里有什么重复吗?
  • 首先,我不想在这里使用Spring注解。无论如何,您的 StudentDaoImpl 接受类型 Student。所以对于另一个实体Teacher,我需要写一个新的TeacherDaoImpl,它需要打开一个新的会话。如果没有 Spring 注释,这将重复我的问题中的 sessionFactory.openSession()session.beginTransaction(); 以及 trycatch 之类的内容。
  • 那么你是使用普通的JPA来持久化还是休眠?上面的例子使用了hibernate注解,只是为了让你知道hibernate是多么的强大和简单:)你可以通过在签名中添加一个简单的注解来节省多行代码。
  • 你可以写一个类,定义一个返回会话对象的方法。最后每次需要会话时调用该方法。这可能是没有注释的情况。
【解决方案2】:

您可以使用spring-data-jpa 模块并创建extends the CrudRepository interface for your repository 的存储库接口。

默认情况下,它将具有所有标准 CRUD 方法,例如 save。大多数情况下,您不必定义实现 - Spring 将为接口提供实现。对于非常复杂的存储库代码,您可以混合使用自己的 java 实现方法。

它还允许您在界面中定义许多查询,而无需编写一行代码。

CrudRepository 提供的功能:

public interface CrudRepository<T, ID extends Serializable>
    extends Repository<T, ID> {
                                                                                                                       (1)
    <S extends T> S save(S entity);
                                                                                                                       (2)
    T findOne(ID primaryKey);
                                                                                                                       (3)
    Iterable<T> findAll();

    Long count();
                                                                                                                       (4)
    void delete(T entity);
                                                                                                                       (5)
    boolean exists(ID primaryKey);
                                                                                                                       (6)
    // … more functionality omitted.
}

【讨论】:

  • 我不想在这个中使用弹簧。想和球衣一起去冬眠。所以,如果你能给出一个自定义的实现就太好了。
【解决方案3】:

通过结合Rahul Raj's answer on DAO pattern 和有关泛型类型的各种建议,我设法实现了我想要的,如下所示。

创建了实体类EntityAEntityBEntityC。创建了一个名为Object 的接口,如下所示:

public interface Object<T> {
    public T addObject(Class<T> entityClass, T newObject);
} 

并用

实现它
public class ObjectImpl<T> implements Object<T> {
    @Override 
    public T getObject(Class<T> entityClass, long objectId) {
        SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
        Session session = sessionFactory.openSession();
        Transaction transaction = null;
        T entity = null;
        try {
            transaction = session.beginTransaction();
            entity = session.get(entityClass, objectId);
            session.getTransaction().commit();
        } catch (HibernateException e) {
            if (transaction != null) {
                transaction.rollback();
            }
        } finally {
            session.close();
        }
        return entity;
    }
}

现在我可以使用 addObject 方法插入不同实体的对象,例如

public class EntityAService {

    public EntityAService() {
    }
    public EntityA addRedemption(EntityA entityA) {
        Object<EntityA> newEntityA = new ObjectImpl<EntityA>();
        return newEntityA.addObject(EntityA.class, newEntityA);
    }
}

HibernateUtil 提供单例 SessionFactory 对象。这样,我可以添加任意数量的不同实体并将它们的实例保存到数据库中,而无需过多的代码重复。如果有人有更好的建议/cmets,请告诉我。感谢大家的想法!

【讨论】:

    【解决方案4】:

    使用泛型,您可以执行以下操作:

    public static <T>T addObject(Object object, Class<T> type) {
        Session session = HibernateUtil.getSessionFactory().openSession();
        org.hibernate.Transaction tx = null;
    
        try{
            tx = session.beginTransaction();
            session.save(object);
            tx.commit();
        }catch (HibernateException e) {
            if (tx!=null) tx.rollback();
            e.printStackTrace();
        }
        session.close();
        return type.cast(object);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-06-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多