quinta-feira, 17 de maio de 2012

JPA (EclipseLink) and Complex Parameters Stored Procedures

As JAVA has a limited support to Stored Procedures (SP) and there still are people that think of it like one of the "Wonders of the Software's World", some frameworks and a hard work can help solving some complex problems that clients love to come up with.

In this sample I m working with ORACLE as my database server, so the scripts were made for it.

The SP that I m dealing here are far away from usual, they got TYPE definitions in the packages and cursors all mixed together.

The EclipseLink project (http://www.eclipse.org/eclipselink) has a great SP support but some SP calls could be a real nightmare.

Glossary:
TIPO means TYPE
ENTRADA means INPUT
USUARIO means USER
SISTEMA means SYSTEM
RETORNO means OUTPUT
MENSAGEM means MESSAGE
Procedure sample:

 PACKAGE  RBR_SP_SAMPLE AS  
   
  TYPE TIPO_ENTRADA IS RECORD (  
   NM_USUARIO VARCHAR2(30),  
   ID_SISTEMA NUMBER(15)  
  );  
   
  TYPE TIPO_RETORNO IS RECORD (  
   TIPO_MENSAGEM NUMBER(15),  
   DESC_MENSAGEM VARCHAR2(30)  
  );  

  PROCEDURE SP_LISTA_USUARIO (  
   PR_NM_USUARIO IN  RBR_SP_SAMPLE.TIPO_ENTRADA,  
   PR_VC_USUARIO OUT SYS_REFCURSOR,  
   PR_TIPO_RETORNO OUT RBR_SP_SAMPLE.TIPO_RETORNO  
  );  
   
 END RBR_SP_SAMPLE;  


Now we have our SP sample, now let's define the TYPES as OBJECTS:

 CREATE OR REPLACE TYPE O_TIPO_ENTRADA AS OBJECT (  
   NM_USUARIO VARCHAR2(30),  
   ID_SISTEMA  NUMBER(15)  
  );  
   
  CREATE OR REPLACE TYPE O_TIPO_RETORNO AS OBJECT (  
   DESC_MENSAGEM VARCHAR2(30),  
   TIPO_MENSAGEM NUMBER(15)  
  );  


Those objects will be very handy in the mapping process.

Ok then code...

Let's start defining an interface:

 package br.com.brainsoftware.rbr.storedprocedure;  
   
 import java.util.Collection;  
   
 public interface StoredProcedure<ENT, RET, TYPE> {  
   
      /**  
       * Call the stored procedure and prepare the results  
       *   
       * @throws StoredProcedureException  
       */  
      public void call(ENT entry) throws StoredProcedureException;  
   
      /**  
       * After call the <code>call()</code> method this one should return  
       * <code>true</code>  
       *   
       * @return  
       */  
      boolean isReady();  
   
      /**  
       * Only call this method after <code>call()</code> and  
       * <code>isReady()</code>  
       *   
       * @return  
       */  
      public RET getReturn();  
   
      /**  
       * Only call this method after <code>call()</code> and  
       * <code>isReady()</code>  
       *   
       * @return  
       */  
      public Collection<TYPE> getResults();  
 }  
   

Now an abstract class that implements the defined interface, controls the data source connection and defines the type and object mapping:


 package br.com.brainsoftware.rbr.storedprocedure;  
   
 import org.eclipse.persistence.logging.SessionLog;  
 import org.eclipse.persistence.sessions.DatabaseSession;  
 import org.eclipse.persistence.sessions.Project;  
 import org.eclipse.persistence.sessions.Session;  
   
 import br.com.brainsoftware.rbr..pojo.RWEntrada;  
 import br.com.brainsoftware.rbr..pojo.RWRetorno;  
 import br.com.brainsoftware.rbr..util.ConfigUtil;  
   
 public abstract class AbstractStoredProcedure<ENT, RET, TYPE> implements  
           StoredProcedure<ENT, RET, TYPE> {  
      protected Session session;  
      protected boolean ready = false;  
   
      public AbstractStoredProcedure() {  
           // Configuring connection properties  
           Project project = new Project(ConfigUtil.getLogin());  
   
           // Mapping input parameter type - O_TIPO_ENTRADA -> TipoEntrada  
           project.addDescriptor(TipoEntrada.getMapping());  
   
           // Mapping output parameter type - O_TIPO_RETORNO -> TipoRetorno  
           project.addDescriptor(TipoRetorno.getMapping());  
   
           // Connecting to database  
           session = project.createDatabaseSession();  
           session.setLogLevel(SessionLog.FINE);  
           ((DatabaseSession) session).login();  
      }  
 }  



Mapping classes:

 package br.com.brainsoftware.rbr.pojo;  
   
 import java.io.Serializable;  
   
 import org.eclipse.persistence.descriptors.ClassDescriptor;  
 import org.eclipse.persistence.mappings.DirectToFieldMapping;  
 import org.eclipse.persistence.mappings.structures.ObjectRelationalDataTypeDescriptor;  
 import org.eclipse.persistence.platform.database.jdbc.JDBCTypes;  
 import org.eclipse.persistence.platform.database.oracle.plsql.PLSQLrecord;  
   
 public class TipoEntrada implements Serializable {  
   
      public static final String PARAMETER_NAME = "PR_NM_USUARIO";  
      private static final long serialVersionUID = 2165067461541295168L;  
   
      // Mapping  
      private static ObjectRelationalDataTypeDescriptor recordDescriptor;  
      private static PLSQLrecord record;  
      static {  
           recordDescriptor = new ObjectRelationalDataTypeDescriptor();  
           recordDescriptor.descriptorIsAggregate();  
           recordDescriptor.setJavaClass(TipoEntrada.class);  
           recordDescriptor.setAlias("Entrada");  
           recordDescriptor.setStructureName("O_TIPO_ENTRADA");  
           DirectToFieldMapping nmUsuarioMapping = new DirectToFieldMapping();  
           nmUsuarioMapping.setAttributeName("nmUsuario");  
           nmUsuarioMapping.setFieldName("NM_USUARIO");  
           recordDescriptor.addMapping(nmUsuarioMapping);  
           DirectToFieldMapping idSistemaMapping = new DirectToFieldMapping();  
           idSistemaMapping.setAttributeName("idSistema");  
           idSistemaMapping.setFieldName("ID_SISTEMA");  
           recordDescriptor.addMapping(idSistemaMapping);  
   
           record = new PLSQLrecord();  
           record.setTypeName("RBR_SP_SAMPLE.TIPO_ENTRADA");  
           record.setCompatibleType("O_TIPO_ENTRADA");  
           record.setJavaType(TipoEntrada.class);  
           record.addField("NM_USUARIO", JDBCTypes.VARCHAR_TYPE);  
           record.addField("ID_SISTEMA", JDBCTypes.NUMERIC_TYPE);  
      }  
   
      private String nmUsuario;  
      private String idSistema;  
   
      public TipoEntrada() {  
      }  
   
      public TipoEntrada(String nmUsuario, String idSistema) {  
           super();  
           this.nmUsuario = nmUsuario;  
           this.idSistema = idSistema;  
      }  
   
      public String getNmUsuario() {  
           return nmUsuario;  
      }  
   
      public void setNmUsuario(String nmUsuario) {  
           this.nmUsuario = nmUsuario;  
      }  
   
      public String getIdSistema() {  
           return idSistema;  
      }  
   
      public void setIdSistema(String idSistema) {  
           this.idSistema = idSistema;  
      }  
   
      public String toString() {  
           return String.format("TipoEntrada [nmUsuario=%s, idSistema=%s]", nmUsuario,  
                     idSistema);  
      }  
   
      public static ClassDescriptor getMapping() {  
           return recordDescriptor;  
      }  
   
      public static PLSQLrecord getRecord() {  
           return record;  
      }  
 }  
   

 package br.com.brainsoftware.rbr.pojo;  
   
 import java.io.Serializable;  
   
 import org.eclipse.persistence.descriptors.ClassDescriptor;  
 import org.eclipse.persistence.mappings.DirectToFieldMapping;  
 import org.eclipse.persistence.mappings.structures.ObjectRelationalDataTypeDescriptor;  
 import org.eclipse.persistence.platform.database.jdbc.JDBCTypes;  
 import org.eclipse.persistence.platform.database.oracle.plsql.PLSQLrecord;  
   
 public class TipoRetorno implements Serializable {  
   
      public static final String PARAMETER_NAME = "PR_TIPO_RETORNO";  
      private static final long serialVersionUID = -8192064531359394512L;  
   
      // Mapping  
      private static ObjectRelationalDataTypeDescriptor recordDescriptor;  
      private static PLSQLrecord record;  
      static {  
           recordDescriptor = new ObjectRelationalDataTypeDescriptor();  
           recordDescriptor.descriptorIsAggregate();  
           recordDescriptor.setJavaClass(TipoRetorno.class);  
           recordDescriptor.setAlias("Retorno");  
           recordDescriptor.setStructureName("O_TIPO_RETORNO");  
           DirectToFieldMapping descMensagemMapping = new DirectToFieldMapping();  
           descMensagemMapping.setAttributeName("descMensagem");  
           descMensagemMapping.setFieldName("DESC_MENSAGEM");  
           recordDescriptor.addMapping(descMensagemMapping);  
           DirectToFieldMapping tipoMensagemMapping = new DirectToFieldMapping();  
           tipoMensagemMapping.setAttributeName("tipoMensagem");  
           tipoMensagemMapping.setFieldName("TIPO_MENSAGEM");  
           recordDescriptor.addMapping(tipoMensagemMapping);  
   
           record = new PLSQLrecord();  
           record.setTypeName("RBR_SP_SAMPLE.TIPO_RETORNO");  
           record.setCompatibleType("O_TIPO_RETORNO");  
           record.setJavaType(TipoRetorno.class);  
           record.addField("DESC_MENSAGEM", JDBCTypes.VARCHAR_TYPE);  
           record.addField("TIPO_MENSAGEM", JDBCTypes.NUMERIC_TYPE);  
      }  
   
      private String tipoMensagem;  
      private String descMensagem;  
   
      public TipoRetorno() {  
      }  
   
      public TipoRetorno(String tipoMensagem, String descMensagem) {  
           super();  
           this.tipoMensagem = tipoMensage;  
           this.descMensagem = descMensagem;  
      }  
   
      public String getTipoMensagem() {  
           return tipoMensagem;  
      }  
   
      public void setTipoMensagem(String tipoMensagem) {  
           this.tipoMensagem = tipoMensagem;  
      }  
   
      public String getDescMensagem() {  
           return descMensagem;  
      }  
   
      public void setDescMensagem(String descMensagem) {  
           this. descMensagem = descMensagem;  
      }  
   
      public String toString() {  
           return String.format("TipoRetorno [tipoMensagem=%s, descMensagem=%s]",tipoMensagem,  
                     descMensagem);  
      }  
   
      public static ClassDescriptor getMapping() {  
           return recordDescriptor;  
      }  
   
      public static PLSQLrecord getRecord() {  
           return record;  
      }  
 }  

An exception:

 package br.com.brainsoftware.rbr.storedprocedure;  
   
 public class StoredProcedureException extends Exception {  
   
      private static final long serialVersionUID = 6325249553440417092L;  
   
      public StoredProcedureException() {  
      }  
   
      public StoredProcedureException(String arg0) {  
           super(arg0);  
      }  
   
      public StoredProcedureException(Throwable arg0) {  
           super(arg0);  
      }  
   
      public StoredProcedureException(String arg0, Throwable arg1) {  
           super(arg0, arg1);  
      }  
   
 }  

By extend the AbstracStoredProcedure class, the next class will call the SP and prepares the data so they can be used properly and easier:

 package br.com.brainsoftware.rbr.storedprocedure;  
   
 import java.util.ArrayList;  
 import java.util.Collection;  
 import java.util.Collections;  
 import java.util.Enumeration;  
 import java.util.List;  
 import java.util.Vector;  
   
 import org.eclipse.persistence.platform.database.oracle.plsql.PLSQLStoredProcedureCall;  
 import org.eclipse.persistence.queries.DataReadQuery;  
 import org.eclipse.persistence.sessions.DatabaseRecord;  
   
 import br.com.brainsoftware.rbr.databasetype.OracleCursorDatabaseType;  
 import br.com.brainsoftware.rbr.pojo.Usuario;  
 import br.com.brainsoftware.rbr.pojo.TipoEntrada;  
 import br.com.brainsoftware.rbr.pojo.TipoRetorno;  
 import br.com.brainsoftware.rbr.util.ReflectionUtil;  
   
 /**  
  * Chama a procedure usando objetos de entrada e saida e tratando o resultado do  
  * cursor  
  *   
  */  
 public class ListaMaterial extends  
           AbstractStoredProcedure<RWEntrada, RWRetorno, Material> {  
   
      private static final String CURSOR_PARAMETER_NAME = "PR_VC_USUARIO";  
      private static final String PROCEDURE_NAME = "RBR_SP_SAMPLE.SP_LISTA_USUARIO";  
      private TipoRetorno retorno = null;  
      private List<Usuario> usuarios = Collections.emptyList();  
   
      @SuppressWarnings("unchecked")  
      public void call(TipoEntrada entrada) throws StoredProcedureException {  
           // Preparing the SP call  
           PLSQLStoredProcedureCall call = new PLSQLStoredProcedureCall();  
           call.setProcedureName(PROCEDURE_NAME);  
           call.addNamedArgument(TipoEntrada.PARAMETER_NAME, TipoEntrada.getRecord());  
           call.addNamedOutputArgument(TipoRetorno.PARAMETER_NAME,  
                     TipoRetorno.getRecord());  
           call.addNamedOutputArgument(CURSOR_PARAMETER_NAME,  
                     new OracleCursorDatabaseType());  
   
           // Preparing the query  
           DataReadQuery query = new DataReadQuery();  
           query.setCall(call);  
           query.addArgument(TipoEntrada.PARAMETER_NAME);  
   
           // Adding arguments  
           List<Object> queryArgs = new ArrayList<Object>();  
           queryArgs.add(entrada);  
           query.bindAllParameters();  
   
           // Executing query  
           Object result = session.executeQuery(query, queryArgs);  
   
           // Treating the results  
           Vector<DatabaseRecord> results = new Vector<DatabaseRecord>();  
           Enumeration<DatabaseRecord> records = ((Vector<DatabaseRecord>) result)  
                     .elements();  
           while (records.hasMoreElements()) {  
                DatabaseRecord record = records.nextElement();  
                retorno = (TipoRetorno) record.get(TipoRetorno.PARAMETER_NAME);  
                results = (Vector<DatabaseRecord>) record  
                          .get(CURSOR_PARAMETER_NAME);  
           }  
   
           try {  
                List<DatabaseRecord> outList = new ArrayList<DatabaseRecord>();  
                outList.addAll(results);  
                materiais = ReflectionUtil.mapper(outList, Usuario.class);  
                ready = true;  
           } catch (Exception e) {  
                throw new StoredProcedureException(e);  
           }  
      }  
   
      public boolean isReady() {  
           return ready;  
      }  
   
      public RWRetorno getReturn() {  
           return retorno;  
      }  
   
      public Collection<Usuario> getResults() {  
           return usuarios;  
      }  
 }  
   


A little hacking... the returning data must work like a cursor, therefore the next class implements that behavior:



 package br.com.brainsoftware.rbr.databasetype;  
   
 import static org.eclipse.persistence.internal.helper.DatabaseType.DatabaseTypeHelper.databaseTypeHelper;  
 import static org.eclipse.persistence.internal.helper.Helper.NL;  
   
 import java.util.List;  
 import java.util.ListIterator;  
   
 import oracle.jdbc.OracleTypes;  
   
 import org.eclipse.persistence.internal.helper.DatabaseField;  
 import org.eclipse.persistence.internal.helper.SimpleDatabaseType;  
 import org.eclipse.persistence.internal.sessions.AbstractRecord;  
 import org.eclipse.persistence.platform.database.DatabasePlatform;  
 import org.eclipse.persistence.platform.database.oracle.plsql.PLSQLStoredProcedureCall;  
 import org.eclipse.persistence.platform.database.oracle.plsql.PLSQLargument;  
 import org.eclipse.persistence.queries.StoredProcedureCall;  
 import org.eclipse.persistence.sessions.DatabaseRecord;  
   
 @SuppressWarnings("rawtypes")  
 public class OracleCursorDatabaseType implements SimpleDatabaseType {  
   
      public boolean isComplexDatabaseType() {  
           return false;  
      }  
   
      public boolean isJDBCType() {  
           return false;  
      }  
   
      public int getSqlCode() {  
           return OracleTypes.CURSOR;  
      }  
   
      public int getConversionCode() {  
           return getSqlCode();  
      }  
   
      public String getTypeName() {  
           return "SYS_REFCURSOR";  
      }  
   
      public int computeInIndex(PLSQLargument inArg, int newIndex,  
                ListIterator<PLSQLargument> i) {  
           inArg.outIndex = newIndex;  
           return ++newIndex;  
      }  
   
      public int computeOutIndex(PLSQLargument outArg, int newIndex,  
                ListIterator<PLSQLargument> iterator) {  
           outArg.outIndex = newIndex;  
           return newIndex;  
      }  
   
      public void buildInDeclare(StringBuilder sb, PLSQLargument inArg) {  
           // TODO Auto-generated method stub  
           System.out.println("buildInDeclare");  
      }  
   
      public void buildOutDeclare(StringBuilder sb, PLSQLargument outArg) {  
           sb.append(" ");  
           sb.append(databaseTypeHelper.buildTarget(outArg));  
           sb.append(" ");  
           sb.append(getTypeName());  
           sb.append(";");  
           sb.append(NL);  
      }  
   
      public void buildBeginBlock(StringBuilder sb, PLSQLargument arg,  
                PLSQLStoredProcedureCall call) {  
           // TODO Auto-generated method stub  
           System.out.println("buildBeginBlock");  
   
      }  
   
      public void buildOutAssignment(StringBuilder sb, PLSQLargument outArg,  
                PLSQLStoredProcedureCall call) {  
           String target = databaseTypeHelper.buildTarget(outArg);  
           sb.append(" :");  
           sb.append(outArg.outIndex);  
           sb.append(" := ");  
           sb.append(target);  
           sb.append(";");  
           sb.append(NL);  
      }  
   
      public void translate(PLSQLargument arg, AbstractRecord translationRow,  
                AbstractRecord copyOfTranslationRow,  
                List<DatabaseField> copyOfTranslationFields,  
                List<DatabaseField> translationRowFields,  
                List translationRowValues, StoredProcedureCall call) {  
           // TODO Auto-generated method stub  
           System.out.println("translate");  
      }  
   
      public void buildOutputRow(PLSQLargument outArg, AbstractRecord outputRow,  
                DatabaseRecord newOutputRow, List<DatabaseField> outputRowFields,  
                List outputRowValues) {  
           databaseTypeHelper.buildOutputRow(outArg, outputRow, newOutputRow,  
                     outputRowFields, outputRowValues);  
      }  
   
      public void logParameter(StringBuilder sb, Integer direction,  
                PLSQLargument arg, AbstractRecord translationRow,  
                DatabasePlatform platform) {  
           // TODO Auto-generated method stub  
           System.out.println("logParameter");  
      }  
 }  


Some util classes:

 package br.com.brainsoftware.rbr.util;  
   
 import org.eclipse.persistence.platform.database.oracle.Oracle11Platform;  
 import org.eclipse.persistence.sessions.DatabaseLogin;  
   
 public class ConfigUtil {  
   
      public static DatabaseLogin getLogin() {  
           String USERNAME = "user_name";  
           String PASSWORD = "password";  
           String URL = "jdbc:oracle:thin:@192.168.51.10:1521:XE";  
           String DRIVER = "oracle.jdbc.driver.OracleDriver";  
   
           DatabaseLogin login = new DatabaseLogin();  
           login.setUserName(USERNAME);  
           login.setPassword(PASSWORD);  
           login.setConnectionString(URL);  
           login.setDriverClassName(DRIVER);  
           login.setDatasourcePlatform(new Oracle11Platform());  
           ((DatabaseLogin) login).bindAllParameters();  
           return login;  
      }  
   
 }  
   

 package br.com.brainsoftware.rbr.util;  
   
 import java.lang.reflect.Constructor;  
 import java.lang.reflect.Field;  
 import java.lang.reflect.InvocationTargetException;  
 import java.lang.reflect.Method;  
 import java.math.BigDecimal;  
 import java.util.ArrayList;  
 import java.util.List;  
   
 import org.eclipse.persistence.sessions.DatabaseRecord;  
   
 public class ReflectionUtil {  
   
      public static Object invokeGetterMethod(Field field, Object object)  
                throws SecurityException, IllegalArgumentException,  
                NoSuchMethodException, IllegalAccessException,  
                InvocationTargetException {  
           String methodName = "get"  
                     + String.valueOf(field.getName().charAt(0)).toUpperCase()  
                     + field.getName().substring(1);  
           Class<?>[] parameterTypes = new Class[0];  
           Method method = object.getClass().getMethod(methodName, parameterTypes);  
           return method.invoke(object);  
      }  
   
      public static Object invokeSetterMethod(Field field, Object object,  
                Object value) throws SecurityException, IllegalArgumentException,  
                NoSuchMethodException, IllegalAccessException,  
                InvocationTargetException {  
           Method method = null;  
           Object result = null;  
           String methodName = "set" + field.getName();  
           for (Method m : object.getClass().getMethods()) {  
                if (methodName.equalsIgnoreCase(m.getName())) {  
                     method = m;  
                }  
           }  
           if (null != method) {  
                result = method.invoke(object, value);  
           }  
           return result;  
      }  
   
      public static <T> List<T> mapper(List<DatabaseRecord> records, Class<T> type)  
                throws SecurityException, IllegalArgumentException,  
                InstantiationException, IllegalAccessException,  
                NoSuchMethodException, InvocationTargetException {  
           List<T> result = new ArrayList<T>();  
           for (DatabaseRecord record : records) {  
                result.add(setValues(record, type));  
           }  
   
           return result;  
      }  
   
      @SuppressWarnings("unchecked")  
      private static <T> T setValues(DatabaseRecord record, Class<T> type)  
                throws InstantiationException, IllegalAccessException,  
                SecurityException, IllegalArgumentException, NoSuchMethodException,  
                InvocationTargetException {  
           Object result = type.newInstance();  
   
           for (Field field : result.getClass().getDeclaredFields()) {  
                Object value = record.get(field.getName().toUpperCase());  
                value = adjustType(field.getType(), value);  
   
                ReflectionUtil.invokeSetterMethod(field, result, value);  
           }  
   
           return (T) result;  
      }  
   
      private static Object adjustType(Class<?> type, Object value)  
                throws IllegalArgumentException, InstantiationException,  
                IllegalAccessException, InvocationTargetException {  
           Object result = value;  
   
           if (value == null) {  
                return result;  
           }  
   
           if (BigDecimal.class.equals(value.getClass())) {  
                BigDecimal new_name = (BigDecimal) value;  
                int helper = new_name.intValue();  
                for (Constructor<?> constructor : type.getDeclaredConstructors()) {  
                     Class<?>[] parameters = constructor.getParameterTypes();  
                     if (parameters.length == 1 && parameters[0].equals(int.class)) {  
                          result = constructor.newInstance(helper);  
                          break;  
                     }  
                }  
           }  
           return result;  
      }  
 }  
   


The POJO class (user data) and its members as well as the cursor statement, you can write by yourself. ;)

Now the main class:

   
      public static void main(String[] args) throws Exception {  
           TipoRetorno retorno = null;  
           List<Usuario> usuarios = Collections.emptyList();  
           ListaUsuario listaUsuario = new ListaUsuario();  
           TipoEntrada entrada = new TipoEntrada();  
           entrada.setNmUsuario("USER1");  
           entrada.setIdSistema("1");  
           listaUsuario.call(entrada);  
           if (listaUsuario.isReady()) {  
                retorno = listaUsuario.getReturn();  
                usuarios = (List<Usuario>) listaUsuario.getResults();  
           }  
           // Displaying the results  
           System.out.format("Retono: Tipo:[%s] Mensagem:[%s]%n",  
                     retorno.getTipoMensagem(), retorno.getDescMensagem());  
           for (Usuario usuario : usuarios) {  
                System.out.format("Usuario [%s]%n", usuario);  
           }  
   
      }  
   


Well, we are done. Wasn't that hard, was it?

I tried to solve the problem using EclipseLink's resources but... no luck, so I took a deep breath and started reading the code, this way I  would understand better how it works, then a implementation of DatabaseType interface and a couple of classes to create a interesting solution.

sexta-feira, 27 de janeiro de 2012

Flat TVs

Ainda está difícil de achar alguém que entenda sobre as tecnologias conversando por aí...
Vou postar aqui para ter como falar: "Na boa, vai no Google e digite: Ronaldo Blanc Flat TVs".

Inspiração:

Muita gente vem falar "de entendido":
"Eu vi e a imagem é muito melhor..."

Ah é?
Viu nada... é muito fácil enganar os olhos... pergunte para um mágico... nem precisa ser o Criss Angel, com aquelas maluquices.

As telas tem algumas características, vale a pena revê-las para não fazer confusão:

1. Resolução
É a quantidade de pixels(pontos) na tela.
Quanto mais ponto, melhor a imagem.

Vale lembrar:
1 kg de chumbo não pesa mais que 1 kg de algodão ou de pena, não interessa que você pense diferente, NÃO PESA!!!
Então não venha me dizer que uma LED Full HD(1920x1080) "tem resolução melhor" que uma LCD Full HD(1920x1080), NÃO TEM!!!

2. Contraste
O que importa nessa medida é como a cor preta se apresenta, seja ela um cinza bem escuro ou um preto profundo. Nesse quesito as TVs de Plasma e LED, tem uma enorme vantagem (com destaque para o plasma): como elas são feitas de células independentes o preto é preto! Na LCD o preto é meio cinza, pois existe uma luz atrás da tela iluminado a imagem gerada nela (backlight).  Como queremos cores brilhantes, o preto acaba sendo prejudicado na LCD, por ter a mesma fonte de luz para todos os pontos.

3. Brilho
Depende muito das condições de iluminação do ambiente, mas é uma medida importante, vamos analisar.

As TVs de LCD, por terem uma luz, óbvio, tem melhor brilho para ambiente iluminados. É uma vantagem do LCD para esse tipo de ambiente, pois você pode ver a imagem sem reflexos e etc...
Em locais pouco iluminados as TVs de plasma e de LED, são mais interessantes, pelo efeito criado pelo alto contraste, as imagens ficam com mais profundidade de cores, ou seja, mais vivas e mais reais*.

* Sobre realidade em imagens - É complicado usar esse termo, mas é o melhor aqui. Imagens reais, que se aproximam do mundo real, em geral são um pouco mais opacas e 'apagadas' do que queremos em uma TV, para vivermos o mundo de fantasia. Vai uma dica, a Sony tem trabalhado muito bem em trazer imagens reais, acontece que um ou outro vem e diz:  "Aquela imagem ali é melhor do que essa da Sony...", simplesmente porque está mais "colorida", no fim queremos mesmo ver isso.

4. Tempo de reposta
Apesar de todo mundo achar que as telas de LED são mais "legais", porque custam mais caro e é uma tecnologia nova, na verdade o tempo de resposta é menor nas telas de plasma, assim, para assistir algo mais "fluído", como um joguinho de poker :P (hahah), agora sério, um jogo de basquete, tênis ou futebol a TV de plasma é mais indicada.

Novas TVs tem altas frequências de refresh e etc... mas não podemos confundir o tempo de refresh(refresh rate) com o tempo de resposta (response time), basicamente é o seguinte:
Response time é o tempo que demora para um pixel ir do cinza->branco->cinza.
Refresh rate é relacionado com a imagem toda, é o tempo para atualizar e sincronizar um frame na tela.

5. Ângulo de visão
Depende de onde a pessoa está sentada em relação à TV a imagem pode parecer distorcida, invertida(em relação às cores) ou simplesmente 'bagunçada'. Aqui outra vez temos vantagens na tecnologia plasma.

Considerações:

1) Plasma, tem melhor contraste, menores tempos de resposta e refresh, maior ângulo de visão. WOW!!! "Eu quero uma dessas!!!"
Ok, eu também, mas nem tudo é virtude, como tudo, elas tem alguns defeitos:
a) Esquentam muito, para gerar o estado plasma é necessário muita energia e isso gera também calor.
b) Consomem bastante energia, cerca de 5-6 lâmpadas de 60W.
c) Geralmente (existem algumas exceções) o menor tamanho é 42".

Você pode estar pensando: "E daí Ronaldão?? Quanto maior a tela melhor...", muito bem,  eu concordo, mas devemos nos lembrar da regra: <Tamanho da TV> * 2,5 = distância que você deve estar dela.
Então em uma tela de 42"(106 cm), você deve estar no mínimo a 265 cm (2,6m) de distância, ou seja o tamanho da tela e o tamanho do cômodo devem ser compatíveis.

2) LCD, tem melhor brilho, consomem menos energia se comparadas com as TVs de plasma e tem bom preço.
Depois de tudo isso, você vem falar de LCD?
Bom,  eu mesmo tenho uma TV de LCD.
Por que?
Porque a LED estava um pouco cara, eu precisava de uma tela menor que 42" (Plasma) e essa estava em promoção :D (money talks).

Se o ambiente é muito iluminado (a minha sala tinha portas de vidro para a varanda, ou seja muito sol) LCD é a melhor opção, já que as outras opções são suscetíveis ao reflexo (glare).

a) "Baixa qualidade" de cores devido ao baixo contraste
b) Pode criar "borrões" em imagens com muito movimentos

3) LED
Todo mundo fala/quer uma TV com essa tecnologia. E eu também quero! :D
 O meu Vaio e o meu Mac são equipados com telas de LED/LCD.

"Peraí....LCD/LED????"
Sim, é LED porque a iluminação é por LEDs, ou seja, no lugar de uma única fonte de luz, existem várias.

"Legal, LED, o que isso?"
O diodo emissor de luz(LED), é famoso, por acender no seu controle remoto quando você aperta um botão, aquela luzinha vermelha, na TV, é o mesmo conceito, mas são peças menores, e um painel cheio de pequenos LEDs torna a iluminação mais interessante (chegando perto da plasma) já que agora a iluminação tem células individuais. WOW!

Os maiores atrativos são:
A) a espessura (são muito fininhas - vale um cuidado: as mais finas, geralmente, tem iluminação lateral, portanto não tem tanto ganho em contraste. As LED "backlit" são as que chegam mais perto da plasma) e
B) o baixo consumo de energia (uma tela de 55" consome o mesmo que 3 lâmpadas de 60W).
Assim como a tela de plasma as cores e o contraste são melhores que a de LCD.

Não tem muito o que criticar aqui, tem baixo consumo, bom contraste, são bem recentes, então tem boas taxas de refresh.

a) (Muito difícil de notar) Assim como a LCD, pode causar "borrões" em imagens com muito movimento. (Como é uma tecnologia mais nova, a tela tem bom refresh rate (100, 120, 200 Hz, ...) mas ainda perde no response time para o (quase instantâneo) da plasma.

Conclusões:

Se você gosta de esportes, reunir várias pessoas para assistir esses eventos e não se incomoda em gastar um pouco de energia... a sua TV tem que ter uma tela de PLASMA!

Parece que esse estado da matéria(plasma) é a coisa mais perfeita que apareceu para cortar aço como se fosse manteiga e para gerar imagens com cores vivas, altíssimo contraste e com alta velocidade. É realmente muito complicado chegar nas cores/velocidade do plasma, como cada pixel é controlado individualmente (em ligado/desligado) enquanto um tem um preto profundo o outro tem um brilho fantástico.

Vamos esperar as novas telas de OLED para ver o que elas podem fazer contra o plasma.
Por enquanto a tecnologia que ganha em quase todos os quesitos importantes para se analisar uma TV ainda é a plasma. As TVs de LED, tem seu espaço garantido nos próximos anos, fazem um bom trabalho tentando alcançar uma tecnologia que se adapta tão bem às nossas necessidades de cor/brilho/contraste/frequência (ficou devendo no consumo, não se pode ganhar todas).

Agora, relaxe e vá ver alguma coisa na sua TV não plana de tubo. :D hahahaha
Afinal, independente da qualidade da imagem, o importante é o seu interesse no assunto.

Abs,
Ronaldão