Skip to content

Tutorial 6: Creating data generation plan

Kumar Rohit edited this page Jan 27, 2018 · 5 revisions

Configuration completed

Till tutorial 5, we have configured JPAContextFactory with custom dependencies, generators, triggers and preconditions. Now from JPAContextFactory we will create JPAContext.

final JPAContextFactory jpaContextFactory 
    = JPAContextFactory.newInstance(Database.MY_SQL, entityManager);
jpaContextFactory.with(generator);
jpaContextFactory.with(dependencies);
jpaContextFactory.withPreconditions(...);
jpaContextFactory.withTriggers(...);

final JPAContext jpaContext = jpaContextFactory.create();

Create Simple Plan

To explain with a simple example, let us say that we want to create following table data

  1. Account

  2. Person

  3. Employee (extends Person)

    final CreationPlan creationPlan = jpaContext.create(Plan.create() .add(Entity.of(Account.class)) .add(Entity.of(Employee.class)));

This would create a creation plan with 3 entities (Account, Person, Employee). Since Employee has a foreign key relation with Person, you don't need to explicitly specify Person in the Plan

Print creation plan

Now you have a creationPlan, so lets have a look at what we are going to create...

creationPlan.print(new Printer() {
            @Override
            public void print(String s) {
                System.out.println(s);
            }
        });

You will get a tree printed on the console

*******
└── *ROOT*
    └── com.github.kuros.sample.entity.Account|0 
        └── com.github.kuros.sample.entity.People|0 
            └── com.github.kuros.sample.entity.Employee|0 

Attributes with null values

In the plan, you need to specify the attributes for which you want null values to be set. By default, if not specified some random value will be assigned to all the attributes of entity.

final CreationPlan creationPlan = jpaContext.create(Plan.create()
        .add(Entity.of(Person.class))
        .withNullValues(Person_.lastName));

This would set null values for person's lastName.

Preconditions at plan level

We have seen preconditions at system level, in the similar way, we have preconditions to enforce order at plan level.

Let us take the our previous example

final CreationPlan creationPlan = jpaContext.create(Plan.create()
                .add(Entity.of(Account.class))
                .add(Entity.of(Employee.class)));

/* creationPlan would print
    *******
        └── *ROOT*
            └── com.github.kuros.sample.entity.Account|0 
                └── com.github.kuros.sample.entity.Person|0 
                    └── com.github.kuros.sample.entity.Employee|0
*/

let us just reverse the plan, and the creation order differs, so it would generate

final CreationPlan creationPlan = jpaContext.create(Plan.create()
                .add(Entity.of(Employee.class))
                .add(Entity.of(Account.class)));

/* creationPlan would print
    *******
        └── *ROOT*
            └── com.github.kuros.sample.entity.Person|0 
                └── com.github.kuros.sample.entity.Employee|0 
                    └── com.github.kuros.sample.entity.Account|0
*/

Now we want Account to be created before creating Person, so to achieve it, we will add preconditions

final CreationPlan creationPlan = jpaContext.create(Plan.create()
                    .add(Entity.of(Employee.class))
                    .add(Entity.of(Account.class))
                    .add(Entity.of(Person.class).createBefore(Account.class)));
    
/* creationPlan would print
        *******
            └── *ROOT*
                └── com.github.kuros.sample.entity.Account|0 
                    └── com.github.kuros.sample.entity.Person|0 
                        └── com.github.kuros.sample.entity.Employee|0
*/

Providing Custom Values

We want to create Entities with some specific values at the time of creating plan.

final CreationPlan creationPlan = jpaContext.create(Plan.create()
                .add(Entity.of(Account.class).with(Account_.accountType, "Saving")));

Note: If id is provided, random-JPA would execute a findById query and load the data. So other values provided would not be persisted

Create multiple copies of entity

Let us say that we want to create two different Employees referring to single Person.

Plan plan = Plan.create()
            .add(Entity.of(Employee.class, 2).with(Employee_.Country, INDIA))
            .add(Entity.of(Person.class).with(Person_.gender, "Male");
CreationPlan creationPlan = jpaContext.create(plan);

/*

└── *ROOT*
    └── com.github.kuros.sample.entity.Person|0
        ├── com.github.kuros.sample.entity.Employee|0
        └── com.github.kuros.sample.entity.Employee|1
*/

The numeric values represents the index number, using which entities can be accessed. We will see this details in the next section.

Using createAfter & createBefore to generate correct order

I would like to have a pizza with two cheese & 2 olives

jpaContext.create(Entity.of(Pizza.class),
            Entity.of(Cheese.class , 2),
            Entity.of(Olives.class, 2));

/*
└── *ROOT*
└── (h=1) com.github.kuros.sample.entity.Pizza|0
    ├── (h=0) com.github.kuros.sample.entity.Olives|0
    │   ├── (h=0) com.github.kuros.sample.entity.Cheese|0
    │   └── (h=0) com.github.kuros.sample.entity.Cheese|1
    └── (h=0) com.github.kuros.sample.entity.Olives|1
        ├── (h=0) com.github.kuros.sample.entity.Cheese|2
        └── (h=0) com.github.kuros.sample.entity.Cheese|3
*/

Something went wrong... (expecting 2 cheese but got 4 cheese??)

Here is how Random-JPA works: It generates graph of entities based on dependencies

     Pizza
    /     \
Olives   Cheese

Then merges the graph to generate order, since Olives & Cheese both are at same height, it could generate:

Pizza          Pizza
  |              |
Olives   OR    Cheese
  |              |
Cheese         Olives

Now when you said a pizza with 2 olives & 2 cheese, it would generate, something like this:

               Pizza                                           Pizza
             /       \                                       /       \
            /         \                                     /         \
           /           \                OR                 /           \
        Olives        Olives                           Cheese          Cheese
        /  \          /   \                             /  \           /   \
       /    \        /     \                           /    \         /     \
   Cheese  Cheese   Cheese Cheese                   Olives  Olives  Olives   Olives

So When you want a pizza with 2 olives & 2 cheese, you can simply say:

jpaContext.create(Entity.of(Pizza.class),
            Entity.of(Cheese.class , 2),
            Entity.of(Olives.class, 1))
/*
But it can generate

└── *ROOT*
└── (h=1) com.github.kuros.sample.entity.Pizza|0
    └── (h=0) com.github.kuros.sample.entity.Olives|0
        ├── (h=0) com.github.kuros.sample.entity.Cheese|0
        └── (h=0) com.github.kuros.sample.entity.Cheese|1

OR

└── *ROOT*
└── (h=1) com.github.kuros.sample.entity.Pizza|0
    ├── (h=0) com.github.kuros.sample.entity.Cheese|0
    │   └── (h=0) com.github.kuros.sample.entity.Olives|0
    └── (h=0) com.github.kuros.sample.entity.Cheese|1
        └── (h=0) com.github.kuros.sample.entity.Olives|1
*/

To Fix this we should use createAfter/createBefore to make sure Cheese is always created before Olives, this would force a Cheese to be virtual parent of olives, now this would ensure you have correct order & tree.

jpaContext.create(Entity.of(Pizza.class),
        Entity.of(Olives.class, 1),
        Entity.of(Cheese.class , 2).createBefore(Olives.class)
)

/*
└── *ROOT*
└── (h=2) com.github.kuros.random.jpa.testUtil.entity.Pizza|0
    ├── (h=1) com.github.kuros.random.jpa.testUtil.entity.Cheese|0
    │   └── (h=0) com.github.kuros.random.jpa.testUtil.entity.Olives|0
    └── (h=1) com.github.kuros.random.jpa.testUtil.entity.Cheese|1
        └── (h=0) com.github.kuros.random.jpa.testUtil.entity.Olives|1
 */
Clone this wiki locally