简体   繁体   中英

Some doubts related to the interface use in Java (creating an Hibernate DAO)

I am developing a simple sample appication that use Hibernate to implementa a DAO that execute some CRUD operation on a database table.

My application work fine but I have a doubt that probably is connected to a my persona gap in the use of Java.

In my application architecture I have an interface named HibernateDAO in which I only declare the CRUD method that I would have, this one:

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

}

Then I have the concrete class the implement the previouse interface and this class is named HibernateDAOImpl , this one:

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

Ok, omitting the entity class Person, the last class it the MainApp class that contain the main() method that I use to test how this class work:

package org.andrea.myH8.myH8HelloWorld;

public class 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();

}

}

Now I have a doubt about this last class...in the main() method of this class I declare my DAO Object as the interface type ( HibernateDAO ) but I have to construct it using the concrete type that implement this interface ( HibernateDAOImpl ), in this way:

HibernateDAO ME = new HibernateDAOImpl();

I would know if this is the best practice that I have to use or if exist some better way to say Java to create it as HibernateDAO type and then, automatically, is Java that automatically detect that I have only an implementation and use it.

Can you explain me this thing?

Tnx

Andrea

Firstly, I'd recommend renaming HibernateDAO to something like EmployeeDAO then naming the concrete class HibernateEmployeeDAO. Remember that the point of an interface is to hide implementation details away so they can be changed later. For example, if you decided not to use Hibernate you'd only change the concrete class and it would be confusing to have an interface called HibernateDAO when the implementation doesn't use Hibernate.

Java can't automatically know what implementation you want to use so you will need to use a concrete constructor. Declaring the DAO as the interface is useful for dependency inject.

public class ServiceLogic {
    private final EmployeeDAO dao;
    public ServiceLogic(EmployeeDAO dao){
        this.dao = dao;
    }
    //some logic code
}

Using the class above I could call new ServiceLogic(new HibernateEmployeeDao()); if i wanted to use Hibernate or new ServiceLogic(new MyBatisEmployeeDAO()); if I wanted to use MyBatis.

This is actually the best practice to use the interface but instantiate the concrete class (obviously you have no choice there). This is the best way to go in order to eventually replace the implementation (let's say in the very near future you might want to use another ORM than hibernate because your boss decided to change it because he read an article about some obscure framework and you have no word to say (this case, obviously, never happen in real life), then you just have to create a new implementation of the interface and change the instantiation and you're done.)

What you are asking next is that if some framework can pick for you the right implementation of the class on your classpath, especially if you only have one, then the answer is yes : IOC framework can do that and the one that I know the best : Spring is particularly good at this kind of stuff.

Java by default doesn't have a registry of classes available at runtime. Beside, classes definition may be dinamically loaded at runtime, even from the network. So no, there's no way to ask Java "what are the concrete classes that implement the interface X?" .

The new operator must be followed by a non-abstract type, so your code is correct and is the only legal way to instantiate an object.

You could also use a class registry and reflection, ie dynamically build an object at runtime without knowing the concrete subclass name at compile time. This technique requires the class name to be availabe in a configuration file at runtime. Suppose you have the following:

<!-- In configuration.xml -->
<database>
  <className>org.andrea.myH8.myH8HelloWorld.HibernateDAOImpl</className>
</database>

Inside your main() you read the class name from the configuration file, and then do something like:

// Note, this is unchecked
Class<HibernateDAO> daoClass = (Class<HibernateDAO>) Class.forName(name);
HibernateDAO me = daoClass.getConstructor().newInstance();

You can also build the registry in some other way, for example by scanning the CLASSPATH , but this is of course less efficient and may slow down the startup of your app.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM