lunes, julio 08, 2013

Generar JPA Entities a partir de tablas

Eclipse cuenta con herramientas muy utiles para trabajar con JPA una de las mas comunes es la llamada: "Dali Java Persistence Tools" que viene integrada en las nuevas versiones de Eclipse, especificamente hablare de la STS 3.3

Una de las funciones que he utilizado en recientes proyectos es la de generar entidades JPA a partir de tablas ya existentes en la BD
, aqui los pasos para hacerlo:

Nota: Esto es un caso de uso, no un tutorial.

1. Configurar la conexion a la BD usando la vista Data Source Explorer

2. Hay que agregar la faceta JPA a nuestro proyecto para poder utilizar las herramientas JPA de Dali, boton derecho sobre el proyecto e ir a Properties --> Project Facets


Como se muestra en la imagen hay que seleccionar el checkbox JPA y despues dar click en el link: "Further configuration required"

Se abrira el siguiente dialogo:
Como se ve en la imagen, primero seleccionas el sabor de JPA a utilizar en este caso 2.1 generico.

El proyecto en el que estoy trabajando es un proyecto Maven-Spring, el cual tiene como dependencia a Hibernate asi que no es necesario decirle a Dali donde esta la libreria JPA pues ya se encuentra en el build path. Es por eso que seleccione: "Disable Library Configuration".

En seguida seleccionas la conexion que previamente configuraste en Eclipse como se indico en el paso 1.

En mi caso selecciono: "Discover annotated classes..." porque mis entidades utilizan la anotacion @Entity y no las defino en el persistence.xml

3. Una vez que el proyecto tiene la Faceta JPA se podra tener acceso al menu contextual: "JPA Tools" al hacer click sobre el proyecto, como se ve en la fig:



Al seleccionar la opcion "Generar Entidades desde Tablas" saldra el siguiente dialogo:


Aqui seleccione mi conexion de BD: "Teradata Local", despues el esquema en el que estan las tablas y finalmente seleccionas las tablas que quieres usar para generar los entities.

Es importante notar que en mi caso deseleccione el checkbox "List generated classes in persistence.xml" ya que como lo mencione anteriormente estoy usando el enfoque de anotaciones @Entity.

Si haces click en Next, apareceran mas opciones entre ellas decirle como generar las llaves, seleccionar los campos si no requieres que los genere todos, etc. Una opcion que es importante es la de definir en que paquete pondra los entities generados:



Al dar click en el boton Finish se crearan los entities en el paquete definido.


miércoles, julio 03, 2013

Como renombrar proyecto Eclipse Spring

Estos son los pasos para renombrar una aplicacion Spring que se encuentra como proyecto en Eclipse (especificamente STS):

1. Con boton derecho; Refactor --> Rename, sobre el proyecto
2. Refactor --> Rename en los paquetes
3. Editar el archivo web.xml y cambiar todas las ocurrencias del viejo nombre por el nuevo
4. Editar application-context.xml y cambiar todas las ocurrencias del viejo nombre por el nuevo
5. Editar webmvc-config.xml y cambiar todas las ocurrencias del viejo nombre por el nuevo
6. Editar org.eclipse.wst.common.component (este es importante porque de aqui sale el nombre con que se publica la aplicacion en el tc server) y cambiar todas las ocurrencias del viejo nombre por el nuevo
7. Finalmente cambiar todas las demas configuraciones en las que uses el nombre de la aplicacion, p. ej. log4j.properties

lunes, febrero 11, 2013

Como construir el url jdbc para una BD DB2-AS/400

La base de datos relacional AS/400 tiene sus peculiaridades, a diferencia de otras BD que se instalan sobre un sistema operativo en el servidor o ambiente, DB2-AS/400 viene integrada con este. El sistema es conocido como OS400, iSeries, i5 o simplemente AS/400. Al estar integrada la BD almacena la informacion directamente en el sistema de archivos, una tabla es un archivo fisico, una vista es un archivo logico, un esquema es una libreria, etc.

Estos detalles hacen que los url de conexion cuando usamos JDBC tengan su sintaxis especifica, por ejemplo si queremos que el datasource se conecte a un esquema por default la sintaxis es la siguiente:

jdbc:as400:<host>:naming=sql;libraries=<lib1>,<lib2>,...

Al especificar naming=sql se indica que se usaran "." para separar los nombres de los objetos, si se utiliza naming=system entonces se debera utilizar "/" para separar los nombres de objetos.

El url anterior tomara la primera libreria como el esquema por default, ignorando los demas para la busqueda de objectos (tablas, vistas, etc), si lo que se desea es usar varias librerias a la vez se tiene que usar un url como el siguiente:

jdbc:as400:<host>:naming=sql;libraries=,<lib1>,<lib2>,...

Es importante la coma al inicio de libraries, esto indica que no hay una libreria por default y cargara todas las librerias en la conexion.

Otra forma es utilizar un wildcard de la siguiente manera:

jdbc:as400:<host>:naming=sql;libraries=*LIBL,<lib1>,<lib2>,... 

A diferencia del anterior, este ultimo tomara como default schema la segunda en la lista en este caso lib1, pero a la vez cargara las demas librerias en la conexion.

Espero que sea de utilidad :)


jueves, enero 24, 2013

Removiendo dependencias Maven no necesarias, de proyecto Spring generado con ROO

En mi actual proyecto tenemos una aplicacion que fue generada con Spring Roo, despues removimos Rooya que no planeamos usarlo como framework. El proyecto es en si un REST API por lo cual no necesitamos tener vistas, utiliza Spring MVC para asociar los controladores con los REST services usando las caracteristicas out of the box de Spring MVC.

Sin embargo al momento de crear el proyecto en Roo viene toda la serie de dependencias que se usarian en una aplicacion estandar web con UI, en mi caso la aplicacion ocupaba 48.6 MB de espacio en disco. Decidi dar una limpieza a las dependencias maven, para conseguir finalmente un espacio en disco de 34 MB osea 14.6 MB de diferencia.

Estoy usando Spring 3.1.1, Spring Roo 1.2.2

Una herramienta util para localizar las dependencias es el comando:  

mvn dependency:tree

Este comando inspecciona el proyecto y construye un arbol de dependencias de los jars configurados el el pom.xml, al ejecutarlo se ve algo como lo siguiente:

[INFO] +- junit:junit:jar:4.10:test
[INFO] |  \- org.hamcrest:hamcrest-core:jar:1.1:test
[INFO] +- log4j:log4j:jar:1.2.16:compile
[INFO] +- org.slf4j:slf4j-api:jar:1.6.4:compile
[INFO] +- org.slf4j:jcl-over-slf4j:jar:1.6.4:compile
[INFO] +- org.slf4j:slf4j-log4j12:jar:1.6.4:compile
[INFO] +- org.aspectj:aspectjrt:jar:1.7.0.RC1:compile
[INFO] +- org.aspectj:aspectjweaver:jar:1.7.0.RC1:compile
[INFO] +- javax.servlet:servlet-api:jar:2.5:provided
...
[INFO] +- org.apache.tiles:tiles-jsp:jar:2.2.2:compile
[INFO] |  +- org.apache.tiles:tiles-servlet:jar:2.2.2:compile
[INFO] |  |  \- org.apache.tiles:tiles-core:jar:2.2.2:compile
[INFO] |  \- org.apache.tiles:tiles-template:jar:2.2.2:compile
[INFO] |     \- org.apache.tiles:tiles-api:jar:2.2.2:compile
[INFO] \- org.codehaus.jackson:jackson-mapper-asl:jar:1.9.11:compile
[INFO]    \- org.codehaus.jackson:jackson-core-asl:jar:1.9.11:compile

Las dependencias que encontre que en mi caso no son requeridas para crear un REST API y que removi de mi pom son las siguientes:

POM.xml

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>${aspectj.version}</version>
        </dependency>

        <dependency>
            <groupId>net.sf.flexjson</groupId>
            <artifactId>flexjson</artifactId>
            <version>2.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.roo</groupId>
            <artifactId>org.springframework.roo.annotations</artifactId>
            <version>${roo.version}</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.webflow</groupId>
            <artifactId>spring-js-resources</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet.jsp.jstl</groupId>
            <artifactId>jstl-api</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.web</groupId>
            <artifactId>jstl-impl</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>javax.el</groupId>
            <artifactId>el-api</artifactId>
            <version>1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>1.6</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.1</version>
            <scope>provided</scope>
        </dependency>

    <dependency>
            <groupId>org.apache.tiles</groupId>
            <artifactId>tiles-jsp</artifactId>
            <version>2.2.2</version>
        </dependency>

Ademas removi el siguiente plugin:

    <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>aspectj-maven-plugin</artifactId>
    ...

Tambien hay que editar el archivo webmvc-config.xml y quitar la configuracion del view resolver que apunta a tiles:

webmvc-config.xml

<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver" id="tilesViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/>
  </bean>
    <bean class="org.springframework.web.servlet.view.tiles2.TilesConfigurer" id="tilesConfigurer">
    <property name="definitions">
      <list>
        <value>/WEB-INF/layouts/layouts.xml</value>
<!--        Scan views directory for Tiles configurations -->
        <value>/WEB-INF/views/**/views.xml</value>
      </list>
    </property>
  </bean>

 Ademas ya no es necesario usar aspectj para el manejo de transacciones, asi que podemos remover mode="aspectj" en el archivo: applicationContext.xml de la siguiente linea de codigo:

applicationContext.xml

<t:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>

Quedando de la siguiente manera:

 <tx:annotation-driven transaction-manager="transactionManager"/>

Espero que esto sea de utilidad a otras personas con un caso similar.




miércoles, enero 16, 2013

Spring MVC / Jackson : como ignorar bean metadata y otras propiedades

Estaba tratando de usar jackson en mi proyecto actual con Spring MVC 3, tengo un metodo en el controlador parecido al siguiente:

@Controller
@RequestMapping("/usuario")
public class UsuarioController {

    @Autowired
    private GenericResponse genericResponse;

    @RequestMapping(value = "/{nombre}", method = GET)
    public @ResponseBody GenericResponse regresaDatos(@PathVariable String nombre) {
        try {
            genericResponse.setData(servicio.getDatos(nombre));
        }
       
        catch(Exception e) {
            genericResponse.setMessage("Error al obtener datos");
        }
       
        return genericResponse;
    }

La clase GenericResponse

public class GenericResponse {
    private String message;
    private IWrapper data;
   ...
    setters y getters aqui
    ...
}

Cuando utilizas Jackson con spring mvc y la anotacion @ResponseBody en el retorno de un metodo, jackson convertira el retorno del metodo a JSON

El problema es que el objeto genericResponse es un proxy bean con scope request:

    <bean id="genericResponse" class="com.mycompany.common.GenericResponse" scope="request">
        <aop:scoped-proxy />
    </bean>

Esto implica que Spring agrega al proxy infinidad de propiedades y metadatos que Jackson intenta convertir a JSON tambien, como eso no es lo que quiero me puse a investigar como evitar que Jackson convierta las propiedades no deseadas.

La solucion es agregar las siguientes anotaciones en la clase GenericResponse:

@JsonIgnoreProperties({"proxyTargetClass", "exposeProxy", "frozen", "preFiltered"})
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE)
public class ErrorResponse {
    private String message;
    private IWrapper data;

    @JsonProperty
    public String getMessage() {
        return message;
    }
   
    public void setMessage(String message) {
        this.message = message;
    }


    @JsonProperty    
    public IWrapper getData() {
        return data;
    }
   
    public void setData(IWrapper data) {
        this.data = data;
    }
}

Con esto Jackson convierte el objeto a JSON de la manera esperada.