Hibernates, DAO, Spring Struktur und Problem

C

crossib

Hallo zusammen,

hab mir in letzter Zeit Hibernates, DAO und Spring angeschaut. Funktioniert bis jetzt eigentlich auch ganz gut alles. Nun will ich aber mein Code mal posten, damit ihr beurteilen könnt, ob der Aufbau von der Struktur auch okay ist.

Habe 2 POJOs: User, Role

Role
Code:
package user;

import java.io.Serializable;
import java.text.MessageFormat;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.*;

@SuppressWarnings("serial")
@Entity
public class Role implements Serializable{
	
	private Integer roleid;
	private String description;
	private Set<User> user = new HashSet<User>();
	
	public Role() {
	}
	
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	public Integer getRoleid() {
		return roleid;
	}

	public void setRoleid(Integer roleid) {
		this.roleid = roleid;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}
	
	@OneToMany(mappedBy="role")
	public Set<User> getUser() {
		return user;
	}
	
	public void setUser(Set<User> user) {
		this.user = user;
	}
	
	public String toString() {
		return MessageFormat.format("Role: {0} {1}",
				new Object[]{roleid, description});
	}
}

User
Code:
package user;

import java.io.Serializable;
import java.text.MessageFormat;

import javax.persistence.*;

@SuppressWarnings("serial")
@Entity
public class User implements Serializable{

	private int userid;
	private String firstname;
	private String lastname;
	private String email;
	private String username;
	private String password;
	private Role role;
	
	public User() {
	}
	
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	public int getUserid() {
		return userid;
	}

	public void setUserid(int userid) {
		this.userid = userid;
	}

	public String getFirstname() {
		return firstname;
	}

	public void setFirstname(String firstname) {
		this.firstname = firstname;
	}

	public String getLastname() {
		return lastname;
	}

	public void setLastname(String lastname) {
		this.lastname = lastname;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	@ManyToOne
	@JoinColumn
	public Role getRole() {
		return role;
	}

	public void setRole(Role role) {
		this.role = role;
	}

	public String toString() {
		return MessageFormat.format("{0}: userid={1}, firstname={2}, lastname={3}, email={4}, username={5}, password={6}", 
				new Object[] { getClass().getSimpleName(), userid, firstname, lastname, email, username, password });
	}
	

}

Dann dazu natürlich die 2 DAOs

RoleDAO
Code:
package user;

import java.util.List;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

public class RoleDAO extends HibernateDaoSupport {

	public void makePersistent(Role role) {
		getHibernateTemplate().saveOrUpdate(role);
	}

	public void makeTransient(Role role) {
		getHibernateTemplate().delete(role);
	}
	
	public void deleteAllRoles() {
		getHibernateTemplate().deleteAll(findAllRoles());
	}

	@SuppressWarnings("unchecked")
	public List findAllRoles() {
		return getHibernateTemplate().loadAll(Role.class);
	}

}

UserDAO
Code:
package user;

import java.util.List;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

public class UserDAO extends HibernateDaoSupport{
	
	public void makePersistent(User user) {
		getHibernateTemplate().saveOrUpdate(user);
	}

	public void makeTransient(User user) {
		getHibernateTemplate().delete(user);
	}
	
	public void deleteAllUsers() {
		getHibernateTemplate().deleteAll(findAllUsers());
	}

	@SuppressWarnings("unchecked")
	public List findAllUsers() {
		return getHibernateTemplate().loadAll(User.class);
	}
}

Dann die Service

RoleService
Code:
package user;

import java.util.List;

public class RoleService {

	private RoleDAO roleDAO;

	public RoleDAO getRoleDAO() {
		return roleDAO;
	}

	public void setRoleDAO(RoleDAO roleDAO) {
		this.roleDAO = roleDAO;
	}

	public void saveRole(Role role) {
		roleDAO.makePersistent(role);
	}

	public void deleteRole(Role role) {
		roleDAO.makeTransient(role);
	}

	public void deleteAllRoles() {
		this.roleDAO.deleteAllRoles();
	}

	@SuppressWarnings("unchecked")
	public List findAllRoles() {
		return roleDAO.findAllRoles();
	}
	
}

UserService
Code:
package user;

import java.util.List;

public class UserService {

	private UserDAO userDAO;

	public UserDAO getUserDAO() {
		return userDAO;
	}

	public void setUserDAO(UserDAO userDAO) {
		this.userDAO = userDAO;
	}

	public void saveUser(User user) {
		userDAO.makePersistent(user);
	}

	public void deleteUser(User user) {
		userDAO.makeTransient(user);
	}

	public void deleteAllUsers() {
		this.userDAO.deleteAllUsers();
	}

	@SuppressWarnings("unchecked")
	public List findAllUsers() {
		return userDAO.findAllUsers();
	}
	
}

Und der Testcase

Test
Code:
package user;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

	public static void main(String[] args) {
		ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
		RoleService RoleService = (RoleService)ctx.getBean("roleService");
		UserService UserService = (UserService)ctx.getBean("userService");

		RoleService.deleteAllRoles();
		Role role = new Role();
		role.setDescription("Kunde");
		RoleService.saveRole(role);
		
		User user = new User();
		user.setFirstname("xxx");
		user.setLastname("xxx");
		user.setPassword("xxx");
		user.setEmail("xxx@xx.xx");
		user.setUsername("xx");
	    UserService.saveUser(user);

	    user.setRole(role);
	    role.getUser().add(user);
	}

}

applicationContext.xml
Code:
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans default-autowire="no" default-lazy-init="false" default-dependency-check="none">
	
	<bean id="roleDAO" class="user.RoleDAO">
		<property name="sessionFactory"><ref bean="sessionFactory"/></property>
	</bean>
	
	<bean id="roleServiceTarget" class="user.RoleService">
		<property name="roleDAO"><ref bean="roleDAO"/></property>
	</bean>
	
	<bean id="roleService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
		<property name="transactionManager"><ref local="transactionManager"/></property>
		<property name="target"><ref local="roleServiceTarget"/></property>
		<property name="transactionAttributes">
        	<props>
            	<prop key="save*">PROPAGATION_REQUIRED</prop>
            	<prop key="delete*">PROPAGATION_REQUIRED</prop>
            	<prop key="rename*">PROPAGATION_REQUIRED</prop>
            	<prop key="print*">PROPAGATION_REQUIRED</prop>
			</props>
		</property>
	</bean>
	
	<bean id="userDAO" class="user.UserDAO">
		<property name="sessionFactory"><ref bean="sessionFactory"/></property>
	</bean>
	
	<bean id="userServiceTarget" class="user.UserService">
		<property name="userDAO"><ref bean="userDAO"/></property>
	</bean>
	
	<bean id="userService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
		<property name="transactionManager"><ref local="transactionManager"/></property>
		<property name="target"><ref local="userServiceTarget"/></property>
		<property name="transactionAttributes">
        	<props>
            	<prop key="save*">PROPAGATION_REQUIRED</prop>
            	<prop key="delete*">PROPAGATION_REQUIRED</prop>
            	<prop key="rename*">PROPAGATION_REQUIRED</prop>
            	<prop key="print*">PROPAGATION_REQUIRED</prop>
			</props>
		</property>
	</bean>

	<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
		<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration"/>
		<property name="configLocation" value="classpath:hibernate.cfg.xml"/>
	</bean>

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

</beans>

hibernate.cfg.xml
Code:
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
	<session-factory>
		<property name="connection.url">jdbc:mysql://localhost/wcms</property>
		<property name="connection.username">root</property>
		<property name="connection.password">mysql</property>
		<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
		<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
		<property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
		<property name="current_session_context_class">thread</property>
		<property name="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property>
		<property name="hibernate.hbm2ddl.auto">create</property>
		<property name="hibernate.show_sql">true</property>
		<mapping class="user.Role"/>
		<mapping class="user.User"/>
   </session-factory>
</hibernate-configuration>



So nun meine Fragen:

1.) Stimmt alles soweit strukturmäßig?

2.) In meinem Testcase erstelle ich ja erst eine Rolle dann den Benutzer. Anschließend will ich eine Beziehung herstellen. Leider schreibt er mit beim User im RoleId = Null rein, anstatt die ID. Bei ganz normalen Hibernate ohne DAO und Spring hat es funktioniert. ?!

3.) Verbesserungsvorschläge, Links zu Tutorials?, Eigene Beispielprojekte?!

Danke im voraus!
 
Nicht ganz schlecht, aber jede Menge Verbesserungspotential ;):

1. Warum nutzt du plain Hibernate und nicht Hibernate hinter JPA? Damit würdest du auf einen Standard setzen und die Dependency zu Hibernate im Code wegbekommen.

2. @SuppressWarnings("serial") ist nie eine gute Idee

3. Es macht Sinn, für die DAOs Interfaces einzuziehen. So kannst du den Service Unittesten indem du das DAO einfach mockst.

4. Es macht meiner meinung nach keinen Sinn für beide Entities DAOs und Services zu haben. Ein Service sollte grobgranularer sein als das DAO und sich an den Bedürfnissen des Clients orientieren. Und ich vermute, ein Client würde über den Service UserManagement auch gern Rollen verwalten.

5. Services sollten auch Interfaces bekommen. Nur so kannst du ohne CGLIB die Transaktion an den Service nageln. Ausserdem sorgt das dann halt wieder dafür, dass die Clients ohne Implementierung getestet werden können.

6. Für den Test würde ich auf eine Hilfsklasse von Spring zurückgreifen. Wenn du nur die DAOs Integrationstesten willst ist AbstractTransactionalSpringContextTests die Basisklasse mit der du arbeiten solltest. Die macht automatisch für jede Testmethode eine Transaktion auf und rollt die danach zurück. Willst du den Service (und damit auch die Transaktionskonfiguration) testen, reicht AbstractDependencyInjectionSpringContextTests als Basisklasse. Damit gibst du durch überschreiben von getConfigLocation() den Pfad zum Spring Konfigurationsfile an und kannst dann einfach Setter für die Komponenten anbieten und sie dir injizieren lassen. Kein manueller Contextlookup notwendig.

7. Zur Konfiguration: die Transaktionskonfiguration geht mittlerweile eleganter (http://static.springframework.org/s...on.html#transaction-declarative-first-example) die Verbindungsdaten würde ich auch über eine DataSource konfigurieren (die tauscht sich leichter aus). JPA machts ein wenig einfacher.

Gruß
Ollie
 
Zurück