Herramientas de usuario

Herramientas del sitio


patrones:excepciones

Diferencias

Muestra las diferencias entre dos versiones de la página.


patrones:excepciones [2023/04/07 21:26] (actual) – creado - editor externo 127.0.0.1
Línea 1: Línea 1:
 +====== Tratamiento de Excepciones ======
 +El tratamiento de excepciones en Java demasiadas veces se realiza de forma errónea. Veamos ahora la forma de hacer un correcto tratamiento de excepciones.
  
 +===== Excepciones en Java =====
 +Empecemos recordando los dos tipos de excepciones que hay en Java(( Hay realmente mas como <javadoc jdk7 >java.lang.Error</javadoc> pero que no trataremos aqui ))
 +  * [[#Excepciones Checked]]: Objetos de la clase <javadoc jdk7>java.lang.Exception|Exception</javadoc> o cualquier otra clase que hereda de ella, excepto si heredan de <javadoc jdk7>java.lang.RuntimeException|RuntimeException</javadoc>.
 +  * [[#Excepciones Unchecked]]: Objetos de la clase <javadoc jdk7>java.lang.RuntimeException|RuntimeException</javadoc> o de cualquiera otra clase que herede de ella.
 +
 +<uml>
 +note "Checked" as N1
 +note "Unchecked" as N2
 +class Exception 
 +class RuntimeException 
 +
 +Exception . N1
 +
 +Exception <|-- RuntimeException
 +
 +RuntimeException . N2
 +</uml>
 +==== Excepciones Checked ====
 +Las excepciones Checked son aquellas que deben declararse en el método mediante la palabra ''throws'' y que obligan al que lo llama a hacer un tratamiento de dicha excepción.
 +
 +Son excepciones Checked cualquier objeto de la clase <javadoc jdk7>java.lang.Exception|Exception</javadoc> o de cualquier otra clase que herede de ella, excepto si el objeto es de la clase <javadoc jdk7>java.lang.RuntimeException|RuntimeException</javadoc> o cualquier otra clase que herede de ésta última.
 +
 +<code java 1>
 +public class Matematicas {
 +    public double dividir(double a, double b) throws Exception {
 +        if (b == 0) {
 +            throw new Exception("El argumento b no puede ser 0");
 +        }
 +
 +        return a / b;
 +    }
 +}
 +</code>
 +
 +El método ''dividir'' lanza en la línea 4 una Checked Exception por lo tanto el método lo declara mediante ''throws''.
 +
 +Ahora cualquiera que llame al método ''dividir'' está obligado a tratar la excepción <javadoc jdk7>java.lang.Exception|Exception</javadoc> de dos formas:
 +  * Mediante un ''try-cath'':<code java 1>
 +public class Main {
 +    public static void main(String[] args) {
 +        Matematicas matematicas=new Matematicas();
 +        try {
 +            double c=matematicas.dividir(-1.6, 0);
 +        } catch (Exception ex) {
 +            //Tratar la excepción
 +        }
 +    }    
 +}
 +</code> En las líneas 4,6,7 y 8, se usa un ''try-catch'' para //atrapar// la excepción que puede producir la llamada el método ''dividir''.
 +
 +  * Declarándo a su vez en el método que puede lanzar dicha excepción:<code java 1>
 +public class Main {
 +    public static void main(String[] args) throws Exception {
 +        Matematicas matematicas=new Matematicas();
 +        
 +        double c=matematicas.dividir(-1.6, 0);
 +    }    
 +}
 +</code> En la línea 2 vemos como se declara´que el método ''main'' puede lanzar una excepción del tipo <javadoc jdk7>java.lang.Exception|Exception</javadoc> ya que ''dividir'' lo lanza y no se atrapa con un ''try-catch''.
 +
 +==== Excepciones Unchecked ====
 +Las excepciones Unchecked son aquellas que **no** se declaran en el método  y que **no** obligan al que lo llama a hacer un tratamiento de dicha excepción.
 +
 +Son excepciones Unchecked  todas aquellas excepciones que heredan de la clase <javadoc jdk7>java.lang.RuntimeException|RuntimeException</javadoc>.
 +
 +<code java 1>
 +public class Matematicas {
 +    public double dividir(double a, double b) {
 +        if (b == 0) {
 +            throw new RuntimeException("El argumento b no puede ser 0");
 +        }
 +
 +        return a / b;
 +    }
 +}
 +</code>
 +
 +El método ''dividir'' lanza en la línea 4 una unchecked Exception por lo que no es necesario hacer nada especial con ella.
 +
 +<code java 1>
 +public class Main {
 +    public static void main(String[] args) {
 +        Matematicas matematicas=new Matematicas();
 +
 +        double c=matematicas.dividir(-1.6, 0);
 +    }    
 +}
 +</code>
 +Como la excepción que lanza ''dividir'' es Unchecked no necesitamos tratarla en el método ''main'' de ninguna forma especial.
 +
 +===== Cuando usar excepciones Checked o Unchecked y que hacer con ellas =====
 +Una vez visto como Java trata las excepciones de ambos tipos pasemos a explicar cuando debemos usar las de un tipo u otro.
 +
 +==== Checked Exceptions ====
 +Las excepciones Checked son condiciones excepcionales del flujo del programa pero que no son debido a un error del propio programa. Es decir si se lanza una excepción checked  el programa no tiene ningún tipo de error y sigue funcionando correctamente, simplemente se ha producido una situación excepcional que hemos //marcado// como una excepción. 
 +
 +Por ejemplo si tenemos una función que borra ficheros y al ir a borrarlo , dicho fichero ya no existe, deberemos lanzar una <javadoc>java.io.FileNotFoundException|FileNotFoundException</javadoc> indicando que el fichero no existe. Que el fichero ya no exista no es ningún error en el programa, puede haberse borrado justo un instante antes , sin embargo,  si que es una situación excepcional porque la mayoría de las veces cuando vayamos a borrarlo deberá estar el fichero si hemos acabado de seleccionarlo. 
 +
 +De ahí que al ser usa situación legitima del programa pero poco probable los creadores de Java han decidido que es necesario declararla en el método y que sea obligatorio tratarla. Las Checked Exceptions no tienen //nada de malo// y son muy útiles para realizar correctamente los programas.
 +
 +<note important>
 +La Checked Exceptions forman parte del flujo lógico de nuestro programa y al programar debemos tenerlas en cuenta y tratarlas adecuadamente no viéndolas como un problema del que debemos deshacernos.
 +</note>
 +
 +<note tip>
 +Aunque hemos puesto el mismo ejemplo del método ''dividir'' tanto para explicar las excepciones Checked o Uncheked tras la explicación debe quedar claro que la excepción que debe lanzar el método ''dividir'' debe ser del tipo unchecked.
 +</note>
 +==== Unchecked Exceptions ====
 +Este tipo de error es mucho mas sencillo de entender. Una excepción unchecked es aquella que se lanza cuando hay un error en el programa. 
 +
 +Por ejemplo en el caso anterior de la división por cero, es un error de programación que alguien nos pase como dividendo un cero, en ese caso lanzaremos un <javadoc>java.lang.RuntimeException|RuntimeException</javadoc>.
 +
 +Al ser las unchecked Exceptions errores en el programa , poco podemos hacer por arreglarlas. Por ello no es obligatorio tratarlas con un ''try-catch''. Lo único que podemos hacer en la mayoría de casos es hacer que el programa acabe fallando y que se muestre un mensaje de error al usuario. Es decir, si el programa funciona mal , es mejor que no siga funcionando antes que hacer que continúe funcionado pero usando datos erróneos, ya que en ese caso los resultados que generara también podrían ser erróneos.
 +
 +<note important>
 +Las unchecked exceptions no debemos tratarlas ya que no forma parte de la lógica de la aplicación y no debemos hacer nada con ella, simplemente debemos preocuparnos en el mensaje que acabará llegando al usuario. En una aplicación Web es tan sencillo como tener páginas de error personalizadas.
 +</note>
 +
 +===== El problema con las excepciones =====
 +Hasta ahora hemos visto los dos tipos de excepciones que hay  , como las trata el compilador de Java y cuando usar una u otra. Sin embargo hay un problema con las excepciones que hace que no se traten de forma correcta.
 +
 +El problema de las excepciones es que en muchas APIs de Java se lanzan checked cuando deberían ser unchecked.
 +
 +Por ejemplo, en el API de JDBC al ejecutar una consulta mediante <javadoc>java.sql.PreparedStatement#executeQuery()|executeQuery()</javadoc> se lanzar la excepción <javadoc>java.sql.SQLException</javadoc> que es de tipo Checked si la SQL es erronea. ¿Acaso eso no es un error de programación y por lo tanto debería ser de tipo unchecked? Es decir que tenemos un problema ya que deberemos tratar excepciones Checked pero no se pueden hacer ningún tratamiento para solucionar lo que generó dicha excepción.
 +
 +<note important>
 +El verdadero problema de Java es que muchas API lanzan Checked exceptions cuando deberían haber sido uncheked exceptions.
 +</note>
 +
 +Como acabamos de decir, eso nos lleva a los programadores a capturar dichas excepciones y tener que tratarlas. Pero ¿como tratamos dichas excepciones Checked si realmente son errores de programa y por lo tanto excepciones unchecked? Pues solo hay una forma de hacerlo, es transformando las excepciones Checked en unchecked. 
 +
 +<code java 1>
 +try {
 +    double c=matematicas.dividir(-1.6, 0);
 +} catch (Exception ex) {
 +    throw new RuntimeException("Fallo al llamar a 'dividir'",ex)
 +}
 +</code>
 +
 +Vemos en la línea 4 como lanzamos una nueva excepción del tipo unchecked pero //dentro// contiene la excepción original que se ha producido ya que se le pasa como argumento. Con ésto hemos solucionado el problema. Pero vuelvo a repetir ésta construcción solo debe hacerse con aquellas excepciones checked que consideramos que deberían haber sido unchecked.
 +
 +<note warning>
 +Esta construcción tan sencilla que consiste en que las excepciones checked **,pero que deberían haber sido uncheked,** trasformarlas en excepciones uncheked nos soluciona prácticamente todos nuestros problemas.
 +
 +<code java 1>
 +try {
 +    double c=matematicas.dividir(-1.6, 0);
 +} catch (Exception ex) {
 +    throw new RuntimeException("Fallo al llamar a 'dividir'",ex)
 +}
 +</code>
 +
 +</note>
 +
 +==== Mas problemas ====
 +Veamos ahora otro problema de las excepciones.Hay excepciones unchecked , que según su significado,  deberían haber sido del tipo checked ya que no son debido a errores del programa (lo contrario del caso anterior).
 +
 +La excepción <javadoc jee6>javax.validation.ConstraintViolationException|ConstraintViolationException</javadoc> se lanza cuando alguna validación de una entidad no se ha cumplido. Se suele utilizar para indicar que el usuario ha introducido algún dato erroneo en la aplicación. Entonces, ¿porqué es una excepción unchecked? Según lo que se está explicando no hay duda de que debería ser del tipo Checked ya que no se lanza por un error de programación sino a causa de un dato mal introducido por el usuario.
 +
 +El problema que se genera con ésto es que la excepción <javadoc jee6>javax.validation.ConstraintViolationException|ConstraintViolationException</javadoc> no sabemos que método la va a lanzar a no ser que nos leamos la documentación, pero es importante capturarla para poder mostrar un mensaje al usuario. Si hubiera sido declarada como Checked, el propio compilador nos avisaría de cuando capturarla. Todo ésto nos puede llevar a que el usuario acabe viendo nuestra página de error de la aplicación simplemente porque se ha dejado un campo sin rellenar.
 +
 +<note important>
 +Otro problema de Java es que hay APIs que lanzan UnChecked Exceptions cuando deberían haber sido Cheked Exceptions.
 +</note>
 +
 +La solución a este problema consiste simplemente en leerse la documentación para ver cuando se lanza una excepción unchecked , que debería haber sido Checked, para capturarla y hacer el correcto tratamiento con ella.
 +
 +Mas información sobre la excepción <javadoc jee6>javax.validation.ConstraintViolationException|ConstraintViolationException</javadoc> y como solucionar el problema lo tenemos en [[unidades:07_arquitectura:02_excepciones]]
 +===== Formas erróneas de tratar las excepciones =====
 +Veamos ahora una serie de errores que se cometen al tratar las excepciones.
 +
 +==== Perder la excepción original ====
 +Quizás otro origen del desconocimiento de las excepciones es que hasta Java 1.4 no se podía encapsular una excepción dentro de otra. Lo podemos ver en la clase ''Throwable'' de la que hereda Exception.
 +  * [[http://docs.oracle.com/javase/1.3/docs/api/java/lang/Throwable.html|Throwable en Java 1.3]]: No existen un constructor que acepte como argumento otra excepción ni el método ''Throwable getCause()'' que obtiene la excepción encapsulada.
 +  * [[http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/Throwable.html|Throwable en Java 1.4]]: Ya dispone de un constructor al que pasarle otra excepción y el método ''Throwable getCause()''.
 +
 +Esto último nos ha llevado a soluciones erróneas como la siguiente:
 +<code java 1>
 +try {
 +    double c=matematicas.dividir(-1.6, 0);
 +} catch (Exception ex) {
 +    throw new RuntimeException("Falló al llamar a 'dividir':" + ex.getMessage());
 +}
 +</code>
 +
 +En la línea 4 vemos como se crea una ''RuntimeException'' pero de la excepción original se ha pedido la traza completa y solo vamos a conseguir el mensaje de error, pero las excepciones pueden tener mucha mas información que simplemente el mensaje y la traza.
 +
 +Por ejemplo la excepción <javadoc jdk7>java.sql.SQLException|SQLException</javadoc>  dispone de los métodos <javadoc jdk7>java.sql.SQLException#getSQLState()|getSQLState()</javadoc> y <javadoc jdk7>java.sql.SQLException#getErrorCode()|getErrorCode()</javadoc> que contiene información sobre el error concreto que ha ocurrido en la base de datos. Usando el código anterior también lo habríamos perdido.
 +
 +==== Crear una excepción inutil ====
 +Otro segundo error es crear una nueva excepción **generica** (( No me refiero a excepciones concretas que tienen significado específico como ''NullPointerException'' )) que no aporta nada respecto a ''RuntimeException'' (( Lo mismo se aplica a Exception )) y usarla en vez de ''RuntimeException''
 +
 +<code java 1>
 +public class MiExcepcion extends RuntimeException { 
 +  public MiExcepcion (String msg) {
 +      super(msg);
 +  }
 +}
 +</code>
 +
 +Creando una excepción personalizada que no aporta nada.
 +
 +<code java 1>
 +try {
 +    double c=matematicas.dividir(-1.6, 0);
 +} catch (Exception ex) {
 +    throw new MiExcepcion ("Falló al llamar a 'dividir'.");
 +}
 +</code>
 +
 +En la línea 4 se lanza la nueva excepción ''MiExcepcion'' que no hace nada que no hiciera ya ''RuntimeException'' e incluso seguimos perdiendo la excepción original.
 +
 +==== Pasar el problema hacia arriba ====
 +La siguiente forma errónea de tratar la excepciones es simplemente hacer que el método superior a su vez declare que lanza esas excepciones checked pero que deberían haber sido uncheked. Con ésto lo único que hacemos es expandir el problema hacia arriba sin solucionarlo.
 +
 +<code java 1>
 +public class Main {
 +    public static void main(String[] args) throws Exception {
 +        Matematicas matematicas=new Matematicas();
 +        
 +        double c=matematicas.dividir(-1.6, 0);
 +    }    
 +}
 +</code>
 +
 +En la línea 2 vemos ahora como el método ''Main'' ahora también lanza una excepción checked.
 +
 +==== Ignorarla ====
 +Este es el peor error que podemos cometer al tratar una excepción.Consiste simplemente en ignorar la excepción y que continúe la ejecución del programa. Esto es tan peligroso ya que si se ha producido algún fallo en el programa lo más seguro es hacer que se detenga cuanto antes y no seguir haciendo operaciones con datos que quizás sean erróneos.
 +
 +<code java 1>
 +try {
 +    double c=matematicas.dividir(-1.6, 0);
 +} catch (Exception ex) {
 +
 +}
 +</code>
 +
 +
 +Vemos en la línea 4 como no se realiza ninguna acción al capturar la excepción, por lo tanto la ejecución seguirá como si no hubiera pasado nada.
 +
 +==== Log ====
 +Acabamos de ver en el apartado anterior que nunca (( Siempre hay //excepciones// para todo )) se debe ignorar la excepción. Una forma de ignorarla pero que aparenta ser mejor solución es guardar en el log la excepción que se ha producido pero sin hacer nada mas.
 +
 +<code java 1>
 +try {
 +    double c=matematicas.dividir(-1.6, 0);
 +} catch (RuntimeException ex) {
 +    Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
 +}
 +</code>
 +
 +En la línea 4 guardamos en el log la excepción pero el programa sigue ejecutándose. Como ya hemos comentado no debería seguir la ejecución del programa puesto que los datos pueden haber quedado en un estado erróneo.
 +
 +La idea de guardar en el log la excepción es buena por si misma. Sin embargo no suele ser necesario guardarla explícitamente ya que en muchas ocasiones ya se guarda automáticamente. Por ejemplo en aplicaciones Web , el propio contenedor al detectar ((Siempre que no la detengamos antes )) una excepción guardará en el log la excepción que se ha producido.
 +
 +==== Consola  ====
 +Otra forma erronea de guardar las excepciones consiste en mostrar la traza del error por la consola.
 +
 +<code java 1>
 +try {
 +    double c=matematicas.dividir(-1.6, 0);
 +} catch (RuntimeException ex) {
 +    ex.printStackTrace();
 +}
 +</code>
 +
 +En la línea 4 mostramos por la consola la traza de la excepción pero el programa sigue ejecutándose. Como ya se ha dicho no debería seguir la ejecución del programa puesto que los datos pueden haber quedado en un estado erróneo.
 +
 +En aplicaciones Web o aplicaciones de Ventanas , esta opción tiene menos sentido aun que la opción del log, ya que guardando la traza en un fichero de log está mucho más accesible para poder averiguar el origen del problema que mostrándolo por consola ya que la consola la puede cerrar el usuario y perder toda la traza o simplemente que no tengamos acceso a la consola.
 +
 +==== Imprimirla ====
 +Por último pero no por ello menos usada está la //técnica// de imprimir el mensaje de la excepción.
 +
 +<code java 1>
 +try {
 +    double c=matematicas.dividir(-1.6, 0);
 +} catch (RuntimeException ex) {
 +    System.out.println("Falló al dividir");
 +}
 +</code>
 +
 +Esta forma tiene los mismos problemas del anterior de imprimirla por consola pero ésta es aun peor, hemos perdido toda la información de la excepción.
 +
 +===== Mejoras en el tratamiento de excepciones =====
 +Ya hemos visto como es la forma correcta de tratar las excepciones Checked que realmente deberían ser unchecked:
 +
 +<code java 1>
 +try {
 +    double c=matematicas.dividir(-1.6, 0);
 +} catch (Exception ex) {
 +    throw new RuntimeException("Fallo al llamar a 'dividir'",ex)
 +}
 +</code> 
 +
 +Este código tiene unas pegas al respecto de las trazas que se generara por pantalla (( o en el sistema de log )) cuando se produce una excepción.
 +
 +Si ejecutamos el código se produce la siguiente traza:
 +<code 1>
 +Exception in thread "main" java.lang.RuntimeException: Fallo al llamar a 'dividir'
 + at ejemplo.Main.main(Main.java:17)
 +Caused by: java.lang.Exception: El argumento b no puede ser 0
 + at ejemplo.Matematicas.dividir(Matematicas.java:14)
 + at ejemplo.Main.main(Main.java:15)
 +</code>
 +
 +Como podemos ver en la traza, se muestran dos excepciones, la excepción original en la línea 3 y la ''RuntimeExcepcion'' que encapsula a la excepción original en la línea 1. Esto es inevitable así que por ahora lo vamos a asumir.
 +
 +El problema viene si el método ''dividir'' puede lanzar tanto una ''Exception'' como una ''RuntimeException''
 +
 +Veamos el código modificado de ''dividir'' para que en caso de que algún número sea negativo se produzca una ''RuntimeException'':
 +
 +<code java 1>
 +public class Matematicas {
 +    public double dividir(double a, double b) throws Exception {
 +        if (a<0) {
 +            throw new RuntimeException("El argumento a no puede ser negativo");
 +        }        
 +        if (b<0) {
 +            throw new RuntimeException("El argumento b no puede ser negativo");
 +        }        
 +        
 +        if (b == 0) {
 +            throw new Exception("El argumento b no puede ser 0");
 +        }
 +
 +        return a / b;
 +    }
 +}
 +</code>
 +
 +Vemos en las líneas de la 3 a la 8 como ahora también se lanza un ''RuntimeException''.
 +
 +Si volvemos ahora a ejecutar el programa se genera la siguiente traza de error:
 +
 +<code 1>
 +Exception in thread "main" java.lang.RuntimeException: Fallo al llamar a 'dividir'
 + at ejemplo.Main.main(Main.java:17)
 +Caused by: java.lang.RuntimeException: El argumento a no puede ser negativo
 + at ejemplo.Matematicas.dividir(Matematicas.java:14)
 + at ejemplo.Main.main(Main.java:15)
 +</code>
 +
 +Ahora la traza si que ha quedado un poco mal.Hemos encapsulado una ''RuntimeException'' (línea 3) dentro de otra ''RuntimeException'' (línea 1) lo que tiene muy poco sentido. Es decir que la traza ha quedado poco clara cuando no hay necesidad para ello. Esto ocurre ya que ''RuntimeException'' hereda de ''Exception'' por lo que el ''catch'' también atrapa la ''RuntimeException'' y la encapsula en otra ''RuntimeException''.
 +
 +La solución a ésto, es bastante sencilla.Si nos llega una ''RuntimeException'' simplemente la volvemos a lanzar en vez de encapsularla.
 +
 +<code java 1>
 +public class Main {
 +    public static void main(String[] args) {
 +        Matematicas matematicas=new Matematicas();
 +        try {
 +            double c=matematicas.dividir(-1.6, 0);
 +        } catch (RuntimeException ex) {
 +            throw ex;            
 +        } catch (Exception ex) {
 +            throw new RuntimeException("Fallo al llamar a 'dividir'",ex);
 +        }
 +    }    
 +}
 +</code> 
 +
 +En la línea 6 ahora capturamos la ''RuntimeException'' y al tratarla simplemente relanzamos la misma excepción (línea 7). 
 +
 +Si ahora volvemos a ejecutar el código, la traza resultante es:
 +
 +<code 1>
 +Exception in thread "main" java.lang.RuntimeException: El argumento a no puede ser negativo
 + at ejemplo.Matematicas.dividir(Matematicas.java:14)
 + at ejemplo.Main.main(Main.java:15)
 +</code>
 +
 +Es decir que ahora la traza para una ''RuntimeException'' es como debería ser ,ya que no queda encapsulada por ninguna otra excepción.
 +
 +<note tip>
 +Por los ejemplos puede parecer un poco exagerado querer que no se encapsulen las excepciones pero pongo aquí un ejemplo de excepciones en Hibernate:
 +
 +  Exception in thread "main" org.hibernate.MappingException: Could not get constructor for org.hibernate.persister.entity.SingleTableEntityPersister
 +  at org.hibernate.persister.internal.PersisterFactoryImpl.create(PersisterFactoryImpl.java:185)
 +  at org.hibernate.persister.internal.PersisterFactoryImpl.createEntityPersister(PersisterFactoryImpl.java:135)
 +  at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:367)
 +  at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1740)
 +  at curshibernatet03e01.CursHibernateT03E01.main(CursHibernateT03E01.java:23)
 +  Caused by: org.hibernate.HibernateException: Unable to instantiate default tuplizer [org.hibernate.tuple.entity.PojoEntityTuplizer]
 +  at org.hibernate.tuple.entity.EntityTuplizerFactory.constructTuplizer(EntityTuplizerFactory.java:138)
 +  at org.hibernate.tuple.entity.EntityTuplizerFactory.constructDefaultTuplizer(EntityTuplizerFactory.java:188)
 +  at org.hibernate.tuple.entity.EntityMetamodel.<init>(EntityMetamodel.java:341)
 +  at org.hibernate.persister.entity.AbstractEntityPersister.<init>(AbstractEntityPersister.java:502)
 +  at org.hibernate.persister.entity.SingleTableEntityPersister.<init>(SingleTableEntityPersister.java:144)
 +  at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
 +  at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
 +  at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
 +  at java.lang.reflect.Constructor.newInstance(Constructor.java:525)
 +  at org.hibernate.persister.internal.PersisterFactoryImpl.create(PersisterFactoryImpl.java:163)
 +  ... 4 more
 +  Caused by: java.lang.reflect.InvocationTargetException
 +  at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
 +  at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
 +  at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
 +  at java.lang.reflect.Constructor.newInstance(Constructor.java:525)
 +  at org.hibernate.tuple.entity.EntityTuplizerFactory.constructTuplizer(EntityTuplizerFactory.java:135)
 +  ... 13 more
 +  Caused by: org.hibernate.PropertyNotFoundException: Could not find a getter for domicilio in class curshibernatet03e01.Cliente
 +  at org.hibernate.property.BasicPropertyAccessor.createGetter(BasicPropertyAccessor.java:316)
 +  at org.hibernate.property.BasicPropertyAccessor.getGetter(BasicPropertyAccessor.java:310)
 +  at org.hibernate.mapping.Property.getGetter(Property.java:320)
 +  at org.hibernate.tuple.entity.PojoEntityTuplizer.buildPropertyGetter(PojoEntityTuplizer.java:436)
 +  at org.hibernate.tuple.entity.AbstractEntityTuplizer.<init>(AbstractEntityTuplizer.java:200)
 +  at org.hibernate.tuple.entity.PojoEntityTuplizer.<init>(PojoEntityTuplizer.java:82)
 +  ... 18 more
 +  
 +Realmente la información mas útil la encontramos justamente en la primera excepción que se produce. Sin embargo está justamente al final de toda la traza: 
 +
 +  PropertyNotFoundException: Could not find a getter for domicilio in class curshibernatet03e01.Cliente
 +  
 +Así que si no se hubiera encapsulado la excepción se vería en la primera línea en vez de ir a buscarla al final de la traza.
 +
 +</note>
 +==== La solución definitiva ====
 +La solución definitiva es que las excepciones checked tenga también una traza //limpia// como las unchecked. Antes hemos comentado que es imposible pero hay un truco en Java que permite hacerlo, no voy a explicar como funciona el truco simplemente voy a explicar como hacerlo.
 +
 +Se debe crear la siguiente clase:
 +<code java 1>
 +public  class RelanzadorExcepciones {
 +
 +    public static RuntimeException lanzar( Exception ex){
 +        RelanzadorExcepciones.<RuntimeException>lanzarComoUnchecked(ex);
 +
 +        throw new AssertionError("Esta línea  nunca se ejecutará pero Java no lo sabe");
 +    }
 +
 +    private static <T extends Exception> void lanzarComoUnchecked(Exception toThrow) throws T{
 +        throw (T) toThrow;
 +    }
 +}
 +</code>
 +
 +Ahora nuestro código ''Main'' se modificará quedando de la siguiente forma:
 +<code java 1>
 +public class Main {
 +    public static void main(String[] args) {
 +        Matematicas matematicas=new Matematicas();
 +        try {
 +            double c=matematicas.dividir(3, 0);           
 +        } catch (Exception ex) {
 +            RelanzadorExcepciones.lanzar(ex);
 +        }
 +    }    
 +}
 +</code>
 +
 +Vemos como ahora para relanzar la excepción en la línea 22 usamos el nuevo método que hemos creado ''RelanzadorExcepciones.lanzar''
 +
 +Si ejecutamos ahora la aplicación y vemos la traza, queda de la siguiente forma:
 +<code>
 +Exception in thread "main" java.lang.Exception: El argumento b no puede ser 0
 + at ejemplo.Matematicas.dividir(Matematicas.java:21)
 + at ejemplo.Main.main(Main.java:15)
 +</code>
 +
 +¡¡Es decir que únicamente se ve la excepción checked sin que quede encapsulada por nadie.!!
 +
 +Por fin con este ultimo truco hemos conseguido simplificar la traza con todas las excepciones.
 +
 +===== Referencias =====
 +  * [[http://www.ibm.com/developerworks/java/library/j-jtp05254/index.html|Java theory and practice: The exceptions debate]]
 +  * [[http://www.oracle.com/technetwork/articles/javase/bloch-effective-08-qa-140880.html|Effective Java (2nd Edition)]]
 +  * [[http://mindview.net/Books/TIJ4|Thinking in Java, 4th edition]]
 +  * [[http://www.wrox.com/WileyCDA/WroxTitle/productCd-0764543857.html|Expert One-on-One J2EE Design and Development]]
 +  * [[http://docs.oracle.com/javase/tutorial/essential/exceptions/runtime.html|The Java Tutorials.Unchecked Exceptions—The Controversy]]
 +  * [[http://www.gamlor.info/wordpress/2010/02/throwing-checked-excpetions-like-unchecked-exceptions-in-java/|Throwing Checked Exceptions Like Unchecked Exceptions in Java]]
 +  * [[http://www.manageability.org/blog/stuff/exceptional-exception-handling-techniques|13 Exceptional Exception Handling Techniques]]