Lotus Notes / Domino Sonstiges > Java und .NET mit Notes/Domino

Java Web Frameworks

<< < (2/7) > >>

flaite:
Mark, das ist schön.
Aber diese DAO/Service Layer Geschichten sind echt nicht zum Spaß da.
Du machst ja jetzt quasi deinen Datenbankzugriff aus dem Frontend.

Bei mir sieht (als Beispiel ein Authentifizierungsfeature) so aus:

1. jsf Seite ruft Methode userReg.login in einem JSF backing bean auf (bei Klick auf "Anmelden button")

--- Code: ---<h:form id="login">
<table class="infoTable" width="80%">
    <tr>
    <td class="infoTableTD" width="30%">
      <h:outputLabel for="j_username" value="#{msg.name}"/>
      </td>
      <td class="infoTableTD" width="70%">
      <t:inputText id="j_username" forceId="true" value="#{userReg.name}" required="true" size="40" maxlength="40">
      <h:message for="j_username" styleClass="message"/>
      </t:inputText>
  </td>
</tr>
      <tr>
      <td class="infoTableTD">
      <h:outputLabel for="j_password" value="#{msg.pwd}"/>
</td>
<td class="infoTableTD">
<t:inputSecret id="j_password" forceId="true" value="#{userReg.pwd}" required="true" size="40" maxlength="40">
<h:message for="j_password" styleClass="message"/>
</t:inputSecret>
</td>
</tr>
<tr>
<td colspan="2" class="infoTableTD">
      <h:commandButton value="#{msg.login}" action="#{userReg.login}"  />
</td>
</tr>
</table>
</h:form>

--- Ende Code ---

Methode login in JSF Backing Bean, die wiederum die Methode login(String name, String pwd) in der Service Klasse userService aufruft.

--- Code: ---public String login() {
user = userService.login(name, pwd);


if (user == null) {

message("j_username", "login_message_user_not_found", name); // internationalisiert
return "failure";

} else {
context().getExternalContext().getSessionMap().put("user", user);
setId(user.getId());
return "success";
}
}

--- Ende Code ---
Methode userService.login(String name, String pwd), die wiederum als sagen wir wichtigste Methode userDao.findByNameAndPwd(String name, String pwd) aufruft.

--- Code: --- public User login(String name,
String pwd) {
// initDeferredClose();

User user = userDao.findByNameAndPwd(name, pwd);

user = addStockpositionsToUser(user);
user = addOffersOrdersToUser(user);

return user;
}

--- Ende Code ---
userDao:

--- Code: --- /**
* @see de.aja.fussi.model.UserDao#findByNameAndPwd(java.lang.String, java.lang.String)
*/
public User findByNameAndPwd(String name, String pwd) {
log.debug("getting user for name:" + name + "and PWD " + pwd);

List<User> list = (List<User>) getHibernateTemplate()
.findByNamedQueryAndNamedParam("findUserByNameAndPwd",
new String[] { "name", "pwd" },
new Object[] { name, pwd });

if (list.size() == 1) {
return (User) list.get(0);
} else {
return null;
}

}

--- Ende Code ---
(der untere Teil ist zugegeben nicht so toll, weil ich nicht weiss wo ich in Spring das getUniqueObject Feature von Hibernate finde).

Sieht zuerst ein bischen nach overkill aus, bringt aber eine Menge Flexibilität und Stabilität in die Geschichte, da auch die DAO und Service Klassen alle unter Einbindung der Datenbank regressionstestbar sind.
D.h. Wenn ich da was ändere, starte ich den Test und der kann mir genau sagen, ob das noch wie erwartet funktioniert. Dabei wird die Datenbank automatisch auf einen erwünschten Zustand gebracht.
Das ganze wird mit Spring zusammengebunden. Ach ja. Die Transactions werden quasi auf einen Schlag in Spring definiert:

--- Code: --- <bean id="transactionManager"
          class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>

<!-- deklarative Transaktion -->
<tx:advice id="txAdvice" transaction-manager="transactionManager"> 
<tx:attributes>
<tx:method name="get*" read-only="true" propagation="REQUIRED"/>
<tx:method name="find*" read-only="true" propagation="REQUIRED"/>
<tx:method name="select*" read-only="true" propagation="REQUIRED"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
        <aop:config>
<aop:pointcut id="txServiceMethodsUser" expression="execution(* de.aja.fussi.market.UserService*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txServiceMethodsUser"/>
</aop:config>

--- Ende Code ---

Separation of concern:
- JSF-> Internationalisierung
- Service Layer -> Transaktionen, Aufruf der DAO Methoden, Validierung der Eingaben, Business Logic
- DAO Layer -> Hibernate Aufrufe

Die einzelnen Methoden sind übersichtlich. Jede Klasse hat klare Aufgaben (und nicht Web-Präsentation, DB-Zugriff, Transaktionen gleichzeitig).
In den Schichten gibt es keine zyklischen Abhängigkeiten.

Die JSF-Backing Beans sprechen nur die Service Klassen an, (aber nicht die DAO Klassen).
Die Service Klassen sprechen nur die DAO Klassen an (aber nicht die JSF Backing Beans)
Die DAO Klassen kennen die Klassen der beiden anderen Layer überhaupt nicht.

flaite:
Und jetzt Testen. Ich möchte sichergehen, dass ich jederzeit prüfen will, dass die Methode login(String Name, String pwd) von UserService funktioniert.

In der Klasse UserServiceTest, die eine Spring-Klasse überschreibt, die wiederum von junit-Testcase erbt habe ich diese Methode, um die Daten der Datenbank in einen sauberen Status zu bringen:

--- Code: ---@Override
public void onSetUp() throws Exception {

jdbcTemplate = new JdbcTemplate(dataSource);

JdbcHelper.cleanDatabase(jdbcTemplate);
JdbcHelper.fillStocks(jdbcTemplate, "copa");

}

--- Ende Code ---

und eine Methode, die die Funktionalität testet. Hier wird erst ein User registriert und dann per log-in geprüft, ob ein login funktioniert. Das alles ohne einen Web-Container wie Tomcat zu starten:

--- Code: ---public void testLogin() throws DataValidationException {
User user = new User();
String name = "axel";
user.setName(name);
user.setPwd("pwd");
user.setMoney(20000.00);

userService.registerNewUser(user);

User user1 = userService.login(name, "pwd");
assertEquals(user, user1);
}

--- Ende Code ---

Ich kann dann auch noch Testmethoden schreiben, die prüfen wie die Anwendung reagiert, wenn eine falsche User/Passwort Kombination eingegeben wurde.

flaite:
Wenn ich in meiner Anwendung jsf nicht mehr will und stattdessen struts2 oder zk. Ich kann das JSF Zeugs jederzeit rausschmeissen und stattdessen struts2 oder zk Zeugs schreiben, das meinen Service Layer anspricht.
Wenn ich keine Lust mehr auf Hibernate habe und stattdessen lieber ibatis-SQLmaps oder Spring-JDBC verwenden möchte, muss ich nur die DAO Schicht verändern und sicherstellen, das die Methoden gleich heissen wie früher. Das externe Interface der Klassen der DAO Schicht ist unabhängig von Hibernate.
Die Service Schicht selbst ist sowieso völlig entkoppelt von technischen Belangen wie zufällige Auswahl der verwendeten Frameworks.

flaite:
Hi,

eigentlich meine ich was in diesen Ruby-on-Rails Werbespots 2 bis 4 thematisiert wird.
http://notepad.emaillink.de/2007/05/23/ruby-on-rails-vs-php-vs-java/

Für dieses Zusammenklumpen was nicht zusammengehört ist eigentlich jene "einfache" Plattform, die mit P anfängt und mit P aufhört berühmt. Jenes Framework, das 2003/4 sehr stark aber seitdem so gar nicht mehr bei dem von mir sehr geschätzten Enterprise-Architekten Volker Weber erwähnt wird (vowe.de, try search).

Komischerweise setzen sich die strukturierten Lösungen dann irgendwann gegen die "einfachen" Lösungen durch. Ruby-On-Rails setzt ähnlich wie Java auf einen geschichteten Ansatz.

Wenn ich in meiner Hibernate basierten Anwendung von Posgres auf eine andere DB wie Oracle oder DB2 umsteigen will, muss ich an diesen Stellen Änderungen vornehmen:
1. Datei jdbc.properties:
db.driverClass=org.postgresql.Driver
db.jdbcUrl=jdbc:postgresql:fussi
db.user=neil
db.password=wonttellucauseitsasecret
hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect

2. Die eine von mir benutzte stored procedure in Oracle neu schreiben.

3. Evtl. Automatic key generation Strategie in hbm.xml Dateien:

4. Evtl. bei den Queries.

flaite:
Hallo Mark,

wenn ich eins in den letzten 4 Jahren gelernt habe sind es diese 3 einfachen Regeln:
1. Frameworks können nicht einfach so eingesetzt werden. Es gibt vielmehr einen Satz von best practices, die zu implementieren sind. Man kann dagegen verstossen, aber dann muss man wissen warum. Für Hibernate/Spring sind diese Regeln implementiert in den Beispielanwendungen CaveatEmptor (hibernate) und Petstore (Spring) oder in Anwendungen aus dem code-download von guten Büchern wie dem Spring & Hibernate Arbeitsbuch oder Richardson, Pojos in Action. Es gibt mehr Beispiele, aber die Regeln sind da sowieso alle gleich.
2. Die Regeln sind keine hyper-übertriebenen 3-fachen Ridberger mit gute B-Note sondern die Basics. Reale Anwendungen haben zusätzliche Anforderungen on top. Basics bedeutet: Die joseki-artigen Zugfolgen, die Du kennst. The safe haven. Lo fundamental.
3. Sobald Entwickler zu weit diese Regeln verlassen, wirds irgendwann unweigerlich Chaos geben (Unzufriedenheit, geplatzte Zeitplanungen, etc.)

Die Wahl von Frameworks ist THE LAW nachgeordnet. Ob ich jetzt Hibernate oder Spring-JDBC verwende? EGAL. Eine neue Dimension von EGAL ist mir gerade heute klargeworden. Die Anwendung verwendet Hibernate. Die Integrationstest Spring-JDBC. Vermutlich könnte ich das ganze in 4 Tagen nach EJB3 und SEAM umschreiben. All das hat sich soweit in Richtung eines UNIFIED LAW entwickelt, dass weite Teile der Struktur und des Codes der Anwendung gleich bleiben würden.

Gruß Axel

Navigation

[0] Themen-Index

[#] Nächste Seite

[*] Vorherige Sete

Zur normalen Ansicht wechseln