Donnerstag, 26. Februar 2009

Hibernate Basisklasse für saubere Datenbanken und einfaches Testen

Ich stolpere öfters über Entity Objekte aus Hibernate, die zum Teil String, int, long, Integer oder auch Long als Datentyp haben. Ich will nicht auf die Nachteile von Strings oder ints als Primary Key eingehen, aber auf einen Weiteren: Einfaches Testen von Apps mit InMemory Mockdaten. Wer für jedes Entity Objekt einen eigenen InMemory Service schreiben muss, produziert zwar viele Zeilen Code, tut dabei aber nichts sinnvolles. Wenn man seine Entities von einer Abstrakten Basislasse ableitet, welche den PK, die Version, hashCode() und equals() enthält, kann man auch mit einer generischen InMemoryService Implementation arbeiten. Hier der Code:

@MappedSuperclass
public abstract class PersistantObject implements Serializable {
 private static final String TO_STRING_FORMAT = "ID: {0}, Version: {1}";
 private static final long serialVersionUID = -2782842909081144965L;
 protected Long id;
 protected Long version;

 public PersistantObject() {
 }

 @Id
 @GeneratedValue(strategy = GenerationType.IDENTITY)
 public Long getId() {
  return id;
 }

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

 @Version
 public Long getVersion() {
  return version;
 }

 public void setVersion(Long version) {
  this.version = version;
 }

 @Override
 public int hashCode() {
  if (id == null)
   return super.hashCode();
  final int prime = 31;
  int result = 1;
  result = prime * result + id.hashCode();
  return result;
 }

 @Override
 public boolean equals(Object obj) {
  if (obj == null)
   return false;
  if (!(obj.getClass().equals(this.getClass())))
   return false;
  PersistantObject checkMe = (PersistantObject) obj;
  if (this.id == null && checkMe.id == null)
   return super.equals(obj);
  if (this.id == null)
   return false;
  if (checkMe.id == null)
   return false;
  if (!id.equals(checkMe.id))
   return false;
  return version.equals(checkMe.version);
 }
 
 @Override
 public String toString() {
  return MessageFormat.format(TO_STRING_FORMAT, id, version);
 }
}

public class InMemoryService {
 private List data;

 public InMemoryService() {
  data = new ArrayList();
 }

 public void insert(T entity) {
  if (data.size() == 0)
   entity.setId(0L);
  else
   entity.setId(data.get(data.size() - 1).getId() + 1L);
  entity.setVersion(0L);
  data.add(entity);
 }

 public T get(Long id) throws ServiceException {
  for (T entity : data)
   if (entity.getId().equals(id))
    return entity;
  throw new ElementNotFoundException();
 }

 public void update(T entity) throws ServiceException {
  T old = get(entity.getId());
  if (!old.getVersion().equals(entity.getVersion()))
   throw new RuntimeException(
     "Concurrent modification");
  data.remove(old);
  entity.setVersion(entity.getVersion() + 1L);
  data.add(entity);
 }

 public void delete(Long id) throws ServiceException {
  data.remove(get(id));
 }

 public List getAll() {
  return data;
 }
}
Natürlich ist der InMemoryService nicht ThreadSafe, aber er ist ja auch nur zum Testen da :-)

Keine Kommentare:

  © Blogger template 'Morning Drink' by Ourblogtemplates.com 2008

Back to TOP