Hibernate 3, Events, Listener

DerGrinsemann

Mitglied
Hallo!

Ich habe folgendes Mapping

Code:
@ManyToOne(optional=true, fetch=FetchType.LAZY)
@JoinColumn(name="createdByAccountId", nullable=true, insertable=true, updatable=false)
@ForeignKey(name="fk_createdByAccountId")
private UserAccount createdByAccount = null;

@ManyToOne(optional=true, fetch=FetchType.LAZY)
@JoinColumn(name="lastModifiedByAccountId", nullable=true, insertable=false, updatable=true)
@ForeignKey(name="fk_lastModifiedByAccountId")
private UserAccount lastModifiedByAccount = null;

Nun möchte ich mittels eines Hibernate-Events, denn Wert der Spalte "createdByAccountId" setzen, also nicht die Eigenschaft "createdByAccount" (UserAccount).

Hintergrund: Die ID kommt aus einem Spring Security (acegi) Context und wird in einem DTO gehalten und um nicht jedesmal den passenden UserAccount laden zu müssen, möchte ich hier nur die ID setzen.

Hat jemand eine Idee für mich?

Marco
 
Hallo,

das was du da machen willst nennt man Auditing bzw. Annotieren / Belegen / Historisieren von Veränderungen an Stamm- / Bewegungsdaten mit zusatzinformationen (Wer hat wann was angelegt, geändert, gelöscht).
Üblicherweise findet man dann folgende zusätzliche Properties an den Entitäten:
CreateUser
CreateDate
ModifyUser
ModifyDate

Es gibt natürlich (wie immer) mehrere Möglichkeiten das zu realisieren.
Beispielsweise über einen Hibernate-Interceptor einem eigenen Interceptor, oder (vorzugsweise) per AspectJ. Die Audit-Properties werden den einzelnen Entities per AOP Introduction hinzugefügt. In einem entsprechenden Audit-Aspect kann man nun einen Advice Formulieren der diese per Introduction hinzugefügten Properties befüllt. Dort kannst du dann über SecurityContextHolder.getSecurityContext().getAuthentication(); auf den aktuellen SecurityContext zugreifen.
Gruß Tom
 
Danke für deine Antwort! Die Zeitwerte erledige ich mittels Events so

Code:
public class ChronologyEntityListener implements PreInsertEventListener, PreUpdateEventListener {

	private static final long serialVersionUID = 984071745542183464L;

	/* (non-Javadoc)
	 * @see org.hibernate.event.PreInsertEventListener#onPreInsert(org.hibernate.event.PreInsertEvent)
	 */
	public boolean onPreInsert(PreInsertEvent event) {
		if (event.getEntity() instanceof ChronologyTimstampInterface) {
			Object entity = event.getEntity();
			if (entity instanceof ChronologyTimstampInterface) {
				String[] names = event.getPersister().getPropertyNames();
				Calendar now = Calendar.getInstance();
				for (int x=0; x < names.length; x++) {
					if ("created".equals(names[x])) {
						((ChronologyTimstampInterface) entity).setCreated(now);
						event.getState()[x] = now;
						break;
					}
				}
			}
		}
		return false;
	}

	/* (non-Javadoc)
	 * @see org.hibernate.event.PreUpdateEventListener#onPreUpdate(org.hibernate.event.PreUpdateEvent)
	 */
	public boolean onPreUpdate(PreUpdateEvent event) {
		if (event.getEntity() instanceof ChronologyTimstampInterface) {
			Object entity = event.getEntity();
			if (entity instanceof ChronologyTimstampInterface) {
				String[] names = event.getPersister().getPropertyNames();
				Calendar now = Calendar.getInstance();
				for (int x=0; x < names.length; x++) {
					if ("lastModified".equals(names[x])) {
						((ChronologyTimstampInterface) entity).setLastModified(now);
						event.getState()[x] = now;
						break;
					}
				}
			}
		}
		return false;
	}

Nun möchte ich das "selbe" mit dem User-Account machen.

Natürlich könnte ich mir aus meinem Security-Context die UserID holen, dann in einer zweiten Session den User aus DB holen und dann mittels setCreatedByAccount(userAccount) setzen. Meine Überlegung war einfach nur die ID des Users in einem Event zu setzen.

Marco

P.S. Hast du vielleicht ein Beispiel bzgl. "AOP Introduction " für mich?
 
Hallo,

ich würde mal versuchen Acegi ein eigenes Authentication Objekt mit unterzuschieben wo eben auch die ID mitgefüllt ist. Dann hast du ja was du brauchst beispielsweise über einen Custom UserDetailsService erreichen der eine passende UserDetails Implementierung zurück gibt (mit public int getId() ....)
Ansonsten könntest du natürlich einfach hingehen und den Username anstatt der id mit dem Satz speichern (sofern dieser eindeutig ist).

Gruß Tom
 
Wie der Zufall es will schreibe ich gerade an einem DAO Klassen Framework für generische DAOs. Darins ist genau so ein Advice implementiert:

http://trac.synyx.de/hades/browser/...ynyx/hades/domain/support/AuditionAdvice.java

(Achtung, das ist nicht die aktuellste Version. Kann momentan leider nichts einchecken, da wir gerade die SSH Keys neu generieren)

Interessant sind vor allem 3 Interfaces. Auditable ist die entity, die mit den Informationen versehen werden soll. Es definiert also Getter und Setter für created / modified usw. Der Auditor ist lediglich Persistable muss also eigentlich nur eine Id haben. Den Auditor bekommt der Advice über ein Interface CurrentUserAware, dass eine Implementierung haben muss, die z.B. über den aktuellen User bescheid weiß.

Der Pointcut hängt den Advice an Methoden des Interfaces GenericDao und dessen Methoden die mit "save" anfangen.

Die Dependency zu Spring (über @Required) fliegt mit RC2 auch wieder raus. Der Advice setzt einfach nur die Daten, falls kein CurrentUserAware injected wird. D.h. danach kann man den Advice auch komplett ohne Spring nutzen.

Schau dich ruhig mal um, im Trac. Die Lib wird vermutlich in 1 bis 2 Wochen OpenSource gehen unter Apache 2 Lizenz. Ende der Woche kommt ein RC2 mit verschiedenen bugfixes und Verbesserungen.

Gruß
Ollie
 
Hallo,

schau mal hier:
Entity:
Java:
/**
 * 
 */
package de.tutorials.domain;

/**
 * @author Thomas.Darimont
 *
 */
public class Entity {
	long id;
	

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}
	
	
}

Auditable:
Java:
/**
 * 
 */
package de.tutorials.security.auditing;

/**
 * @author Thomas.Darimont
 *
 */
public interface IAuditable {
	long getCreateUser();
	void setCreateUser(long createUser);
	long getModifyUser();
	void setModifyUser(long modifyUser);
	long getCreateTimeStamp();
	void setCreateTimeStamp(long createTimeStamp);
	long getModifyTimeStamp();
	void setModifyTimeStamp(long modifyTimeStamp);
}

Auditable-Aspect:
Java:
/**
 * 
 */
package de.tutorials.aspects;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.DeclareParents;

import de.tutorials.security.auditing.Auditable;
import de.tutorials.security.auditing.IAuditable;

import de.tutorials.domain.Entity;

/**
 * @author Thomas.Darimont
 * 
 */
@Aspect
public class Auditing {
	
	@DeclareParents(value="de.tutorials.domain.Entity",defaultImpl=Auditable.class)
	public static IAuditable auditableMixin;
	
	@Before("execution(* de.tutorials.AOPIntroductionExample.save(..)) && args(entity)")
	public void audit(Entity entity){
		IAuditable auditable = (IAuditable)entity;
		
		if(entity.getId() == -1){ //isNew() ?
			System.out.println("audit new: "+ entity);
			auditable.setCreateUser(1);
			auditable.setCreateTimeStamp(System.currentTimeMillis());
		}else{
			System.out.println("audit existing: "+ entity);
			auditable.setModifyUser(2);
			auditable.setModifyTimeStamp(System.currentTimeMillis());
		}
	}
}

Auditable:
Java:
/**
 * 
 */
package de.tutorials.security.auditing;


public class Auditable implements IAuditable {
	private long createUser;
	private long modifyUser;
	private long createTimeStamp;
	private long modifyTimeStamp;

	/**
	 * @return the createUser
	 */
	public long getCreateUser() {
		return createUser;
	}

	/**
	 * @param createUser
	 *            the createUser to set
	 */
	public void setCreateUser(long createUser) {
		this.createUser = createUser;
	}

	/**
	 * @return the modifiyUser
	 */
	public long getModifyUser() {
		return modifyUser;
	}

	/**
	 * @param modifiyUser
	 *            the modifiyUser to set
	 */
	public void setModifyUser(long modifiyUser) {
		this.modifyUser = modifiyUser;
	}

	/**
	 * @return the createTimeStamp
	 */
	public long getCreateTimeStamp() {
		return createTimeStamp;
	}

	/**
	 * @param createTimeStamp
	 *            the createTimeStamp to set
	 */
	public void setCreateTimeStamp(long createTimeStamp) {
		this.createTimeStamp = createTimeStamp;
	}

	/**
	 * @return the modifyTimeStamp
	 */
	public long getModifyTimeStamp() {
		return modifyTimeStamp;
	}

	/**
	 * @param modifyTimeStamp
	 *            the modifyTimeStamp to set
	 */
	public void setModifyTimeStamp(long modifyTimeStamp) {
		this.modifyTimeStamp = modifyTimeStamp;
	}
}

Unser Beispiel:
Java:
/**
 * 
 */
package de.tutorials;

import de.tutorials.domain.Entity;
import de.tutorials.security.auditing.IAuditable;

/**
 * @author Thomas.Darimont
 *
 */
public class AOPIntroductionExample {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Entity entity = new Entity();
		Entity anotherEntity = new Entity();

		save(entity);
		save(anotherEntity);
		
		save(entity);
		save(anotherEntity);
	}
	
	public static void save(Entity entity){
		System.out.println("Save: " + entity);
		entity.setId(entity.hashCode());
	}
	
}

Ausgabe:
Code:
audit new: de.tutorials.domain.Entity@190d11 Id: -1
Save: de.tutorials.domain.Entity@190d11 Id: -1
audit new: de.tutorials.domain.Entity@de6ced Id: -1
Save: de.tutorials.domain.Entity@de6ced Id: -1
audit existing: de.tutorials.domain.Entity@190d11 Id: 1641745
Save: de.tutorials.domain.Entity@190d11 Id: 1641745
audit existing: de.tutorials.domain.Entity@de6ced Id: 14576877
Save: de.tutorials.domain.Entity@de6ced Id: 14576877

Gruß Tom
 

Neue Beiträge

Zurück