sobota, 5 stycznia 2013

Fluent builder do generowania fluent builderów cz.2

Tak jak wspominałem w części 1. postaram się przedstawić, jak można tworzyć buildery do klas, które nie korzystają z setterów i nie są w ciele klasy.

Może od razu na konkretnym przykładnie dla klasy:
public class Order {

 private List<OrderItem> items;
 private Date createDate;
 private boolean realized;
 
 public void orderRealized(){
  //very complex implementation
  realized = true;
 }
}
Wykorzystując trochę przerobiony mechanizm generowanie builderów:
AbstractBuilderGenerator.forClass(Order.class)
    .withStaticCreate(true)
    .printBuilder();
Wynikiem jest interfejs dla naszego przyszłego buildera, w postaci abstrakcyjnej klasy i abstrakcyjnych metod:
public abstract class OrderBuilder extends AbstractBuilder<Order, OrderBuilder>{

 public abstract OrderBuilder withItems(List<OrderItem> items);
 public abstract OrderBuilder withCreateDate(Date createDate);
 public abstract OrderBuilder withRealized(boolean realized);
 public static OrderBuilder create(){
  return AbstractBuilderFactory.createImplementation(OrderBuilder.class);
 }
}
Kod z sysout'a należy skopiować do pliku i mamy klasę buildera, którą możemy umieścić sobie w dowolnym miejscu, najlepiej gdzieś w folderze od testów.
OrderBuilder dodatkowo został wygenerowany z metodą create, która wywołuje fabrykę tworzącą instancję buildera na podstawie jego abstrakcyjnego interfejsu. To jest miejsce gdzie się dzieje trochę czarnej magii, tj. refleksje, dynamiczne proxy, generics. Z drugiej strony, który framework na tym w dzisiejszych czasach nie bazuje? Fabryka wykorzystuje domyślny konstruktor do utworzenia obiektu docelowego (może być prywatny), ale jeśli klasa nie ma takiego konstruktora, to można użyć przeładowanej metody createImplementation i przekazać obiekt docelowy utworzony przez jakikolwiek konstruktor.

Samo wykorzystanie:
Order realizedOrder = OrderBuilder.create().withRealized(true).build();
Dzięki temu, że interfejs buildera nie jest tak de facto nie interfejsem a klasa abstrakcyjną mamy pełną swobodę w tworzeniu dodatkowych metod buildera, które mogą korzystać z metod domenowych obiektu.

Zmodyfikowany interfejs:
public abstract class OrderBuilder extends AbstractBuilder<Order, OrderBuilder> {

 public abstract OrderBuilder withItems(List<OrderItem> items);
 public abstract OrderBuilder withCreateDate(Date createDate);
 public abstract OrderBuilder withRealized(boolean realized);

 public OrderBuilder realized() {
  Order order = targetObject();
  order.orderRealized();
  // other methods
  return builder();
 }

 public static OrderBuilder create() {
  return AbstractBuilderFactory.createImplementation(OrderBuilder.class);
 }
}
Korzystając z metod targetObject i builder (zwracające odpowiednio referencje na obiekt docelowy i samego buildera), możemy konstruować dodatkowe metody tak, aby nie przerywały mechanizmu fluent. Wykorzystanie:
Order realizedOrder = OrderBuilder.create()
    .realized()
    .withCreateDate(new Date())
    .build();

Zapraszam do forkowania i komentowania co jeszcze można by tu było usprawnić, biblioteczka jest właściwie w powijakach. Najwygodniej byłoby gdyby generowanie nie odbywało się do sysouta, tylko od razu do pliku. Niestety nie mam żadnego doświadczenia w tworzeniu pluginów eclipsowych...

piątek, 4 stycznia 2013

Fluent builder do generowania fluent builderów cz.1

Na samym początku dla wszystkich, którzy przykładają duża wagę do definicji, nie będzie tutaj mowa o wzorcu builder w pełnej swojej krasie, a raczej o jego uproszczonej wersji, która jest jednak częściej spotykana.

Stosując TDD, prędzej czy później dojdziemy do sytuacji, w której potrzebujemy spreparować sobie jakiś obiekt (niech to będzie np. encja domenowa) do testów. Jeśli obiekt ma dobrą enkapsulację, to nie mamy bezpośredniego dostępu do wszystkich pól, co za tym idzie niektóre z nich zostaną poprawnie zainicjowane dopiero po wywołaniu szeregu metod domenowych. Przykładowo jeśli potrzebujemy encje Order, ale jako zrealizowane zamówienie (pole realized = true), tylko i wyłączenie na potrzebuje testów, to dla mnie stratą czasu jest wywoływanie wielu metod (które nie rzadko są dość skomplikowane) tylko po to żeby ta flaga miała odpowiednią wartość.

Drogą na skróty jest tutaj użycie buildera, który znajduje się wewnątrz danej klasy, dzięki temu nie musimy sztucznie dodawać metod "set" do obiektu. Tworzenie takich builderów jest dość nudnym zajęciem dlatego proponuje generator builderów, który zrobi to za nas.

Dla klasy:
public class Sample implements Serializable {

 private static final long serialVersionUID = 2631948252607310591L;
 private final String SOME_CONSTANCE = "SOME_CONSTANCE";

 private int id;
 private String name;
 private Map<String, List<Integer>> map;
}
Wykorzystując generator, który domyślnie drukuje do konsoli:
SimpleBuilderGenerator.forClass(Sample.class)
    .withMethodPrefix("with")
    .printBuilder();
Po wklejeniu tego co zostało wygenrowane do głownej klasy otrzymujemy:
public class Sample implements Serializable {

 private static final long serialVersionUID = 2631948252607310591L;
 private final String SOME_CONSTANCE = "SOME_CONSTANCE";

 private int id;
 private String name;
 private Map<String, List<Integer>> map;
 
 /** 
  * Fluent builder for Sample
  * @formatter:off
  */
 public static Builder builder() {
  return new Builder();
 }

 public static class Builder {

  private final Sample sample = new Sample();

  public Builder withId(int id) { sample.id = id; return this; }
  public Builder withName(String name) { sample.name = name; return this; }
  public Builder withMap(Map<String, List<Integer>> map) { sample.map = map; return this; }

  public Sample build() { return sample;}
 }
 /** @formatter:on */
}
I samo wykorzystanie:
Sample sample = Sample.builder().withName("name").build();
Wszystko fajnie pięknie, ale taki builder ma jedną zasadniczą wadę - jego kod znajduje się w klasie produkcyjnej. Wymaga to od programisty przestrzegania zasady, żeby nie używać tego mechanizmu w kodzie produkcyjnym.

Właściwie cały ten post to preludium do właściwej treści, czyli tworzenie builderów, które są poza klasami produkcyjnymi i nie wymagają setterów, ale o tym w części 2.

Kod źródłowy generatora można znaleźć tutaj.

środa, 7 listopada 2012

Iterables i reduce

Ostatnio uczestnicząc w kursie "JavaScript dla Javowców" doznałem pewnego olśnienia. Otóż JavaScript, którego nadal nie lubię (choć już trochę bardziej szanuje), ma kilka dość dobrych pomysłów, które warto przenieść do świata Javy.

Konkretnie mówię tu o funkcyjnym podejściu do operacji na tablicach/hashmapach w JS. Ktoś na szczęście już dawno wpadł na ten pomysł i mamy bibliotekę Google Guava, która jest dość dobrze poważana w świecie Javy.

Klika analogii JS => Java:

array.map(...) => Iterables.transform(...)
array.filter(...) => Iterables.filter(...)

W Guavie brakuje mi tylko 1 funkcji do pełni szczęścia, tj. odpowiednika funkcji array.reduce(...). Teoretycznie jest w planach jej prowadzenie, ale póki co jej nie ma, więc można szybko stworzyć własną implementacje.

import com.google.common.base.Preconditions;

public class Iterables {

 /**
  * redukuje przekazany {@link Iterable} do obiektu A
  * @param iterable co redukujemy
  * @param initialValue wartosc inicjalna, najlepiej neutralna dla danej                 operacji 0 dla dodawania, 1 dla mnożenia, itd 
  * @param reducer funkcja redukująca
  * @return wartosc zredukowana
  */
 public static <A, B> A reduce(Iterable<B> iterable, A initialValue, Reducer<A, B> reducer) {

  Preconditions.checkNotNull(initialValue);
  A result = initialValue;
  for (B b : iterable) {
   result = reducer.reduce(result, b);
  }
  return result;
 }
}

/**
 * funkcja redukująca
 * @param <A> klasa obiektu docelowego
 * @param <B> klasa obiektu wejsciowego
 */
public interface Reducer<A, B> {

 /**
  * redukuje parametr wejsciowy do docelowego
  *
  * @param to aktualny obiekt
  * @param from obiekt wejsciowy
  * @return zredukowany obiekt docelowy
  */
 A reduce(A current, B from);
}

I samo wykorzystanie:
List<Integer> someNumbers = Lists.newArrayList(1, 2, 3);
Reducer<Integer, Integer> sum = new Reducer<Integer, Integer>() {

 @Override
 public Integer reduce(Integer current, Integer from) {
  return current + from;
 }
};
Integer sumValue = Iterables.reduce(someNumbers, 0, sum);
assertThat(sumValue).isEqualTo(6);

Niby nic wielce odkrywczego, ale taka jest cała Guava, która proste rzeczy zmienia w jeszcze prostsze lub też "standaryzuje" pewne operacje.

Jeśli komuś takie (funkcyjne) podejście nie bardzo pasuje, to radziłbym się zacząć przyzwyczajać. W kolejnej wersji Javy prawdopodobnie wejdą wyrażenia lambda, więc funkcyjność wkroczy do "czystej" Javy pełną parą.

czwartek, 11 października 2012

Infinitest i applet allert

Jeśli masz problem z infinitestem w postaci alertu z komunikatem:
"The applet is attempting to access the "exists" state attributes of ..."
jak poniżej:


Wystarczy do parametrów uruchomieniowych eclipsa dodać: "-Djava.awt.headless=true".
Pomysł zaczerpnięty ze strony: http://www.eclipse.org/forums/index.php/t/166452/

piątek, 29 czerwca 2012

JavaEE materiały

Mam chciwy plan ogarnąć (cały) internet z materiałami dla programistów, głównie JavyEE, ale wszystkie wspólne mianowniki w programowaniu, będą równie mile widziane. Po co to robić skoro jest google? Ano chociażby po to, że googla trzeba wiedzieć o co spytać, a ja czasami mam po prostu ochotę przeczytać/obejrzeć coś nowego... Po drugie często coś ciekawego znajdę na vimeo, a potem totalnie nie mam pojęcia jak to odnaleźć.

Proponuje następujący układ, za każdy link dzięki, któremu dowiedziałeś/aś się czegoś nowego/przydatnego, proszę o jakiś nowy link, żeby powstał samo napędzający się zbiór materiałów. Myślę, że nie muszę dodawać, że zależy mi na linkach, które jednak coś za sobą niosą. Docelowo chciałbym, aby powstał przesiany kanon materiałów dla programisty, do którego można w każdej chwili wrócić, czy też zweryfikować, że już się dane źródło przetrawiło.

Blogi: 

Video:

Craftsmanship:
DDD:
TDD:

Agile:

Spring:

Java:

Inne:

Książki:

niedziela, 24 czerwca 2012

Mapowanie opakowywacza kolekcji

Opakowywacze kolekcji, które zostały opisane przez Koziołka, uważam za bardzo dobre i przydatne podejście/wzorzec. Jednak w pewnym momencie natrafiłem na problem podczas mapowania takich tworów. Mamy w systemie encje BankAccount, wielu różnych aktorów (Client, Manager, itd) może mieć zbiór BankAccount. Warto opakować taki zbiór komponentem BankAccounts, który miałby wspólne metody dla wszystkich aktorów, takie jak np. znajdź konto z zadanego banku, itd.

Wyglądałyby to mniej więcej tak:
@Embeddable
public class BankAccounts {

    @OneToMany(cascade = { CascadeType.ALL }, orphanRemoval = true)
    private Set<BankAccount> bankAccounts = new HashSet<>();
    //...
}
I w jednym z aktorów:
@Entity
public class Client{
    //...

    @Embedded
    @AssociationOverride(name = "bankAccounts", 
        joinColumns = @JoinColumn(name = "client_id"))
    private BankAccounts bankAccounts = new BankAccounts();
    //...
}
I tu powstaje problem, w postaci wyjątku:
javax.persistence.PersistenceException: [PersistenceUnit: testPU] Unable to configure EntityManagerFactory
    at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:378)
    at org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:56)
    at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:63)
    at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:47)
    at pl.cqbroker.utils.DaoTest.setUp(DaoTest.java:39)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:30)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
    at ...
Caused by: org.hibernate.AnnotationException: Illegal attempt to define a @JoinColumn with a mappedBy association: bankAccounts.bankAccounts
    at org.hibernate.cfg.Ejb3JoinColumn.buildJoinColumn(Ejb3JoinColumn.java:253)
    at org.hibernate.cfg.Ejb3JoinColumn.buildJoinColumnsWithDefaultColumnSuffix(Ejb3JoinColumn.java:227)
    at org.hibernate.cfg.Ejb3JoinColumn.buildJoinColumns(Ejb3JoinColumn.java:194)
    at org.hibernate.cfg.ColumnsBuilder.extractMetadata(ColumnsBuilder.java:124)
    at org.hibernate.cfg.AnnotationBinder.processElementAnnotations(AnnotationBinder.java:1495)
    at org.hibernate.cfg.AnnotationBinder.fillComponent(AnnotationBinder.java:2433)
    at org.hibernate.cfg.AnnotationBinder.fillComponent(AnnotationBinder.java:2336)
    at org.hibernate.cfg.AnnotationBinder.bindComponent(AnnotationBinder.java:2285)
    at org.hibernate.cfg.AnnotationBinder.processElementAnnotations(AnnotationBinder.java:2021)
    at org.hibernate.cfg.AnnotationBinder.processIdPropertiesIfNotAlready(AnnotationBinder.java:796)
    at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:707)
    at org.hibernate.cfg.Configuration$MetadataSourceQueue.processAnnotatedClassesQueue(Configuration.java:4035)
    at org.hibernate.cfg.Configuration$MetadataSourceQueue.processMetadata(Configuration.java:3989)
    at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1398)
    at org.hibernate.cfg.Configuration.buildMappings(Configuration.java:1375)
    at org.hibernate.ejb.Ejb3Configuration.buildMappings(Ejb3Configuration.java:1519)
    at org.hibernate.ejb.EventListenerConfigurator.configure(EventListenerConfigurator.java:193)
    at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:1100)
    at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:282)
    at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:366)
    ... 28 more
Rozwiązanie jest w generalnie proste, ale trochę czasu mi zajęło zanim zrozumiałem co jest nie tak, więc gdyby ktoś miał podobny problem, wystarczy dodać adnotację @JoinColumn(). Może być wypełniona jakimiś parametrami, bądź nie - nie ma to znaczenia bo i tak zostanie nadpisana przez Clienta lub inną encję.
@Embeddable
public class BankAccounts {

    @OneToMany(cascade = { CascadeType.ALL }, orphanRemoval = true)
    @JoinColumn
    private Set<BankAccount> bankAccounts = new HashSet<>();
    //...
}

wtorek, 19 czerwca 2012

Value Object a prawidłowa implementacja


Z racji wszechobecnej mody na DDD, obiekty typu Value Object (VO) powstają jak grzyby po deszczu. Zaimplementowanie takiego obiektu wydaje się być trywialne. Jednak bazując na własnych doświadczeniach widziałem/zrobiłem wiele różnej jakości VO i dopiero teraz chyba mam pełny pogląd w tej sprawie. Przykładem niech będzie VO Email, służący do opakowania stringowej reprezentacji adresu email. Implementacja będzie bazowała na Hibernate, ale zaprezentowane rozwiazania są wspólne do większości frameworków od persystencji.
@ValueObject
@Access(AccessType.FIELD)
public class Email implements Serializable{
 private static final long serialVersionUID = -6632187560263057672L;
 @Basic
 private String value;


 /**
  * na potrzeby hibenrate prywatny konstruktor
  */
 @SuppressWarnings("unused")
 private Email() {}

 /**
  * @param email - poprawy adres email
  * @throws IllegalArgumentException jeśli adres email jest niepoprawny
  */
 public Email(String email) {
  if (!EmailValidator.getInstance().isValid(email)) {
   throw new IllegalArgumentException("podałeś nieprawidłowy format adresu email");
  }
  this.value = email;
 }

 @Override
 public boolean equals(Object obj) {
  if (obj == this)
   return true;
  if (!(obj instanceof Email))
   return true;
  Email email = (Email) obj;
  return new EqualsBuilder().append(value, email.value).isEquals();
 }

 @Override
 public int hashCode() {
  return new HashCodeBuilder().append(value).toHashCode();
 }

 @Override
 public String toString() {
  return value;
 }

 public String getValue() {
  return value;
 }
}
Na pierwszy rzut oka nic wielce okrywczego w tym kodzie nie ma. Mimo wszystko warto opisać kilka kluczowych aspektów. Po pierwsze warto zrobić sobie adnotację „marker” do oznaczania wszystkich VO. Łatwiej je wtedy chociażby wyszukiwać w projekcie.
/**
 * stereotyp do oznacznia klass o charakterze ValueObject, 
 *  - ich identyczność bazuje na nie na Id jak w przypadku encji, tylko na ich stanie
 *  - najczęśniej powinny być niemodyfikowalne
 *  - enkapsulują pola o podobnej semantyce,
 * @author aludwiko
 */
public @interface ValueObject {}
Kolejna adnotacja @Access służy do nadpisania po czym mapowane są pola. Niezależnie od tego czy klasa bazowa np. User, mapowana jest z wykorzystaniem getterów czy bezpośrednio pól, Email zawsze będzie mapowany bezpośrednio do pola. Dzięki temu nie musimy tworzyć settera i gettera, co poprawia nam enkapsulację. W moim przypadku getter został, ale jest on po prostu przydatny.

Z racji tego, że encje przeważnie implementują interfejs Serializable, od razu dodajemy go do VO, na pewno nic złego z tego nie wyniknie.

Konstruktor domyślny (wymagany przez Hibernata) został zaimplementowany jako prywatny, aby nikogo nie kusiło tworzenie „pustych” Emaili. Publiczny konstruktor gwarantuje nam, że nigdy utworzymy „błędnego” obiektu Email, co jest jedną z cech dobrego VO, tzn jego stan nie powinien być nieprawidłowy.

Taki konstruktor i wspomniany wcześniej brak settera powodują że Email spełnia kolejną ważna cechę VO, jest niemodyfikowalny. Jest to bardzo ważna cecha w kontekście VO. Equals i hashCode dopełniają minimalną implementację.

Ok, mamy VO, pytanie co dalej z nim zrobić. Jeśli chodzi o warstwę widoku, to należy stworzyć odpowiedni Converter w JSF, PropertyEditor w starym Springu, lub jakikolwiek inny mechanizm, który z wartości inputa utworzy nam obiekt Email. Z racji tego że nie mamy settera nie bindujemy na widoku wartości bezpośrednio do value
<h:inputtext id="email" value="#{user.email.value}">
tylko do samego email'a
<h:inputtext id="email" value="#{user.email}">
Przykładowa implementacji w springu:
public class EmailPropertyEditor extends PropertyEditorSupport {

 @Override
 public void setAsText(String text) throws IllegalArgumentException {
  if (StringUtils.isNotBlank(text)) {
   Email email = new Email(text);
   setValue(email);
  }
  else {
   setValue(null);
  }
 }

 @Override
 public String getAsText() {
  Email email = (Email) getValue();
  if (email != null) {
   return email.getValue();
  }
  else {
   return "";
  }
 }
}
W tym momencie mamy już obiekt z wypełnionym Email'em, teraz warto powiedzieć Hibernatowi jak ma zapisywać taki obiekt z Email'em do bazy. Są dwa główne podejścia, albo idziemy w kierunku @Embeddable i @Embedded albo Hibernate UserType.

Pierwsze podejście jest już proste i prawdopodobnie każdy miał z nim styczność. Chociaż np. dyskusja czy każdy obiekt @Embeddable jest automatycznie VO, nadal trwa i właściwie jestem po obu stronach w tym konflikcie.

Drugie podejście, choć trochę bardziej skomplikowane, daje ciekawy efekt w postaci możliwości tworzenia bardziej obiektowych zapytań, takich jak np:
Email email = new Email("aaaa@op.pl");
Criteria criteria = session.createCriteria(User.class).add(eq("email", email));
lub jako HQL
"from User WHERE email = :someEmail"
W tym przypadku musimy napisać własną implementację typu pola w klasie, tak aby hibernate wiedział jak zapisywać Emaila. W większości wypadków wystarczy zaimplementowanie interfejsu UserType. Jednak w tym przypadku chciałbym mieć możliwość tworzenia zapytań z wykorzystaniem LIKE:
Criteria criteria = session.createCriteria(User.class).add(ilike("email.value", "op.pl"));
Dostęp do pól VO w zapytaniach jest możliwy, ale musimy zaimplementować CompositeUserType, przykład implementacji:
/**
 * implementacja UserType'a dla VO {@link Email}
 * @author aludwiko
 */
public class EmailCompositeType implements CompositeUserType {

 /**
  * ORDER IS IMPORTANT! it must match the order the columns are defined in the property mapping
  */
 @Override
 public String[] getPropertyNames() {
  return new String[] { "value" };
 }
 @Override
 public Type[] getPropertyTypes() {
  return new Type[] { Hibernate.STRING };
 }
 @Override
 public Object getPropertyValue(Object component, int property) throws HibernateException {
  if (component == null) {
   return null;
  }
  Email email = (Email) component;
  switch (property) {
   case 0: {
    return email.getValue();
   }
   default: {
    throw new HibernateException("Invalid property index [" + property + "] for Email");
   }
  }
 }
 @Override
 public void setPropertyValue(Object component, int property, Object value) throws HibernateException {
  throw new UnsupportedOperationException();
 }
 @Override
 public Class returnedClass() {
  return Email.class;
 }
 @Override
 public boolean equals(Object x, Object y) throws HibernateException {
  if (x == y) {
   return true;
  }
  if (x == null || y == null) {
   return false;
  }
  Email emailX = (Email) x;
  Email emailY = (Email) y;
  return emailX.equals(emailY);
 }
 @Override
 public int hashCode(Object x) throws HibernateException {
  return x.hashCode();
 }
 @Override
 public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException {
  String name = names[0];
  Object emailString = Hibernate.STRING.nullSafeGet(rs, name);
  if (emailString == null) {
   return null;
  }
  return new Email((String) emailString);
 }
 @Override
 public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException {
  if (value == null) {
   Hibernate.STRING.nullSafeSet(st, null, index);
  }
  else {
   Hibernate.STRING.nullSafeSet(st, ((Email) value).toString(), index);
  }
 }
 @Override
 public Object deepCopy(Object value) throws HibernateException {
  return value;
 }
 @Override
 public boolean isMutable() {
  return true;
 }
 @Override
 public Serializable disassemble(Object value, SessionImplementor session) throws HibernateException {
  return (Serializable) value;
 }
 @Override
 public Object assemble(Serializable cached, SessionImplementor session, Object owner) throws HibernateException {
  return cached;
 }
 @Override
 public Object replace(Object original, Object target, SessionImplementor session, Object owner) throws HibernateException {
  return original;
 }
}
I samo wykorzystanie:
@Entity
public class User {
        //...
 @Column
 @Type(type = "package.EmailCompositeType")
 public Email getEmail() {
  return email;
 }
 public void setEmail(Email email) {
  this.email = email;
 }
        //...
}
Mam nadzieję, że temat został opisany kompleksowo i da się to przeczytać w skończonym czasie. Gdyby ktoś miał coś ciekawego do dodania, to zachęcam do komentowania.