3

I just found a piece of code which looks as follow:

@Transactional
public List<Foo> giveMeFooMatchingSomeConstraints(Long param1, String param2) {
 List<Bar> bars = barDao.findBars(param1, param2);
 List<Foo> foos = fooDao.findFoosByBarIds(barService.getIds(bars));
 return foos; 
}

I'm just curious what's the purpose of using @Transactional in case when a method only fetches data and doesn't modify them. Is such an approach like above ok?

pidabrow
  • 966
  • 1
  • 21
  • 47
  • Possible duplicate of [Why do I need Transaction in Hibernate for read only operation?](https://stackoverflow.com/questions/13539213/why-do-i-need-transaction-in-hibernate-for-read-only-operation) – soorapadman Sep 13 '17 at 09:07
  • for fetching data, no need to annotate with Transactional, whenever we have to persist multiple table that time only can use Transactional. – Balasubramanian Sep 13 '17 at 09:09

1 Answers1

3

Yes it makes sense when you aren't using Open*InView and working with lazy relations within your method.

Say we have these entities bounded with one-to-many relation:

@Entity(name = "user")
static class User extends AbstractPersistable<String> {
    private String username;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) //by default fetching is lazy
    @JoinColumn(name = "user_id", foreignKey = @ForeignKey(name = "fk_user_roles"))
    private Set<Role> roles = new HashSet<>();

    public Set<Role> getRoles() {
        return this.roles;
    }
}

@Entity(name = "role")
static class Role extends AbstractPersistable<String> {
    private String name;

    private Role() {
    }

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

    public String getName() {
        return this.name;
    }
}

a repository to access them:

interface UserDao extends JpaRepository<User, String> {}

and a service for some data manipulation - retrieve all users, iterate trough them and grab all the roles:

@Service
static class UserService {
    private final UserDao userDao;

    UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    @Transactional // <---
    public Set<String> findUserRoles() {
        final List<User> users = this.userDao.findAll();
        return users.stream()
                .flatMap(user -> user.roles.stream())
                .map(Role::getName)
                .collect(Collectors.toSet());
    }
}

Remove @Transactional to see LazyInitializationException with no Session message, since nobody have opened it.

To avoid confusions in such cases it is better to explicitly mark transaction as read-only:

@Transactional(readOnly = true)

So reader will know that this method have no intention to modify any data, but transaction (session) is still required.

Full example.

Bohdan Levchenko
  • 3,411
  • 2
  • 24
  • 28
  • Great explanation, the reason of using @Transactional in this case is clear to me now. I like the example the example you provided, thanks! – pidabrow Sep 14 '17 at 09:32