Chariot Training Classes

Training Courses

I run Chariot's training and mentoring services. We provide training in AngularJS, HTML5, Spring, Hibernate, Maven, Scala, and more.

Chariot Education Services

Technology

Chariot Emerging Tech

Learn about upcoming technologies and trends from my colleagues at Chariot Solutions.

Resources

Chariot Conferences

Podcasts

Entries in jpa (3)

Monday
Feb272012

Quizzo in Roo case of the Mondays. Or, how you can be a JPA doofus 

This morning I took a look at my schema, and started to cry. Well, not really, but in that way people say LOL, I say COL. Here was my original schema (click to view the picture in full resolution):

Wait, this guy knows JPA? Sure he does!

Now, I know some of you have my back here. You say, "look, JPA can be confusing, you have to work with it." The thing is, I wrote a bloody chapter on JPA relationships, I teach JPA, my head just wasn't in the game, OK?

Just kidding. You know by now I'm razzing you all...

Anyhoo, I didn't bother taking any time to pay attention to my relationships (sounds like somebody's going to have cold soup for dinner). Turns out, I was abusing my @JoinColumn and mappedBy settings.

How to do a bi-directional one-to-many relationship

Do it up, Rimp. Here's how. First off, you need to define your one-sided entity and allow it to contain your Set (don't get me started about alternative options for collection types, that'll take us all day):

@RooJavaBean
@RooToString
@RooJpaEntity
public class Quiz {

    @NotNull
    @Size(max = 200)
    private String title;

    @NotNull
    @Size(max = 500)
    private String description;

    @OneToMany(mappedBy = "quiz",
        cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    private Set<Question> questions = new HashSet<Question>();

}

Now, I know we're all adults in here, so that's why I put the harder stuff in the cabinet. You see, there are several key items of critical information here:

  • The collection is of type Set, and I'm creating a prototype (for new entities before I save them) of the real type of HashSet. Don't assume they will be HashSet instances when you fetch them again, they likely could be proxies that hydrate when you touch them (wear coding gloves).
  • I've decided to use the cascade option to tell JPA to cascade changes to the questions if I persist or update (merge) the quiz.
  • The mappedBy element is all-important. This tells us that the property pointing back to our Quiz instance in the class named Question will be called quiz. This establishes what side owns the relationship. As one of my students once said, "if you see mappedBy, it means this class does NOT own the relationship." Good rule to live by.

Whew! I'm feeling weak and dizzy. A lot of information hiding in there. Let's look at the Question class for the other side of this thing:

@RooJavaBean
@RooToString
@RooJpaEntity
public class Question {
  @ManyToOne
  @JoinColumn(name = "quiz_id")
  private Quiz quiz;
}

Wow, that's it??? Huh. Ok, well erm let's give this a shake. This class is in charge of the relationship, meaning that if it is touched, it will handle the insert or update of the foreign key. So:

  • The quiz variable name matches the mappedBy element in the Quiz table. Think of this as the role of the relationship. You may use the same table twice with different roles (think sourceAccount, destinationAccount and you get the idea. They would each have different @OneToMany relationships with different mappedBy elements, thereby different roles and variables.
  • The foreign key column that handles the database relationship is called quiz_id, and so we'll get a table called Question with a foreign key in it called quiz_id

Easy, peasy! Now, if you look at the relationship between Question and Quiz in the image above, it looks like there are three tables. But in the one below, the relationships are all simplified. That's because I paid attention to @JoinColumn and mappedBy. Click the image to see the full copy:

Mmmm... Low fat data models. Delicious and 1/2 the tables of your normal schema.

What else did I change recently?

I also got the state machine 99% working, with just a hitch on the test that determines whether the quiz is over or not. I'll work on that one tonight, now that I can actually understand my data model without getting sick to my stomach.

I moved the QuizRunState tracking state into memory, as it was failing me. Oh, one more thing - don't assume that IntelliJ catches Java Language assert statements. I was guarding my code with assertions to catch any invalid states in the state machine. Turns out, Maven turns on assertion checking when testing but IntelliJ does not. I'm not talking about JUnit assertions, but the Java System assertion language statement, such as I use in this fragment of QuizRunStateMachineInMemory:


  @Transactional
  public void startQuiz() {
    assert (runState == QuizRunState.NOT_STARTED);
    runState = QuizRunState.IN_PROGRESS;
  }

Oh, it burns us baggins! To fix this, you edit your JUnit runner and add the -ea -esa flags to your vm settings. And, ladies and gentlemen, that was the reason I was getting errors in my Maven code that weren't showing up in IntelliJ. Just sayin'

See ya tomorrow (or maybe later tonight).

Friday
Feb242012

Quizzo Friday - Goin' all layered on the darn thing

I'm currently working on a somewhat complex, ajax-based web flow. See commit 505fbe7989f62003a4188a4baab5b85cc31077fb for the additional methods and constructs. It's not working yet, so I figured I'd be a good little boy and write tests.

Now, I could be Mr. Smarty-pants and work with the models directly, and I've been doing so. However, here's where that breaks down - when I want to do some more involved application development, and want to try some web-flow tests using the WebFlow mock system, and need to mock the entity layer. That's just not so easy.

The challenge

Ok, take a look at what I'm up against here (click the image to see the fragment of the webflow, courtesy of IntelliJ's great WebFlow editor).
I have a section of the web flow that registers a new user. Two steps are currently involved, one with the team name, the second with a list of members (and this one is not yet working so don't bother reviewing it...)

I'll want to test the method being guarded by the exit, which is defined this way in the web flow:

<transition on="continue" to="ready-to-play">
  <evaluate expression="quizzoFlowManager.saveTeamData(flowRequestContext)"/>
</transition>

The method in ~.web.flow.QuizzoFlowManagerBean looks like this currently:

  @Override
  public Event saveTeamData(RequestContext flowRequestContext) throws FlowException {
    TeamSetupForm teamSetupForm = 
       (TeamSetupForm)flowRequestContext.getViewScope()
       .get("teamSetupForm");

    Team team = new Team();
    team.setName(teamSetupForm.getName());
    team.setMission(teamSetupForm.getMessage());

    Iterator<String> memberNamesIterator = 
      teamSetupForm.getTeamMemberNames().iterator();

    while (memberNamesIterator.hasNext()) {
      String teamMemberName = memberNamesIterator.next();
      TeamMember member = new TeamMember();
      member.setName(teamMemberName);
      team.getTeamMembers().add(member);
    }

    team.setQuizRun(stateMachine.getQuizRun());
    team.persist();
    flowRequestContext.getFlowScope().put("team", team);
    return success();
  }

This is web-flow cruft. It's kind of like controller code, and it's defined in a Spring Bean that lives as a web-layer module. There are several problems with this:

  • It's doing persistence in a web-tier object
  • I don't want to make a web-tier object @Transactional
  • If I'm writing tests, how do I mock the entity layer without a lot of headache?

In thinking of how to express a test against this logic, I'm struck with how complex the test needs to be. It's tough enough to set up the Web Flow layer with the appropriate testing. But to then have to do some magic (see this post on @MockStaticEntityMethods - it hurts us, Baggins!).

I consider a good test to be documentation of the use-case or exercising of the code in question. That's what it should be. I'm sensing coupling here to both the web layer AND the data layer. Not so good, in my feeling. Although it's nice to have model objects and throw around Active Record calls everywhere for prototyping, I think this is where it falls down, in testing without being able to mock a service or repository layer cleanly.

Going all Repository on it

Ok, so how do I set up a repository for each bean? Easy-peasy.

repository jpa --entity ~.model.Answer        --interface ~.db.AnswerRepository
repository jpa --entity ~.model.Choice        --interface ~.db.ChoiceRepository
repository jpa --entity ~.model.Question      --interface ~.db.QuestionRepository
repository jpa --entity ~.model.Quiz          --interface ~.db.QuizRepository
repository jpa --entity ~.model.QuizRun       --interface ~.db.QuizRunRepository
repository jpa --entity ~.model.Team          --interface ~.db.TeamRepository
repository jpa --entity ~.model.TeamMember    --interface ~.db.TeamMemberRepository

Forgive me if my format looks a little odd - I used TextMate (my favorite Mac editor in the whole wide world) to manipulate the text using column pasting.

Now, I have a repository layer. I can do the same thing to add a service layer, but it needs a little more gusto (line breaks are for readability, it doesn't fit on one line in the blog):

service --entity ~.model.Answer --interface ~.service.AnswerService 
         --class ~.service.AnswerServiceBean
service --entity ~.model.Choice        --interface ~.service.ChoiceService
        --class ~.service.ChoiceServiceBean
service --entity ~.model.Question      --interface ~.service.QuestionService
      --class ~.service.QuestionServiceBean
service --entity ~.model.Quiz          --interface ~.service.QuizService
          --class ~.service.QuizServiceBean
service --entity ~.model.QuizRun       --interface ~.service.QuizRunService
       --class ~.service.QuizRunServiceBean
service --entity ~.model.Team          --interface ~.service.TeamService
          --class ~.service.TeamServiceBean
service --entity ~.model.TeamMember    --interface ~.service.TeamMemberService
    --class ~.service.TeamMemberServiceBean

Now, I have a true repository-and-service layer for my system. My controllers and integration tests automatically convert to using the services and repositories once they exist:

@RequestMapping(method = RequestMethod.POST, produces = "text/html")
public String TeamController.create(...) {
  if (bindingResult.hasErrors()) {
    populateEditForm(uiModel, team);
    return "admin/teams/create";
  }
  
  uiModel.asMap().clear();
  teamService.saveTeam(team);
  return "redirect:/admin/teams/" ...
}

It is special, isn't it. Next up, we'll show you how to unit test the logic in our web flow method.

Sunday
Feb192012

Quizzo in Roo, truth be told

Truth be told, I've spent so much time running a training practice, writing a book and dealing with Rails apps that my brain needed a bit of adjustment to long-form, more than one day coding assignments. I tried to start with a simple JPA/hibernate model, but the mental feedback loop, even with Roo at the helm, was too slow for me.

(Remember, I am on a two week deadline right now to write two presentations AND get the quiz working if I have a shot at this. So I am trying to control a flailing feeling. And thinking that I'm only doing one presentation next time.)

I went through three stages of thought before writing this down:

Stage 1 - Let's focus on the model

I figured that the data was key, and that I could model from the database up. I ended up floundering after a few hours of this, realizing that we have two problems here - the model of the quiz structure, and dealing with running instances of the quiz, recording answers, and reporting results.

The results were rather throwaway, so I won't bore you with them.

So, I went to Stage 2

Stage 2 - Go all design-patterny on it

Ok, focus on the objects and the in-memory model. That was a great turn, as it got me thinking about the contracts, building up the objects, etc. I figured I could have a Quiz in-memory model, and that I could build it using a builder. So I spent time writing up builders that looked something like this:

package com.chariot.games.quizzo.model.quiz;

public class QuestionBuilder {
  
  Question question;
  public QuestionBuilder() {
    question = new Question();
  }
  
  public QuestionBuilder title(String title) {
    question.setTitle(title);
    return this;
  }
  
  public QuestionBuilder choice(Choice choice) {
    question.getChoices().add(choice);
    return this;
  }
  
  public Question asQuestion() {
    return question;
  }
}

Then I could use the builder in tests, ala:

  @Test
  public void testBuildQuestionWithBooleanAnswer() 
     throws CloneNotSupportedException {

    Question q = new QuestionBuilder()
                  .title("Studies say people are crazy.")
                  .choice(new ChoiceBuilder()
                          .booleanChoice(true)
                          .text("They are crazy")
                          .asChoice()
                  ).asQuestion();

      assertEquals("Studies say people are crazy.", q.getTitle());
      assertEquals(1, q.getChoices().size());
      assertEquals("They are crazy", q.getChoices().get(0).getAnswerText());

      assertEquals(BooleanChoice.class, q.getChoices().get(0).getClass());

      Question q2 = q.clone();
      assertEquals(q2, q);
      assertNotSame(q2, q);
  }

Stage 3 - Prototype quickly on paper, models again

But in the end, what I really need is a quiz definition, with questions, etc., and I need to persist them at some point. So, it was fun, and helped me understand the relationships, but back to JPA.

My model so far...

Ok, so far I have the following model elements:

@RooJavaBean
@RooToString
@RooJpaActiveRecord
public class Choice {

    @NotNull
    @Size(max = 300)
    private String text;

    @NotNull
    private Boolean correct;

    @NotNull
    @ManyToOne
    private Question question;
}

@RooJavaBean
@RooToString
@RooJpaActiveRecord
public class Question {

    @ManyToOne
    private Quiz quiz;

    @NotNull
    @Size(max = 300)
    private String text;

    @OneToMany(cascade = CascadeType.ALL)
    private Set<Choice> choices = new HashSet<Choice>();
}

@RooJavaBean
@RooToString
@RooJpaActiveRecord
public class Quiz {

    @NotNull
    @Size(max = 200)
    private String title;

    @NotNull
    @Size(max = 500)
    private String description;

}

@RooJavaBean
@RooToString
@RooJpaActiveRecord
public class Team {

    @NotNull
    @Size(max = 80)
    private String name;

    @NotNull
    private String mission;

    @ElementCollection
    private List<TeamMember> teamMembers;
}

@RooJavaBean
@RooToString
@Embeddable
public class TeamMember {

    @NotNull
    @Size(max = 40)
    private String name;
}

Did I mention - Git branches are cheap?

Git is great for things like this - since it keeps versions and branches within your .git database directory, you can branch and merge at will, and then push the repository updates when you are ready.