Skip to content
Patrick Huang edited this page Oct 1, 2013 · 5 revisions

Given Entity mapping classes

irrelevant getter/setter/constructor omitted

@MappedSuperclass
public class Identifier implements Serializable {
    protected Long id;

    @Id
    @GeneratedValue
    public Long getId() {
        return id;
    }
}

@Entity
@Access(AccessType.FIELD)
public class Person extends Identifier {
    @Email
    private String email;

    @Size(min = 1, max = 20)
    private String name;
}


@Entity
@Access(AccessType.FIELD)
public class Category extends Identifier {
    @Size(min = 5)
    private String name;

    public Category(String name) {
        this.name = name;
    }

    @OneToOne
    @JoinColumn(name = "owner_id")
    private Person categoryOwner;

    @OneToMany(targetEntity = LineItem.class)
    @IndexColumn(name = "number", nullable = false)
    @JoinColumn(name = "category_id", nullable = false)
    private List<LineItem> lineItems = Lists.newArrayList();
}

@Entity
public class LineItem extends Identifier {
    private String content;
    private Person owner;
    private Category category;
    private Integer number;

    @Size(min = 20)
    public String getContent() {
        return content;
    }

    @OneToOne(targetEntity = Person.class, optional = false)
    public Person getOwner() {
        return owner;
    }

    @ManyToOne(targetEntity = Category.class)
    @JoinColumn(name = "category_id", insertable = false, updatable = false, 
        nullable = false)
    public Category getCategory() {
        return category;
    }

    @Column(insertable = false, updatable = false, nullable = false)
    public Integer getNumber() {
        return number;
    }
}

Basic Usage:

(Assuming you have obtained an EntityManager instance)

EntityMaker maker = EntityMakerBuilder.builder().build();
LineItem itemOne = maker.makeAndPersist(entityManager, LineItem.class);
LineItem itemTwo = maker.makeAndPersist(entityManager, LineItem.class);

// you should have itemOne and itemTwo in database both referencing the same 
// Category record.
assertThat(itemOne.getId(), Matchers.notNullValue());
assertThat(itemTwo.getId(), Matchers.notNullValue());

// for optional=false OneToOne mapping, it will create one for each
assertThat(itemOne.getOwner(), Matchers.notNullValue());
assertThat(itemTwo.getOwner(), Matchers.notNullValue());
assertThat(itemOne.getOwner(), 
    Matchers.not(Matchers.equalTo(itemTwo.getOwner())));

Category category = itemOne.getCategory();
assertThat(category, Matchers.notNullValue());
assertThat(category.getId(), Matchers.notNullValue());
assertThat(itemOne.getCategory(), 
    Matchers.sameInstance(itemTwo.getCategory()));
assertThat(category.getLineItems(), Matchers.contains(itemOne, itemTwo));

// optional OneToOne mapping is ignored by default
assertThat(category.getCategoryOwner(), Matchers.nullValue());

Need to tweak a bit:

EntityMaker customMaker = EntityMakerBuilder.builder()
        .includeOptionalOneToOne()
        .addFieldOrPropertyMaker(LineItem.class, "content", 
            FixedValueMaker.fix("This is the content I want"))
        .addConstructorParameterMaker(Category.class, 0, 
            FixedValueMaker.fix("Awesomeness"))
        .build();
LineItem itemThree = 
    customMaker.makeAndPersist(entityManager, LineItem.class);

assertThat(itemThree.getContent(), 
    Matchers.equalTo("This is the content I want"));
assertThat(itemThree.getCategory().getName(), 
    Matchers.equalTo("Awesomeness"));
// optional OneToOne entity is also created
assertThat(itemThree.getCategory().getCategoryOwner(), 
    Matchers.notNullValue());

What if I want to have ManyToMany relationship

@Entity
class Account {
   @ManyToMany(targetEntity = Role.class)
   @JoinTable(name = "AccountMembership", 
        joinColumns = @JoinColumn(name = "accountId"), 
        inverseJoinColumns = @JoinColumn(name = "memberOf"))
   public Set<Role> getRoles() {
       return roles;
   }
}

@Entity
class Role {
}

// assuming role is already in database or is made and persisted
Role role = entityManager.find(Role.class, 1L);
Account account = maker.makeAndPersist(entityManager, HAccount.class, 
    new WireManyToManyCallback(HAccount.class, role));

assertThat(account.getRoles(), Matchers.containsInAnyOrder(role));

There are more callbacks available and you can define your own.

Some fields should be skipped

For example some primitive type fields, in some cases we may want to provide a value different from its default, but in other cases the value may be derived from constructor parameter or populated in @PrePersist method.

@Entity
public class Data {
    private boolean active;
    private long modifiedById;
    
    @PrePersist
    public void prePersist() {
        modifiedById = getAuthenticatedUser().getId();
    }
}
EntityMakerBuilder.builder()
    .addFieldOrPropertyMaker(Data.class, "active", 
        FixedValueMaker.ALWAYS_TRUE_MAKER)
    .addFieldOrPropertyMaker(Data.class, "modifiedById", 
        SkipFieldValueMaker.MAKER)
    .build()
    .makeAndPersist(entityManager, Data.class);

Clone this wiki locally