0

I'm new to Spring Data/Hibernate, and I'm trying to get my head around how you're supposed to handle concurrent users accessing data.

Suppose I've got a very simple domain model, consisting of houses and people who live in those houses:

House:

@Entity
public class House {

    @Id
    @GeneratedValue
    private Long id;

    @OneToMany(cascade = CascadeType.ALL)
    private Set<Person> persons;

    public void addPerson(Person p) {
        persons.add(p);        

    public Set<Person> getPersons() {
        return persons;
    }

}

Person:

@Entity
public class Person {

    @Id
    @GeneratedValue
    private Long id;

}

Currently I'm loading a House from a HouseRepository, and using this object to get/add persons.

That all works fine for a single user, but how are you supposed to support concurrent users? That is, say I've got a web application that has 2 concurrent users, who both want to view and add/edit persons from the same house.

What's the standard/best practice?

edit: to clarify what I'd like to do:

  1. User 1 gets houseA from repository
  2. User 2 gets houseA from repository
  3. User 1 adds personA to houseA
  4. User 2 gets persons from houseA, which contains personA

edit: Problem with @Transactional -

@SpringBootApplication
public class ExampleApplication implements CommandLineRunner {

    @Autowired
    private HouseRepository houseRepository;

    public static void main(String[] args) {
        SpringApplication.run(ExampleApplication.class, args);
    }

    @Override
    public void run(String... strings) throws Exception {
        House house = new House();
        Person person = new Person();
        person.setName("Bob");
        house.addPerson(person);
        houseRepository.save(house);

        printPeople(house.getId());
    }

    @Transactional
    public void printPeople(Long id) {
        House house = houseRepository.findOne(id);
        for (Person person : house.getPersons()) {
            System.out.println(person.getName());
        }
    }

}

Throws org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.example.House.persons, could not initialize proxy - no Session at the foreach loop in printPeople

bobsyouruncle
  • 107
  • 1
  • 13

1 Answers1

0

JPA is typically expected to be used within transactions. Use Spring's @Transactional AOP to mark your transaction boundaries, and it will handle concurrent access. Apply @Transactional to the highest-level method where it is relevant, which is typically the MVC controller method for a Web application.

chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152
  • Trouble is, @Transactional doesn't seem to be working for me (throwing org.hibernate.LazyInitializationException, see updated question). Changing the fetch type works, but I'd rather not do that. – bobsyouruncle Sep 20 '16 at 06:55
  • @bobsyouruncle This is a bog-standard issue with *self-calls*. Proxy AOP (the Spring default) can only be applied when the receiver of the method call is injected by the container and doesn't work when you're making a call on `this`. Split the `printPeople` out into an autowired object and it'll work. – chrylis -cautiouslyoptimistic- Sep 20 '16 at 06:57
  • @bobsyouruncle Last thing, then--avoid field injection whenever possible; it leads to all sorts of problems. Use constructor injection instead. – chrylis -cautiouslyoptimistic- Sep 20 '16 at 07:10