Java中接口使用相关的一些疑惑(创建Hibernate DAO)
Posted
技术标签:
【中文标题】Java中接口使用相关的一些疑惑(创建Hibernate DAO)【英文标题】:Some doubts related to the interface use in Java (creating an Hibernate DAO) 【发布时间】:2013-03-07 19:12:18 【问题描述】:我正在开发一个简单的示例应用程序,它使用 Hibernate 来实现一个对数据库表执行一些 CRUD 操作的 DAO。
我的应用程序运行良好,但我怀疑这可能与我在使用 Java 时的角色差距有关。
在我的应用程序架构中,我有一个名为 HibernateDAO 的接口,在其中我只声明了我将拥有的 CRUD 方法,这个:
package org.andrea.myH8.myH8HelloWorld;
public interface HibernateDAO
// Operazione di CREATE:
public Integer addEmployee(String fname, String lname, int salary);
// Operazione di READ:
public void listEmployees();
// Operazione di UPDATE:
public void updateEmployee(Integer EmployeeID, int salary);
// Operazione di DELETE:
public void deleteEmployee(Integer EmployeeID);
然后我有具体的类实现了previous接口,这个类被命名为HibernateDAOImpl,这个:
package org.andrea.myH8.myH8HelloWorld;
import java.util.List;
import java.util.Date;
import java.util.Iterator;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
// Classe principale contenente il metodo main():
public class HibernateDAOImpl implements HibernateDAO
// Factory per la creazione delle sessioni di Hibernate:
private static SessionFactory factory;
/** Registratore di servizzi che contiene, gestisce e fornisce l'accesso
* ai servizzi registrati:
*/
private static ServiceRegistry serviceRegistry;
public HibernateDAOImpl()
try
/** Un'istanza di Configuration consente all'applicazione di specificare le
* proprietà ed il documento di mapping tra oggetti da persistere e dabelle
* che devono essere utilizzati durante la creazione di una SessionFactory
*/
Configuration configuration = new Configuration();
/** Per la configurazione di Hibernate utilizza le proprietà specificate nel
* file XML di configurazione avente nome standard hibernate.cfg.xml:
*/
configuration.configure();
/** ServiceRegistry crea un'istanza di ServiceRegistry (il registratore di servizzi)
* alla quale vengono applicati un gruppo di settaggi contenuti dentro le proprietà
* dell'oggetto configuration.
* Infine il registratore di Service creato viene costruito in base a tutte le
* impostazioni specificate.
*/
serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
/** Crea l'istanza della SessionFactory usando le proprietà ed i mapping
* definiti in questa configurazione
*/
factory = configuration.buildSessionFactory(serviceRegistry);
catch (Throwable ex) // Se viene sollevata un'eccezione la gestisce
System.err.println("Failed to create sessionFactory object." + ex);
throw new ExceptionInInitializerError(ex);
/** Metodo relativo all'operazione CREATE di un record nella tabella EMPLOYEE:
*
* @param fname nome
* @param lname cognome
* @param salary salario
* @return l'identificativo intero univoco che rappresenta l'impiegato
*/
public Integer addEmployee(String fname, String lname, int salary)
// Crea e mette nella connection pool la connessione JDBC:
Session session = factory.openSession();
Transaction tx = null; // Rappresenta una transazione
Integer employeeID = null; // Identificativo univoco impiegato
try
// Inizia un'unità di lavoro e ritorna la transazione ad essa associata:
tx = session.beginTransaction();
// Crea un nuovo oggetto Employee:
Employee employee = new Employee(fname, lname, salary);
/** Persiste l'istanza transiente data ed in primo luogo gli assegna
* un identificatore univoco autogenerato
*/
employeeID = (Integer) session.save(employee);
tx.commit(); // Esegue la commit della transazione
catch (HibernateException e) // Se viene sollevata un'eccezione:
if (tx != null) // Se la transazione non è nulla
tx.rollback(); // Esegue la rollback e riporta alla situazione iniziale
e.printStackTrace();
finally // Alla fine:
// Chiude la sessione rilasciando la connessione JDBC e facendo il cleanup:
session.close();
return employeeID;
/* Metodo usato per elencare tutti i record presenti nella tabella EMPLOYEE: */
public void listEmployees()
// Crea e mette nella connection pool la connessione JDBC:
Session session = factory.openSession();
Transaction tx = null; // Rappresenta una transazione
try
// Inizia un'unità di lavoro e ritorna la transazione ad essa associata:
tx = session.beginTransaction();
/** Crea la query usando una stringa HQL per ottenere tutti i record
* ed ottiene la lista di tutti i record rappresentati ognuno da un
* oggetto Employee
*/
List employees = session.createQuery("FROM Employee").list();
// Stampa i valori delle proprietà di ogni oggett nella lista:
for (Iterator iterator = employees.iterator(); iterator.hasNext();)
Employee employee = (Employee) iterator.next();
System.out.print("First Name: " + employee.getFirstName());
System.out.print(" Last Name: " + employee.getLastName());
System.out.println(" Salary: " + employee.getSalary());
tx.commit(); // Esegue la commit della transazione
catch (HibernateException e) // Se viene sollevata un'eccezione:
if (tx != null) // Se la transazione non è nulla
tx.rollback(); // Esegue la rollback e riporta alla situazione iniziale
e.printStackTrace();
finally // Alla fine:
// Chiude la sessione rilasciando la connessione JDBC e facendo il cleanup:
session.close();
/** Metodo che aggiorna il salario di un impiegato
*
* @param EmployeeID l'ID univoco che identifica il record nella tabella EMPLOYEE
* @param salary il nuovo salario dell'impiegato rappresentato dal record
*/
public void updateEmployee(Integer EmployeeID, int salary)
// Crea e mette nella connection pool la connessione JDBC:
Session session = factory.openSession();
Transaction tx = null; // Rappresenta una transazione
try
// Inizia un'unità di lavoro e ritorna la transazione ad essa associata:
tx = session.beginTransaction();
/**
* Ritorna l'istanza persistita sul database della classe fornita come
* parametro di input avente l'identificatore fornito come parametro
* di input. Se non esiste un tale oggetto persistito nel database
* allora ritorna null.
*/
Employee employee = (Employee) session.get(Employee.class,
EmployeeID);
employee.setSalary(salary); // Setta il nuovo valore del salario
session.update(employee); // Esegue l'update di tale oggetto
tx.commit(); // Esegue la commit della transazione
catch (HibernateException e) // Se viene sollevata un'eccezione:
if (tx != null) // Se la transazione non è nulla
tx.rollback(); // Esegue la rollback e riporta alla situazione iniziale
e.printStackTrace();
finally // Alla fine:
// Chiude la sessione rilasciando la connessione JDBC e facendo il cleanup:
session.close();
/** Metodo che elimina un record dalla tabella EMPOLYEE
*
* @param EmployeeID l'ID univoco che identifica il record nella tabella EMPLOYEE
*/
public void deleteEmployee(Integer EmployeeID)
// Crea e mette nella connection pool la connessione JDBC:
Session session = factory.openSession();
Transaction tx = null; // Rappresenta una transazione
try
// Inizia un'unità di lavoro e ritorna la transazione ad essa associata:
tx = session.beginTransaction();
/**
* Ritorna l'istanza persistita sul database della classe fornita come
* parametro di input avente l'identificatore fornito come parametro
* di input. Se non esiste un tale oggetto persistito nel database
* allora ritorna null.
*/
Employee employee = (Employee) session.get(Employee.class,
EmployeeID);
session.delete(employee); // Rimuove l'oggetto dalla tabella del database:
tx.commit(); // Esegue la commit della transazione
catch (HibernateException e) // Se viene sollevata un'eccezione:
if (tx != null) // Se la transazione non è nulla
tx.rollback(); // Esegue la rollback e riporta alla situazione iniziale
e.printStackTrace();
finally // Alla fine:
// Chiude la sessione rilasciando la connessione JDBC e facendo il cleanup:
session.close();
好的,省略实体类 Person,最后一个类是 MainApp 类,它包含我用来测试这个类如何工作的 main() 方法:
包 org.andrea.myH8.myH8HelloWorld;
公共类 MainApp
public static void main(String[] args)
// Creo una nuova istanza dell'oggetto DAO per interagire con il database:
HibernateDAO ME = new HibernateDAOImpl();
System.out.println("CREAZIONE DEI RECORD:");
// Inserisce 3 record nella tabella EMPLOYEE sul DB:
Integer empID1 = ME.addEmployee("Zara", "Ali", 1000);
Integer empID2 = ME.addEmployee("Daisy", "Das", 5000);
Integer empID3 = ME.addEmployee("John", "Paul", 10000);
// Elenca tutti i record della tabella EMPLOYEE:
System.out.println("ELENCA TUTTI I RECORD:");
ME.listEmployees();
// Update del record avente id corrispondente al valore della variabile empID1
System.out.println("Update del record avente id corrispondente al valore: " + empID1);
ME.updateEmployee(empID1, 5000);
/* Elimina dalla tabella EMPLOYEE il record avente id corrispondente al valore
* della variabile empID2 */
System.out.println("Eliminazione del record avente id corrispondente al valore: " + empID2);
ME.deleteEmployee(empID2);
// Elenca tutti i record della tabella EMPLOYEE:
System.out.println("ELENCA TUTTI I RECORD:");
ME.listEmployees();
现在我对最后一个类有疑问...在这个类的 main() 方法中,我将我的 DAO 对象声明为接口类型 (HibernateDAO)但我必须使用实现此接口的具体类型(HibernateDAOImpl)来构造它,以这种方式:
HibernateDAO ME = new HibernateDAOImpl();
我会知道这是否是我必须使用的最佳实践,或者是否存在一些更好的方式来说 Java 将其创建为 HibernateDAO 类型,然后自动检测到我只有一个实现并使用的 Java它。
你能给我解释一下吗?
Tnx
安德烈亚
【问题讨论】:
【参考方案1】:首先,我建议将 HibernateDAO 重命名为 EmployeeDAO,然后将具体类命名为 HibernateEmployeeDAO。请记住,接口的目的是隐藏实现细节,以便以后可以更改它们。例如,如果您决定不使用 Hibernate,您只需更改具体类,而当实现不使用 Hibernate 时,如果有一个名为 HibernateDAO 的接口会令人困惑。
Java 无法自动知道您要使用什么实现,因此您需要使用具体的构造函数。将 DAO 声明为接口对于依赖注入很有用。
public class ServiceLogic
private final EmployeeDAO dao;
public ServiceLogic(EmployeeDAO dao)
this.dao = dao;
//some logic code
使用上面的类,如果我想使用 Hibernate,我可以调用 new ServiceLogic(new HibernateEmployeeDao());
;如果我想使用 MyBatis,我可以调用 new ServiceLogic(new MyBatisEmployeeDAO());
。
【讨论】:
【参考方案2】:默认情况下,Java 没有在运行时可用的类注册表。此外,类定义可以在运行时动态加载,甚至从网络加载。所以不,没有办法问 Java “实现接口 X 的具体类是什么?”。
new
运算符后面必须跟一个非抽象类型,因此您的代码是正确的,并且是实例化对象的唯一合法方式。
您还可以使用类注册和反射,即在运行时动态构建对象,而无需在编译时知道具体的子类名称。这种技术要求类名在运行时在配置文件中可用。假设您有以下内容:
<!-- In configuration.xml -->
<database>
<className>org.andrea.myH8.myH8HelloWorld.HibernateDAOImpl</className>
</database>
在您的main()
中,您从配置文件中读取类名,然后执行以下操作:
// Note, this is unchecked
Class<HibernateDAO> daoClass = (Class<HibernateDAO>) Class.forName(name);
HibernateDAO me = daoClass.getConstructor().newInstance();
您还可以通过其他方式构建注册表,例如通过扫描CLASSPATH
,但这当然效率较低并且可能会减慢您的应用程序的启动速度。
【讨论】:
【参考方案3】:这实际上是使用接口但实例化具体类的最佳实践(显然你别无选择)。
这是最终替换实现的最佳方式(假设在不久的将来,您可能想要使用另一个 ORM
而不是 hibernate,因为您的老板决定更改它,因为他阅读了一篇关于一些晦涩框架的文章并且您无话可说(显然,这种情况在现实生活中从未发生过),那么您只需要创建接口的新实现并更改实例化即可。)
接下来您要问的是,如果某个框架可以为您在类路径上选择正确的类实现,尤其是如果您只有一个,那么答案是肯定的:IOC 框架可以做到这一点,而我最了解的:Spring
特别擅长这种东西。
【讨论】:
事实上,我的疑惑来自于一个使用 Spring 的应用程序和一个不使用框架 Tnx 的应用程序之间的比较以上是关于Java中接口使用相关的一些疑惑(创建Hibernate DAO)的主要内容,如果未能解决你的问题,请参考以下文章