Buscar
Social
Ofertas laborales ES
« Publicado jasypt 1.9 (java simplified encryption) | Main | OpenMeetings, software de videoconferencia open source »
viernes
dic162011

JEPLayer ORM 1.0 : transacciones JTA y API fluida

He publicado JEPLayer v1.0

JEPLayer es un sencillo ORM sobre JDBC y en esta versión también sobre la Java Transacion API (JTA). JEPLayer es de código abierto con licencia Apache v2

http://jeplayer.googlecode.com

JEPLayer es una herramienta de más bajo nivel que los ORMs de persistencia transparente tal y como Hibernate, JPA o JDO, JEPLayer responde a la necesidad de evitar las tareas más tediosas del uso de JDBC y/o JTA pero sin perder control alguno de las acciones de persistencia tal y como ocurre con los frameworks de alto nivel de persistencia transparente y sin reinventar la rueda como hacen otras herramientas redefiniendo la API JDBC y JTA. JEPLayer no substituye completamente a estas APIs tal que en aquellas acciones en donde JEPLayer no aporta nada respecto a la API JDBC/JTA, el programador puede cuando quiera acceder a los objetos JDBC/JTA que son protagonistas en cada etapa del ciclo de vida de una acción persistente (Connection,PreparedStatement,ResultSet,UserTransaction).

JEPLayer hace un gran énfasis en simplificar de forma extrema la demarcación de transaccione, tanto transacciones JDBC como JTA, en el caso de JTA utilizando la misma semántica que en EJB (o Spring), pero evitando la brutal invasión y viralidad que requieren otras alternativas para simplemente declarar que un conjunto de operaciones persistentes requieren un transacción (el típico REQUIRED).

Como novedades en esta versión se incluye el soporte de transacciones distribuidas JTA incluyendo la transaccionalidad para varias bases de datos (DataSource), falsas transacciones JTA basadas en puro JDBC con la misma semántica que EJB o Spring (aunque incompleta), una API fluida para queries similar a la de JPA incluyendo el uso de parámetros numerados o con nombres y binding automático (configurable) de campos en base de datos y atributos en beans.

 

En javaLobby se pueden consultar ejemplos de uso del API de JEPLayer v0.5 (en la versión en javaHispano no se ven bien los ejemplos al haberse portado la noticia al nuevo portal).

En general la API ha sufrido bastantes cambios en la v1.0, ha continuación mostraré algunos ejemplos de código en donde se muestra la nueva sintaxis, se supone MySQL en el caso de incluir SQL no estándar.

Por ejemplo dada una clase Contact:

public class Contact

{

    protected int id;

    protected String name;

    protected String phone;

    protected String email;

     public Contact(int id, String name, String phone, String email)

    {

        this.id = id;

        this.name = name;

        this.phone = phone;

        this.email = email;

    }

    public Contact()

    {

    }

  /* ... gets and sets ... */

}

 

 

Este es un ejemplo de creación de la tabla (DataSourceFactory es cualquier clase factoría a medida que obtenga un DataSource):

public class CreateDBModel

{

    public static void main(String[] args)

    {

        DataSourceFactory dsFactory = ...;

        DataSource ds = dsFactory.getDataSource();

        JEPLBootNonJTA boot = JEPLBootRoot.get().createJEPLBootNonJTA();

        JEPLNonJTADataSource jds = boot.createJEPLNonJTADataSource(ds);

         try

        {

            JEPLDAL dal = jds.createJEPLDAL();

            dal.createJEPLDALQuery("DROP TABLE IF EXISTS CONTACT").executeUpdate();

             dal.createJEPLDALQuery(

                "CREATE TABLE  CONTACT (" +

                "  ID INT NOT NULL AUTO_INCREMENT," +

                "  EMAIL VARCHAR(255) NOT NULL," +

                "  NAME VARCHAR(255) NOT NULL," +

                "  PHONE VARCHAR(255) NOT NULL," +

                "  PRIMARY KEY (ID)" +

                ")" +

                "ENGINE=InnoDB"

                ).executeUpdate();

        }

        finally

        {

            dsFactory.destroy();

        }

    }

}

La clase DAO correspondiente (algunos métodos son redundantes):

public class ContactDAO implements JEPLResultSetDAOListener<Contact>

{

    protected JEPLDAO<Contact> dao;

   

    public ContactDAO(JEPLDataSource ds)

    {

        this.dao = ds.createJEPLDAO(Contact.class);

        dao.addJEPLListener(this);

    }

 

    public JEPLDAO<Contact> getJEPLDAO()

    {

        return dao;

    }

 

    @Override

    public void setupJEPLResultSet(JEPLResultSet jrs,JEPLTask task)

        throws Exception

    {

    }

 

    @Override

    public Contact createObject(JEPLResultSet jrs) throws Exception

    {

        return new Contact();

    }

   

    @Override

    public void fillObject(Contact obj,JEPLResultSet jrs) throws Exception

    {

        ResultSet rs = jrs.getResultSet();

 

        obj.setId(rs.getInt("ID"));

        obj.setName(rs.getString("NAME"));

        obj.setPhone(rs.getString("PHONE"));

        obj.setEmail(rs.getString("EMAIL"));

    }

 

    public void insert(Contact contact)

    {

        int key = dao.createJEPLDALQuery(

               "INSERT INTO CONTACT (EMAIL, NAME, PHONE) VALUES (?, ?, ?)")

                .addParameters(contact.getEmail(),

                       contact.getName(),contact.getPhone())

                .getGeneratedKey(int.class);

        contact.setId(key);

    }

 

    public void update(Contact contact)

    {

        dao.createJEPLDALQuery(

               "UPDATE CONTACT SET EMAIL = ?, NAME = ?, PHONE = ? WHERE ID = ?")

                .addParameters(contact.getEmail(),contact.getName(),

                       contact.getPhone(),contact.getId())

                .setStrictMinRows(1).setStrictMaxRows(1)

                .executeUpdate();

    }

 

    public boolean delete(Contact obj)

    {

        return deleteById(obj.getId());

    }

 

    public boolean deleteById(int id)

    {

        // Only if there is no "inherited" rows or declared ON DELETE CASCADE

        return dao.createJEPLDALQuery("DELETE FROM CONTACT WHERE ID = ?")

                    .setStrictMinRows(0).setStrictMaxRows(1)

                    .addParameter(id)

                    .executeUpdate() > 0;

    }

 

    public int deleteAll()

    {

        // Only if "inherited" tables are empty or declared ON DELETE CASCADE

        return dao.createJEPLDALQuery("DELETE FROM CONTACT").executeUpdate();

    }

 

    public List<Contact> selectAll()

    {

        return dao.createJEPLDAOQuery("SELECT * FROM CONTACT").getResultList();

    }

 

    public JEPLResultSetDAO<Contact> selectAllResultSetDAO()

    {

        return dao.createJEPLDAOQuery(

                       "SELECT * FROM CONTACT").getJEPLResultSetDAO();

    }

 

    public List<Contact> selectJEPLDAOQueryRange(int from,int to)

    {

        return dao.createJEPLDAOQuery("SELECT * FROM CONTACT")

                .setFirstResult(from)

                .setMaxResults(to - from)

                .getResultList();

    }

 

    public Contact selectById(int id)

    {

        return dao.createJEPLDAOQuery("SELECT * FROM CONTACT WHERE ID = ?")

                .addParameter(id)

                .getSingleResult();

    }

 

    public List<Contact> selectByNameAndEMail(String name,String email)

    {

        return dao.createJEPLDAOQuery(

               "SELECT * FROM CONTACT WHERE NAME = ? AND EMAIL = ?")

                .addParameters(name,email)

                .getResultList();

    }

 

    public int selectCount()

    {

        return dao.createJEPLDALQuery("SELECT COUNT(*) FROM CONTACT")

                    .getOneRowFromSingleField(int.class);

    }

}

En la versión 1.0 se puede evitar el binding manual propio de implementar JEPLResultSetDAOListener y registrar una implementación por defecto (JEPLResultSetDAOListenerDefault) que hace el binding automáticamente. En el siguiente método que podemos añadir al DAO se utiliza el binding automático en una query concreta (podría registrarse a nivel global del DAO) 

    public List<Contact> selectAllExplicitResultSetDAOListenerBean()

    {

        JEPLResultSetDAOListenerDefault<Contact> listener =

                dao.getJEPLDataSource()

               .createJEPLResultSetDAOListenerDefault(Contact.class);

 

        return dao.createJEPLDAOQuery("SELECT * FROM CONTACT")

                .addJEPLListener(listener)

                .getResultList();

    }

 JEPLResultSetDAOListenerDefault admite interceptar atributos concretos que no sean mapeables automáticamente con el campo en la base de datos a través de JEPLRowBeanMapper. En este ejemplo es solamente ilustrativo, el mapeo automático funcionaría correctamente:

    public List<Contact> selectAllExplicitResultSetDAOListenerBeanWithMapper()

    {

        JEPLRowBeanMapper<Contact> rowMapper = new JEPLRowBeanMapper<Contact>()

        {

            public boolean setColumnInBean(Contact obj,JEPLResultSet jrs,

                       int col, String columnName, Object value, Method setter)

            {

                if (columnName.equalsIgnoreCase("email"))

                {

                    obj.setEmail((String)value);

                    return true;

                }

                return false;

            }

        };

        JEPLResultSetDAOListenerDefault<Contact> listener =

                dao.getJEPLDataSource()

                .createJEPLResultSetDAOListenerDefault(Contact.class,rowMapper);

 

        return dao.createJEPLDAOQuery("SELECT * FROM CONTACT")

                .addJEPLListener(listener)

                .getResultList();

    }


  Los parámetros de las queries pueden ser ahora también numerados:

    int key = dao.createJEPLDALQuery(

                "INSERT INTO CONTACT (EMAIL, NAME, PHONE) VALUES (?1, ?2, ?3)")

            .setParameter(1,contact.getEmail())

            .setParameter(2,contact.getName())

            .setParameter(3,contact.getPhone())

            .getGeneratedKey(int.class);

O con nombres (como en JPA):

    int key = dao.createJEPLDALQuery(

        "INSERT INTO CONTACT (EMAIL, NAME, PHONE) VALUES (:email,:name,:phone)")

            .setParameter("email",contact.getEmail())

            .setParameter("name",contact.getName())

            .setParameter("phone",contact.getPhone())

            .getGeneratedKey(int.class);

Se mantiene las técnicas de IoC a lo largo del ciclo de vida de cada acción persistente a través de listeners: JEPLConnectionListener, JEPLPreparedStatementListener, JEPLResultSetDALListener y JEPLResultSetDAOListener  (ver ejemplos en javaLobby y en manual).

Sigue existiendo aunque ahora usando la API fluida la posibilidad de consultar con un ResultSet especial pero ORM:

    public JEPLResultSetDAO<Contact> selectAllResultSetDAO() // Método de ContactDAO
    {
        return dao.createJEPLDAOQuery(
               "SELECT * FROM CONTACT").getJEPLResultSetDAO();
    }

    ContactDAO dao = ...;

    JEPLResultSetDAO<Contact> resSetDAO = dao.selectAllResultSetDAO();

    if (resSetDAO.isClosed()) throw new RuntimeException("UNEXPECTED");

    while(resSetDAO.next())

    {

        Contact contact = resSetDAO.getObject();

        System.out.println("Contact: " + contact.getName());

    }

    // Now we know is closed 

    if (!resSetDAO.isClosed()) throw new RuntimeException("UNEXPECTED");

 

Que al implementar la interfaz List podemos verlo como un List que carga objectos bajo demanda de la base de datos a medida que lo recorremos:


    ContactDAO dao = ...;

    List<Contact> resSetDAO = dao.selectAllResultSetDAO();

    if (((JEPLResultSetDAO)resSetDAO).isClosed())

        throw new RuntimeException("UNEXPECTED");

    for(Contact contact : resSetDAO) // Uses Iterator<Contact> 

    {

        System.out.println("Contact: " + contact.getName());

    }

    // Now we know is closed 

    if (!((JEPLResultSetDAO)resSetDAO).isClosed())

        throw new RuntimeException("UNEXPECTED");

 

Es posible que no necesitemos toda la parafernalia de DAOs, beans etc y simplemente queremos extraer datos a medida de la forma más simple posible sin todo el tedio de abrir y cerrar conexiones etc, para ello tenemos los objetos JEPLCachedResultSet que recogen los resultados y pueden ser recorridos como un ResultSet pero desconectado (suponemos dos filas de resultado):

 

    JEPLDataSource jds = ...;

    JEPLDAL dal = jds.createJEPLDAL();

    JEPLCachedResultSet resSet = dal.createJEPLDALQuery(

                "SELECT COUNT(*) AS CO,AVG(ID) AS AV FROM CONTACT")

                .getJEPLCachedResultSet();

 

    String[] colNames = resSet.getColumnLabels();

    if (colNames.length != 2) throw new RuntimeException("UNEXPECTED");

    if (!colNames[0].equals("CO")) throw new RuntimeException("UNEXPECTED");

    if (!colNames[1].equals("AV")) throw new RuntimeException("UNEXPECTED");

    if (resSet.size() != 2) throw new RuntimeException("UNEXPECTED");

 

    int count = resSet.getValue(1, 1, int.class); // Row 1, column 1 

    count = resSet.getValue(1, "CO", int.class);

 

    int avg = resSet.getValue(1, 2, int.class); // Row 1, column 2 

    avg = resSet.getValue(1, "AV", int.class);

  

 

Respecto a transaccione, podemos como siempre agrupar llamadas persistentes en la misma transacción:

 

    final JEPLDataSource jds = ...;

    JEPLTask<Contact> task = new JEPLTask<Contact>()

    {

        @Override

        public Contact exec() throws Exception

        {

            Contact contact = new Contact();

            contact.setName("A Contact object");

            contact.setPhone("9999999");

            contact.setEmail("contact@world.com");

            ContactDAO dao = new ContactDAO(jds);

            dao.insert(contact);

 

            Contact contact2 = dao.selectById(contact.getId());

            return contact2;

        }

    };

    Contact contact = jds.exec(task);

 

Aunque ahora se distinguen transacciones JDBC (non-JTA) de transacciones JTA. Ejemplo JDBC:

 

    JEPLNonJTADataSource jds = boot.createJEPLNonJTADataSource(ds);

    jds.setDefaultAutoCommit(false);

    ...

    JEPLTask<Contact> task = new JEPLTask<Contact>()

    {

        @Override

        public Contact exec() throws Exception

        {

             ...

        }

    };

    Contact contact = jds.exec(task);

 

la transaccionalidad  puede también declararse a través de una anotación:

 

    JEPLNonJTADataSource jds = ...;

    ...

    JEPLTask<Contact> task = new JEPLTask<Contact>()

    {

        @Override

        @JEPLTransactionalNonJTA

        public Contact exec() throws Exception

        {

            ... 

        }

    };

    Contact contact = jds.exec(task);

 

O a través de un parámetro autoCommit (este ejemplo no es transaccional porque autoCommit es true):

    JEPLNonJTADataSource jds = ...;

    ...

    JEPLTask<Contact> task = new JEPLTask<Contact>()

    {

        @Override

        public Contact exec() throws Exception

        {

            ... 

        }

    };

    Contact contact = jds.exec(task,true); 

 

En esta versión podemos declarar transacciones JTA

    UserTransaction txn = ...;

    TransactionManager txnMgr = ...;

    DataSource ds = ...;

 

    JEPLBootJTA boot = JEPLBootRoot.get().createJEPLBootJTA();

    boot.setUserTransaction(txn);

    boot.setTransactionManager(txnMgr);

 

    JEPLJTADataSource jds = boot.createJEPLJTADataSource(ds);

    jds.setDefaultJEPLTransactionPropagation(

               JEPLTransactionPropagation.REQUIRED);

    ...

 

    JEPLTask<Contact> task = new JEPLTask<Contact>()

    {

        @Override

        public Contact exec() throws Exception

        {

            return ...; // Database actions 

        }

    };

    Contact contact = jds.exec(task);

 

También con una anotación:

 

    ...

 

    JEPLTask<Contact> task = new JEPLTask<Contact>()

    {

        @Override

        @JEPLTransactionalJTA 

        public Contact exec() throws Exception

        {

            return ...; // Database actions 

        }

    };

    Contact contact = jds.exec(task);

 

O con un parámetro en cada caso:

   JEPLTask<Contact> task = new JEPLTask<Contact>()

   {

     @Override

     public Contact exec() throws Exception

     {

         return ...; // Database actions 

     }

   };

   Contact contact = jds.exec(task,JEPLTransactionPropagation.REQUIRED);

 

Con la posibilidad de poder definir de la misma forma (configuración global, anotación, parámetro en cada llamada) transacciones distribuidas que afecten a varias bases de datos y por tanto utilizando two-phase commit:

    UserTransaction txn = ...;

    TransactionManager txnMgr = ...;

    DataSource ds1 = ...;

    DataSource ds2 = ...;

 

    JEPLBootJTA boot = JEPLBootRoot.get().createJEPLBootJTA();

    boot.setUserTransaction(txn);

    boot.setTransactionManager(txnMgr);

 

    JEPLJTAMultipleDataSource jdsMgr = boot.getJEPLJTAMultipleDataSource();

    final JEPLJTADataSource jds1 = boot.createJEPLJTADataSource(ds1);

    final JEPLJTADataSource jds2 = boot.createJEPLJTADataSource(ds2);

 

    JEPLTask<Contact> task = new JEPLTask<Contact>()

    {

        @Override

        public Contact exec() throws Exception

        {

            // Database actions using jds1 and jds2 

            return ...;

        }

    };

    jdsMgr.exec(task);

 

Utilizando la misma semántica transaccional aplicada a un DataSource pero en este caso a todos. Por ejemplo:

  JEPLJTAMultipleDataSource jdsMgr = boot.get JEPLJTAMultipleDataSource();

  jdsMgr.setDefaultJEPLTransactionPropagation(

           JEPLTransactionPropagation.REQUIRED);

 

Idem anotación y parámetro en exec de  JEPLJTAMultipleDataSource

Que lo disfruteis.



    public JEPLResultSetDAO<Contact> selectAllResultSetDAO()
    {
        return dao.createJEPLDAOQuery(
               "SELECT * FROM CONTACT").getJEPLResultSetDAO();
    }

PrintView Printer Friendly Version

EmailEmail Article to Friend

Reader Comments (15)

porque insisiten en usar daos cuando el pattern para esto es el Repository?

diciembre 19, 2011 | Unregistered Commenterdario

Antes que nada enhorabuena por el proyecto.

No se si podré echarle un vistazo pero me gustaría preguntar no obstante las diferencias principales con MyBatis, por si me puedes responder. Y si es posible cual es la razón que te ha movido a hacer otro ORM mas, es decir, que echas en falta en los demás ya existentes.

Pregunto esto porque mas bien soy excéptico con los ORM's que implican modelado, entiéndase JPA y similares (aunque dicen que JDO es bastante superior a JPA). Leí un poco sobre MyBatis y en principio me gusta mas la idea a tal punto que la voy a estudiar mas detenidamente para adoptarla en mis desarrollos.

Saludos.

diciembre 20, 2011 | Registered CommenterAntonio Sánchez

dario: ¿podrías detallar un poco mas tu postura?

Saludos.

diciembre 20, 2011 | Registered CommenterAntonio Sánchez

qué parrafada de código!!
todo eso hay que escribirlo para cada clase??

diciembre 20, 2011 | Unregistered Commenterpsi

En mi opinión MyBatis reinventa la rueda, es decir, reescribe totalmente la API JDBC y de una manera declarativa y la programación declarativa casi siempre es la imposición de rigideces persiguiendo una "supuesta elegancia" que es la programación a base de anotaciones y XMLs, por no hablar de que una consulta en una anotación o en un XML queda simpática pero la realidad es casi siempre tozuda y casi siempre al final se acaba necesitando construir por programación de forma imperativa las consultas y resulta que en el mundo XML por ejemplo se tiene que reinventar incluso el if para conseguir hacer estas cosas, para mi es un absurdo para gustos colores.

La programación declarativa está bien si existe una programación imperativa alternativa, como verás en JEPLayer hay un poquito de programación declarativa (anotaciones de transacciones) pero SIEMPRE hay programación imperativa alternativa.

diciembre 20, 2011 | Registered Commenterjmarranz

psi ya dicen los expertos "no existen las balas de plata"

no seas malo la mejor herramienta para el problema indicado simplemente no vas a hacer todo eso para todas las tablas de tu base de datos solo lo haces cuando tenga sentido por ejemplo cuando quieras trabajar con consultas largas y complicadas que se resuelvan de manera fácil con un simplemente sql de toda la vida

si par tu problema no te ayuda mucho simplemente no la usas y buscas otra opción

la realidad de las super herramientas es que sirven y bastantes pero a la hora de la configurar nadie la ase bien la cubra de aprendizaje es casi eterna y tienes que concomer mucho para tunearla y saber que hace por debajo con herramientas de bajo nivel no neceistas ser un genio para saber que hace por que todo lo haces a mano es decir que si una consulta es lenta pues puedes simplemente cambiar el sql que esta escrito en el mismo código fuente que todo desarrollado que se respete conoce sql pero en cambio con jpa hibernate o lo que sea pues es mas difícil por que tienes que conocer a fondo para saber como te crea las consultas ademas de instalar barias herramientas para ver el sql generado

a propósito querydsl tiene código mas limpio y es mas fácil de usar XD "jmarraz por favor el flame"

diciembre 20, 2011 | Unregistered Commenterluis

Pues yo uso MyBatis para algunos de mis proyectos y no tengo que construir en ningún caso los SQL de forma imperativa.

Con una media de 30~70 lineas de SQL por query, poder hacerlo en un fichero externo en vez de a base de concatenar String es un alivio, además de facilitar el desarrollo, ya que puedo probarlos con un simple copy/paste a la consola de SQL, y un search/replace de los caracteres de escape diferentes.

Las opciones de meter condicionales y cosas extrañas en la construcción de los queries lo uso habitualmente muy poco, y aunque la sintaxis es subjetivamente más fea que en código, la complejidad, IMHO, es la misma.

Anotaciones, las justas. Lo que si utilizo es la posibilidad de reutilizar clases con distintos queries y posibilidades de herencia etc., especialmente en uno de los proyectos que tengo donde me ahorra mucho código repetido y el código Java ni se entera.

Y todo esto simplemente lo digo como mi opinión personal, sin ánimo de convencer a nadie, pero para aclarar que, para mí y en algunos proyectos, MyBatis no es el infierno en la tierra, si no al contrario: una opción bastante agradable para trabajar. En otros proyectos no lo uso y ni se me ocurriría.

Volviendo al tópic, a mí lo que nunca me ha gustado es compilar código fuente para "supuestamente compilar" sentencias SQL que en realidad no se compilan y atar el código y las sentencias juntas. En las aplicaciones donde acabo usando Ibatis/MyBatis, normalmente el código fuente y las sentencias tienen ciclos diferentes y poder tratarlos de forma independiente es una gran ventaja para mí, ya que puedo, por ejemplo, modificar las sentencias SQL en tiempo de ejecución o replicar lo que se está ejecutando de forma sencilla. Tener que compilar cada vez el .java y actualizar los .class por haber cambiado un SQL dentro de una cadena me parece... primitivo e innecesario.

Para cosas más simples, con un wrapper de conexiones/transacciones sobre JDBC voy listo, evitando dependencias y mágia externa.

En todo caso, como ya dice jmarranz, para gustos colores y por lo tanto, mientras funcione, discutir sobre colores es del genero tonto.

diciembre 21, 2011 | Unregistered CommenterVerdoso

Para mí es uno de los inconvenientes de los ORM clásicos (con modelado) el que a la hora de la verdad, que es siempre, cuando se pone la cosa difícil, que es siempre, es bastante complicado hacerlo funcionar como uno quiere. Efectivamente hay que meterse en las entrañas de la criatura y dedicar horas y horas para ver como se arregla el dichoso problemilla que la configuración estándar no te proporciona. Para cosas básicas y elementales si vale, y si se pudiera compartir el uso de distintas tecnologías (transacciones sobre todo o básicamente eso) sería genial, la morralla con JPA por ejemplo y las cosas serias que siempre aparecen y abundantemente con MyBatis, JDBC o lo que se tercie. No se si esta coexistencia es posible, creo que no, pero tampoco estoy muy puesto, creo que sería genial, quizás se pudiera hacer algo con Spring, quizás con JE6, no tengo idea.

Lo que personalmente no me gusta es construir consultas con código Java porque para eso ya está SQL, y por eso me gusta MyBatis. El lenguaje para base de datos es SQL, es lo mas natural, estándar y potente, Java es para otras cosas, y me siento muy incómodo haciendo consultas en Java. Lo entendería quizás en situaciones muy específicas, que se dieran ocasionalmente, o si un requisito importante es la independencia absoluta de base de datos, y en este caso lo ideal sería hacer pruebas unitarias de SQL en base de datos, que imagino debe de haber herramientas (creo que las hay), y solo habría que dedicar el suficiente tiempo inicial para la infraestructura de pruebas, que en realidad no sería mucho (como JUnit o un poco mas, pero no mucho), así que si se cambia de base de datos, que tampoco creo que se haga a cada hora precisamente, solo habría que correr las pruebas unitarias y adaptar lo que hubiera que adaptar. Por supuesto SQL siempre podrá se mas eficiente que lo que plantee el motor del ORM en cuestión, especialmente es consultas complicadas. En fin, que esto también es de gustos y estilos, y yo me siento cómodo con SQL, es lo mas eficaz, eficiente y productivo, al menos para mí.

diciembre 21, 2011 | Registered CommenterAntonio Sánchez

Verdoso: ¿Cual es tu discriminante para utilizar o no MyBatis en un proyecto? ¿Y que otra tecnología utilizarías y por qué?

Saludos.

diciembre 21, 2011 | Registered CommenterAntonio Sánchez

La discriminación suele ser bastante sencilla. Si el modelo de tablas y de datos son bastante calcaditos y las consultas se basan en la navegacion entre relaciones y/o discriminantes sobre pocas entidades, entonces el mapeo y las consultas es algo que un ORM puede hacer practicamente sólo y escribiendolo a mano no ganas practicamente nada, así que hacerlo con *Batis me parece tedioso e innecesario.

En cuanto a alternativa, como indica la propia respuesta, en caso de no usar *Batis uso habitualmente JPA. Eso sí, hay que tener cuidado con los "eager fetch", que los carga el diablo, y aprovechar cosas como los INNER JOIN de JPQL para evitar problemas de consultas N+1 etc. JDO sería otra opción, pero no he necesitado almacenar cosas fuera de BDD y no he acabado usándolo nunca... quizá algún día.

En ambos casos, ya tengo montados, desde hace mucho, wrappers para las partes tediosas de inicialización, control de transacciones, asegurarse de cerrar sesiones etc. así que que esas partes sean verborréicas no me afecta.

Para cosas super-simples donde solo necesitaría SQL a pelo para algo rápido, también tengo wrappers de JDBC a pelo, pero entonces prefiero tirarme a Groovy donde puede simplemente recargar el código dinámicamente y recorrer los resultados etc. es mucho más directo.

Esos son mis colores ahora mismo :)

diciembre 21, 2011 | Unregistered CommenterVerdoso

así que si se cambia de base de datos, que tampoco creo que se haga a cada hora precisamente,

En mi empresa la misma aplicación se instala en cada cliente con una base datos diferente. Es decir, no es que cambiemos la base de datos, es que siempre trabajamos con varias bases de datos. La ventaja para el cliente es que cuando nos compra una aplicación no tiene que cambiar su base de datos corporativa, puede seguir con su AS/400, Informix, PostgreSQL, Oracle, etc. No sólo es que se ahorran licencias, sino que pueden seguir trabajando con las bases de datos que ya conocen y con las que suelen estar encantados.
En este contexto cuanto más SQL pura peor y cuanto más JPA mejor.

diciembre 23, 2011 | Registered Commenterjavierpaniza

@javierpaniza

Bueno, yo lo que decía literalmente, tal como has citado, es que no se cambiará de bbdd cada hora, queriendo decir que no se hará ese cambio a cada momento, o varias veces en la etapa de desarrollo. Otra cosa es que en tiempo de despliegue sí se dé ese requisito, pero el despliegue será una vez por cliente al menos tal como lo cuentas en tu caso.

En tu caso sí cabe una solución sin SQL por defecto, aunque que estaremos de acuerdo que no es el caso prototipo, quizás ni siquiera mayoritario, o hasta ni siquiera frecuente. Y por cierto, ¿que hacéis con las consultas en SQL nativo? ¿Nunca las necesitáis? ¿JPQL os da solución a todo tipo de consultas? ¿Como hacéis las consultas anidadas? En mi experiencia mas pronto que temprano con JPA (al menos JPA) tienes que irte a las consultas nativas, o sí o sí.

En cambio mi experiencia es la de desarrollos a medida, no una solución general, donde desde el principio el cliente también pone su bbdd favorita, con la que efectivamente está encantado y no se quiere cambiar, y quiere tener el mayor rendimiento y a ser posible la mayor funcionalidad de datos a medida, vamos, en SQL. Yo creo que aquí algo como MyBatis gana por goleada. Pero ya te digo que incluso a menudo la arquitectura técnica se ha decidido de tal forma que se ponga la mayor parte de la funcionalidad de datos en la propia bbdd, con vistas, procedimientos almacenados, disparadores, etc... ¡Y no me parece mal! Ni siquiera Batis aplicaría aquí. Y no me disgusta del todo esta práctica, personalmente.

De todas formas lo que yo comentaba de las pruebas unitarias para bbdd tampoco es mala opción para tu caso, ni mucho menos, sabiendo además que el número de bbdd es limitada, es decir que a lo mejor con media docena o poco mas de baterías de pruebas ya vais mas que sobrados.

Saludos.

diciembre 23, 2011 | Registered CommenterAntonio Sánchez

@jmarranz

casi siempre al final se acaba necesitando construir por programación de forma imperativa las consultas y resulta que en el mundo XML por ejemplo se tiene que reinventar incluso el if para conseguir hacer estas cosas

Acabo de repasar MyBatis por encima, y dispone de un SelectBuilder y un SqlBuilder que a fin de cuentas viene a hacer lo mismo que la construcción dinámica mediante código. No obstante personalmente prefiero quedarme con la consulta en el XML siempre que se pueda, MyBatis simplifica mucho el caso de consultas dinámicas en el XML, y sencillamente es que me gusta tenerlo a parte.

Saludos.

diciembre 23, 2011 | Registered CommenterAntonio Sánchez

Hola Antonio,


¿que hacéis con las consultas en SQL nativo? ¿Nunca las necesitáis? ¿JPQL os da solución a todo tipo de consultas?

Sí, claro. A veces es necesario usar consulta SQL nativas por cuestión de rendimiento. Pero la mayoría de los casos se pueden resolver con Java y JPA. La parte nativa es la más difícil de migrar a otras bases de datos, por lo que tratamos de evitarlo.
Muchas veces un mismo problemas tiene varias soluciones posibles y la de una inmensa setencia SQL no es siempre la única. Es una cuestión de abordar el asunto desde otra perspectiva.

diciembre 26, 2011 | Registered Commenterjavierpaniza

because it helps me in my many works and it is very beneficial for me because it gives the work a professional touch that is really an amazing thing.

self catering Edinburgh ,
Edinburgh self catering ,

diciembre 26, 2011 | Registered Commenterpaull2060

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>