# JSF-Tabelle erstellen aus Datenbank mittels JDBC



## nimo22 (25. Mrz 2008)

Hallo,

*die folgende Bean bindet die Datenbank (erfolgreich) an:*


```
import java.sql.*;


public class ApplicationManagedBean {

    public ApplicationManagedBean() {
    }
    
      public static void main(String[] args) {
    try {
      // Parameter für Verbindungsaufbau definieren
      String driver = "...";
      String url = "...";
      String user = "....";
      String password = "...";
      
      // JDBC-Treiber laden
      Class.forName(driver);

      // Verbindung aufbauen
      Connection con;
      con = DriverManager.getConnection(url, user, password);

      // SQL-Anweisungen ausführen
      Statement stmt = con.createStatement();
      ResultSet rs = stmt.executeQuery("SELECT * FROM APPLICATION");

      // Ergebnis abfragen
      return(ResultSupport.toResult(rs));
      }

      // Verbindung schließen
      con.close();
    } 
  catch(Exception ex) { ex.printStackTrace(); }
  }  
}
```


*Nun will ich die Spalte "ID_APPLICATION" aus der DB-Tabelle "APPLICATION" über eine JSF auslesen:*



```
<jsp:root 
 xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0"
 xmlns:h="http://java.sun.com/jsf/html" 
 xmlns:f="http://java.sun.com/jsf/core">


    <jsp:directive.page contentType="text/html" pageEncoding="UTF-8"/>

    
    <jsp:element name="text">
        <jsp:attribute name="lang">EN</jsp:attribute>
        <jsp:body> 
         <f:view>
         
         
          <h:dataTable value="#{ApplicationManagedBean.tableName}" var="dbRow">
            <h:column>
              <f:facet name="header">
               <f:verbatim>ID</f:verbatim>
              </f:facet>
             <h:outputText value="#{dbRow.ID_APPLICATION}"/>
            </h:column>
          </h:dataTable>
          
          
       </f:view>
      </jsp:body>
    </jsp:element>

</jsp:root>
```

Leider funktioniert's NICHT! Die Datenbank-Tabelle "APPLICATION" enthält die Spalte "ID_APPLICATION". In "faces_config.xml" wurde die Bean "ApplicationManagedBean" registriert. Was fehlt noch? getter-setter-Methoden?

dank vorab.[/u]


----------



## maki (25. Mrz 2008)

äääähmm.... wozu soll denn das in einer ManagegBean gut sein?

```
public static void main(String[] args)
```


----------



## nimo22 (25. Mrz 2008)

ohhh...hatte vorher mit "public static void main(String[] args)" getestet, ob die Datenbank-Auslese über die Konsole funktioniert. Den Code bitte einfach ohne "public static void main(String[] args)" lesen...also:


```
import java.sql.*;


public class ApplicationManagedBean {

    public ApplicationManagedBean() {
    }
    
    try {
      // Parameter für Verbindungsaufbau definieren
      String driver = "...";
      String url = "...";
      String user = "....";
      String password = "...";
     
      // JDBC-Treiber laden
      Class.forName(driver);

      // Verbindung aufbauen
      Connection con;
      con = DriverManager.getConnection(url, user, password);

      // SQL-Anweisungen ausführen
      Statement stmt = con.createStatement();
      ResultSet rs = stmt.executeQuery("SELECT * FROM APPLICATION");

      // Ergebnis abfragen
      return(ResultSupport.toResult(rs));
      }

      // Verbindung schließen
      con.close();
 
  catch(Exception ex) { ex.printStackTrace(); }
  } 
}
```


----------



## maki (25. Mrz 2008)

Sag mal, willst du hilfe oder nicht?

Wie soll man dir helfen, wenn du nicht kompilierbaren Code postest?
Wie soll man helfen, wenn du uns das Problem nicht verrätst? (Stacktrace)

Ein bisschen Mühe musst du dir schon geben wenn du Hilfe erwartest...


----------



## nimo22 (25. Mrz 2008)

oh sorry...also folgender Code ist compilierbar:


```
import java.sql.*;

import javax.sql.*;
import javax.naming.*;
import javax.servlet.jsp.jstl.sql.*;


public class ApplicationManagedBean {

    /** Creates a new instance of ApplicationManagedBean */
    public ApplicationManagedBean() {
    }
    


  public Result getTable() {
    try {
      // Parameter für Verbindungsaufbau definieren
      String driver = "...";
      String url = "...";
      String user = "...";
      String password = "...";
      // JDBC-Treiber laden
      Class.forName(driver);
      // Verbindung aufbauen
      Connection con;
      con = DriverManager.getConnection(url, user, password);
      // SQL-Anweisungen ausführen
      Statement stmt = con.createStatement();
      ResultSet rs = stmt.executeQuery("SELECT * FROM APPLICATION");
      // Ergebnis abfragen
      return(ResultSupport.toResult(rs));
    
    } catch(Exception ex) { return(null); }
    
  }  
}
```

Meine Problem ist eigentlich nur: 
Ich kann die Spalte "ID_APPLICATION" per JSF-Tag nicht ausgeben, obwohl die Datenbank-Verbindung funktioniert. Fehlen mir getter/setter-Methoden? Die Spalte "ID_APPLICATION" ist im return-Statement enthalten.


----------



## maki (25. Mrz 2008)

Hab noch nie mit ResultSupport gearbeitet, nimmt eine Datatable das als argument?

Fehlermeldung/Stacktrace?


----------



## nimo22 (26. Mrz 2008)

Ich habe die Datei *Application.jspx*:


```
<?xml version="1.0" encoding="UTF-8"?>
<!-- 
    Document   : Application
    Created on : 20.03.2008, 15:00:18
    Author     : bergmann
-->
<jsp:root 
 xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0"
 xmlns:a4j="http://richfaces.org/a4j" 
 xmlns:rich="http://richfaces.org/rich"
 xmlns:h="http://java.sun.com/jsf/html" 
 xmlns:f="http://java.sun.com/jsf/core">


    <jsp:directive.page contentType="text/html" pageEncoding="UTF-8"/>

    
    <jsp:element name="text">
        <jsp:attribute name="lang">EN</jsp:attribute>
        <jsp:body> 
         <f:view>
         
         
          <h:dataTable value="#{ApplicationManagedBean.Table}" var="dbRow">
            <h:column>
              <f:facet name="header">
               <f:verbatim>ID</f:verbatim>
              </f:facet>
             <h:outputText value="#{dbRow.ID_APPLICATION}"/>
            </h:column>
          </h:dataTable>
          
          
       </f:view>
      </jsp:body>
    </jsp:element>

</jsp:root>
```


und die Datei *ApplicationManagedBean*:


```
package dataController;

import java.sql.*;

import javax.sql.*;
import javax.naming.*;
import javax.servlet.jsp.jstl.sql.*;


public class ApplicationManagedBean {

    /** Creates a new instance of ApplicationManagedBean */
    public ApplicationManagedBean() {
    }
    

private String Table = new String();
    
public void setTable(String table) { this.Table = table;   } 


 public String getTable() {
    try {

      // Parameter für Verbindungsaufbau definieren
      String driver = "...";
      String url = "...";
      String user = "...";
      String password = "...";

      // JDBC-Treiber laden
      Class.forName(driver);

      // Verbindung aufbauen
      Connection con;
      con = DriverManager.getConnection(url, user, password);

      // SQL-Anweisungen ausführen
      PreparedStatement stmt = con.prepareStatement("SELECT * FROM APPLICATION");
      ResultSet rs = stmt.executeQuery();

      // Ergebnis abfragen
       return getTable();
    
    } catch(Exception ex) {return (null); }
    
  }  
}
```

Beides lässt sich einwandfrei compilieren. Der Server bringt keine Fehlermeldung. Die Seite erscheint mit dem Label "ID", jedoch ohne Inhalt der Datenbank (obwohl Daten in der Spalte "ID_APPLICATION" vorhanden wären). Die Datenbankanbindung funktioniert einwandfrei, hab mir die Daten vorher mal per "static void main" über die Konsole ausgeben lassen. 


"Return"-Statement falsch?? Kann man sowas eleganter lösen?


----------



## robertpic71 (26. Mrz 2008)

nimo22 hat gesagt.:
			
		

> ```
> package dataController;
> ...
> public String getTable() {
> ...



Wenn er nicht einmal eine Fehlermeldung schreibt (auch nicht im log), dann dürfte JSF nicht richtig konfiguriert sein. Also der Filter in der web.xml und/oder die faces-config.xml für die Definition des SessionBeans.

Im Moment ist dein Return-Statement 100% falsch und endet in einem Loop. 

Noch zum ResultSupport: Eigentlich ist er dafür gemacht, was du vorhast. Ich bin mir aber nicht sicher ob die JSF-Table direkt mit dem Result umgehen kann ober ob du nicht ein result.getRows() zurückgeben musst.

Technisch gesehen, wird das ResultSet in eine SortedMap[] kopiert. Damit hat man das ResultSet unter Umständen 2x im Speicher. Bei großen ResultSets sollte man die Anzahl ev. limitieren, also mit

```
ResultSet.toResult(java.sql.ResultSet rs, int maxRows)
```
aufrufen.

Das ganze ist eine recht einfach gehaltene Lösungen für große Tabellen muss man wohl selber "pagen". Es gibt auch andere Lösungen (mittels CachedRowSet), aber im Endeffekt landen die Daten in einem Collection mit Maps. 

Ich schreibe mir gerade ein eigenes Model (nicht für JSF), welches mit den Paging-Funktion der Datenbank arbeitet und nur einzelne Seiten im Speicher halten muss. 

Damit man sich um solche Sachen weniger Gedanken machen muss, nimmt man normalerweise eine Persitenzschicht wie Hibernate. Aber für sehr kleine Projekte wie z.B.  ein paar Abfrageseiten mit "erledigt-Update" scheint mir Hibernate etwas übertrieben - außer ich brauche die Datenbank noch in anderen Projekten.

/Robert


----------



## nimo22 (27. Mrz 2008)

Hallo,

also die Methode getRows() existiert nicht in der (aktuellen) JDBC-API.

Die Bean "ApplicationManagedBean" wurde bereits im im "faces_config.xml" angelegt:


```
<managed-bean>
        <managed-bean-name>ApplicationManagedBean</managed-bean-name>
        <managed-bean-class>dataController.ApplicationManagedBean</managed-bean-class>
        <managed-bean-scope>request</managed-bean-scope>
</managed-bean>
```

Die Web.xml ist auch okay.

Meine Frage ist eigentlich recht bescheiden:

Wie kann ich die Daten, welche ich aus der Datenbank über die ManagedBean hergeholt hab, dem JSF übergeben??

Hab nun "return rs.toString()" als Rückgabewert in der Bean:



*nochmal die managedBean:*


```
import java.sql.*;

public class ApplicationManagedBean {
    
    
    /** Creates a new instance of ApplicationManagedBean */
    public ApplicationManagedBean() {
    }
    
    
    private String Table = new String();
    
//    public void setTable(String table) {
//        this.Table = table;
//    }

  //public static void main(String[] args) {
 public String getTable() {
    try {
      // Parameter für Verbindungsaufbau definieren
      String driver = "...";
      String url = "...";
      String user = "...";
      String password = "...";
      // JDBC-Treiber laden
      Class.forName(driver);
      // Verbindung aufbauen
      Connection con;
      con = DriverManager.getConnection(url, user, password);
      // SQL-Anweisungen ausführen
      PreparedStatement stmt = con.prepareStatement("SELECT * FROM APPLICATION");
      ResultSet rs = stmt.executeQuery();
      
      return rs.toString();
    
    } catch(Exception ex) { ex.printStackTrace(); return (null); }
    
  }  
}
```


Die JSF:


```
<?xml version="1.0" encoding="UTF-8"?>

<jsp:root 
 xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0"
 xmlns:h="http://java.sun.com/jsf/html" 
 xmlns:f="http://java.sun.com/jsf/core">


    <jsp:directive.page contentType="text/html" pageEncoding="UTF-8"/>

    
    <jsp:element name="text">
        <jsp:attribute name="lang">EN</jsp:attribute>
        <jsp:body> 
         <f:view>
         
          <h:dataTable value="#{ApplicationManagedBean.getTable}" id="dbRow">
            <h:column>
              <f:facet name="header">
               <f:verbatim>ID</f:verbatim>
              </f:facet>
             <h:outputText value="#{dbRow.APPNAME}"/>
            </h:column>
          </h:dataTable>
          
          
       </f:view>
      </jsp:body>
    </jsp:element>

</jsp:root>
```


*Das Problem:*


```
Caused by: javax.el.PropertyNotFoundException: The class 'dataController.ApplicationManagedBean' does not have the property 'getTable'.

        at javax.el.BeanELResolver.getBeanProperty(BeanELResolver.java:547)
        at javax.el.BeanELResolver.getValue(BeanELResolver.java:249)
        at javax.el.CompositeELResolver.getValue(CompositeELResolver.java:143)
        at com.sun.faces.el.FacesCompositeELResolver.getValue(FacesCompositeELResolver.java:64)
        at com.sun.el.parser.AstValue.getValue(AstValue.java:138)
        at com.sun.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:206)
        at javax.faces.component.UIData.getValue(UIData.java:582)
        ... 52 more
```


Property "getTable" existiert nicht, ist jedoch Methode in der Bean. Was is falsch?


----------



## robertpic71 (27. Mrz 2008)

> also die Methode getRows() existiert nicht in der (aktuellen) JDBC-API.



Result (javax.servlet.jsp.jstl.sql.Result) ist das Rückgabeobjekt von ResultSupport.toResult.

Ich habe es kurz angetestet: Sowohl Result, als auch die SortedHashMap von Result.getRows() können zurückgegeben werden.

dein Bean sollte etwa so aussehen:


```
..
public Result getTable() {
..
   Result result = ResultSupport.toResult(rset);  // rset = ResultSet von SQL
   // ResultSet, Statement und Connection closen, besser: finally
   return  result;
...
```



> Meine Frage ist eigentlich recht bescheiden:
> Wie kann ich die Daten, welche ich aus der Datenbank über die ManagedBean hergeholt hab, dem JSF übergeben??



Das du nicht gleich die richtige Antwort erhalten hast, hat 2 Gründe:

1.) JSF wird in Verbindung mit Datenbanken fast immer mit einem Persitenzframework (EJB, Hibernate) verwendet. Die Erfahrungswerte mit direkter JDBC-Anbindung sind wohl Mangelware - zudem die Implementierung nur für kleine Ergbnislisten taugt.

2.) Ohne Fehlermeldung, Logs kann man nur raten. Außerdem haben sich die Fehler während dieses Threads in deinen Codebespielen verändert.

noch falsch:  
statt:
<h:dataTable value="#{ApplicationManagedBean.*get*Table}" id="dbRow"> 

nur
<h:dataTable value="#{ApplicationManagedBean.table}" id="dbRow"> 

angeben, also imme ohne get. Zusammen mit den Änderungen oben, sollte es dann funktionieren.

/Robert


----------



## geht net.. (28. Mrz 2008)

Ich bekomme zwar keine Fehlermeldung, aber die JSF-Face zeigt nur die Spaltenüberschriften an, ohne Inhalt aus der Datenbank. Wenn ich mir die Datenbank-Inhalte per Konsole ausgebe, funktioniert es. Aber die Übergabe an JSF klappt einfach NICHT!


Meine ManagedBean:

```
import java.sql.*;

import javax.sql.*;
import javax.naming.*;
import javax.servlet.jsp.jstl.sql.*; 

public class ApplicationManagedBean {
   
   
    /** Creates a new instance of ApplicationManagedBean */
    public ApplicationManagedBean() {
    }
   
  
  //public static void main(String[] args) {
    public Result getTable() {
    try {
      // Parameter für Verbindungsaufbau definieren
      String driver = "org.apache.derby.jdbc.ClientDriver";
      String url = "jdbc:derby://localhost:1527/sample";
      String user = "app";
      String password = "app";
      // JDBC-Treiber laden
      Class.forName(driver);
      // Verbindung aufbauen
      Connection con;
      con = DriverManager.getConnection(url, user, password);
      // SQL-Anweisungen ausführen
      PreparedStatement stmt = con.prepareStatement("SELECT * FROM CUSTOMER");
      ResultSet rs = stmt.executeQuery();
      
      Result result = ResultSupport.toResult(rs);
     
      return result;
//      while (rs.next()){
//       System.out.println("ID "+rs.getString(1));
//       }
      //rs.close();
      //stmt.close();
    } catch(Exception ex) { ex.printStackTrace(); return (null);}
   
  } 
}
```


JSF:


```
<jsp:root
xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">


    <jsp:directive.page contentType="text/html" pageEncoding="UTF-8"/>

    
    <jsp:element name="text">
        <jsp:attribute name="lang">EN</jsp:attribute>
        <jsp:body>
         <f:view>
         
          <h:dataTable value="#{ApplicationManagedBean.table}" id="table">
            <h:column>
              <f:facet name="header">
               <f:verbatim>ID</f:verbatim>
              </f:facet>
             <h:outputText value="#{table.CUSTOMER_ID}"/>
            </h:column>
            
            <h:column>
              <f:facet name="header">
               <f:verbatim>AppDate</f:verbatim>
              </f:facet>
             <h:outputText value="#{table.ZIP}"/>
           </h:column>
            
          </h:dataTable>
         
         
       </f:view>
      </jsp:body>
    </jsp:element>

</jsp:root>
```


Noch was anderes:



> JSF wird in Verbindung mit Datenbanken fast immer mit einem Persitenzframework (EJB, Hibernate) verwendet. Die Erfahrungswerte mit direkter JDBC-Anbindung sind wohl Mangelware - zudem die Implementierung nur für kleine Ergbnislisten taugt.



Ist es nicht schneller, per JDBC direkt Kontakt zu knüpfen, als erst über nen OR-Mapper?[/quote][/code]


----------



## robertpic71 (28. Mrz 2008)

Ich sehe jetzt auch keinen Fehler mehr. :? 

1.) Bau  noch 2 System.out./logs vor und nach der SQL-Action ein, damit man weiß ob er überhaupt in den Bean geht.

Hier noch meinen funktionierenden Code:
Die JSP-Datei:


```
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%@ page language="java" contentType="text/html; charset=utf-8"%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Simple JSF Demo</title>

<link rel="stylesheet" type="text/css" href="../../css/example.css" />
</head>
<body>
<f:view>

	<h1>Simple JSF Demo</h1>
	<hr noshade="noshade" />

	<h:messages />
	<h:form id="personForm">

	</h:form>
	

	<hr noshade="noshade" />
	<h:dataTable value="#{SimpleManagedBean2.table}" var="row" border="1">

		<h:column>
			<f:facet name="header">
				<h:outputText value="Id" />
			</f:facet>
			<h:outputText value="#{row.id}" />
		</h:column>

		<h:column>
			<f:facet name="header">
				<h:outputText value="Beschreibung" />
			</f:facet>
			<h:outputText value="#{row.name}" />
		</h:column>

		<h:column>
			<f:facet name="header">
				<h:outputText value="Kürzel" />
			</f:facet>
			<h:outputText value="#{row.short}" />
		</h:column>

	</h:dataTable>

</f:view>
</body>
</html>
```

Der Javacode:

```
..
public class SimpleManagedBean2 {
    private DataSource ds;

    public SimpleManagedBean2 () {
	Context initContext;
	try {
	    initContext = new InitialContext();
	    Context envContext  = (Context)initContext.lookup("java:/comp/env");
	    ds = (DataSource)envContext.lookup("jdbc/postgres");
	} catch (NamingException e) {
	    System.out.println("Unable to get JDBC-Source, " + e);
	}
    }
    public Result getTable() {

	Statement stmt;
	ResultSet rset = null;
	Connection conn;
	try {
	    conn = ds.getConnection();
	    stmt = conn.createStatement();
	    rset = stmt.executeQuery("select * from accounts");
	    // SortedMap[] result = ResultSupport.toResult(rset).getRows();
	    Result result = ResultSupport.toResult(rset);

	    rset.close();
	    stmt.close();
	    conn.close();
	    // Achtung! Beispiel vereinfacht - alle close sollten mit
	    // if != null und SQLException in finally eingetragen sein
	    return  result;
	} catch (SQLException e) {
	    System.out.println("Fehler getTable " + e);
	    return null;
	}	
    }
```

Zur anderen Frage muss ich etwas ausholen (eigene Antwort).


----------



## nimo22 (1. Apr 2008)

hallo,

vielen dank. das hat diesmal geklappt!  

In Zukunft mach ich das lieber mit nem Persistenzframework..

viele grüße

nimo


----------

