From Spring Documentation:
The @Repository
annotation is a marker for any class that fulfils the
role or stereotype of a repository (also known as Data Access Object
or DAO). Among the uses of this marker is the automatic translation of
exceptions, as described in Exception Translation.
Spring provides further stereotype annotations: @Component
, @Service
,
and @Controller
. @Component
is a generic stereotype for any
Spring-managed component. @Repository
, @Service
, and @Controller
are
specializations of @Component
for more specific use cases (in the
persistence, service, and presentation layers, respectively).
Therefore, you can annotate your component classes with @Component
,
but, by annotating them with @Repository
, @Service
, or @Controller
instead, your classes are more properly suited for processing by tools
or associating with aspects.
For example, these stereotype annotations
make ideal targets for pointcuts. @Repository
, @Service
, and
@Controller
can also carry additional semantics in future releases of
the Spring Framework. Thus, if you are choosing between using
@Component
or @Service
for your service layer, @Service
is clearly the
better choice. Similarly, as stated earlier, @Repository
is already
supported as a marker for automatic exception translation in your
persistence layer.
Annotation |
Meaning |
@Component |
generic stereotype for any Spring-managed component |
@Repository |
stereotype for persistence layer |
@Service |
stereotype for service layer |
@Controller |
stereotype for presentation layer (spring-mvc) |
Good question, although not a trivial one to answer.
Defines how transactions relate to each other. Common options:
REQUIRED
: Code will always run in a transaction. Creates a new transaction or reuses one if available.
REQUIRES_NEW
: Code will always run in a new transaction. Suspends the current transaction if one exists.
The default value for @Transactional
is REQUIRED
, and this is often what you want.
Defines the data contract between transactions.
ISOLATION_READ_UNCOMMITTED
: Allows dirty reads.
ISOLATION_READ_COMMITTED
: Does not allow dirty reads.
ISOLATION_REPEATABLE_READ
: If a row is read twice in the same transaction, the result will always be the same.
ISOLATION_SERIALIZABLE
: Performs all transactions in a sequence.
The different levels have different performance characteristics in a multi-threaded application. I think if you understand the dirty reads concept you will be able to select a good option.
Defaults may vary between difference databases. As an example, for MariaDB it is REPEATABLE READ
.
Example of when a dirty read can occur:
thread 1 thread 2
| |
write(x) |
| |
| read(x)
| |
rollback |
v v
value (x) is now dirty (incorrect)
So a sane default (if such can be claimed) could be ISOLATION_READ_COMMITTED
, which only lets you read values which have already been committed by other running transactions, in combination with a propagation level of REQUIRED
. Then you can work from there if your application has other needs.
A practical example of where a new transaction will always be created when entering the provideService
routine and completed when leaving:
public class FooService {
private Repository repo1;
private Repository repo2;
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void provideService() {
repo1.retrieveFoo();
repo2.retrieveFoo();
}
}
Had we instead used REQUIRED
, the transaction would remain open if the transaction was already open when entering the routine.
Note also that the result of a rollback
could be different as several executions could take part in the same transaction.
We can easily verify the behaviour with a test and see how results differ with propagation levels:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:/fooService.xml")
public class FooServiceTests {
private @Autowired TransactionManager transactionManager;
private @Autowired FooService fooService;
@Test
public void testProvideService() {
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
fooService.provideService();
transactionManager.rollback(status);
// assert repository values are unchanged ...
}
With a propagation level of
REQUIRES_NEW
: we would expect fooService.provideService()
was NOT rolled back since it created it's own sub-transaction.
REQUIRED
: we would expect everything was rolled back and the backing store was unchanged.
Best Answer
Propagation.REQUIRED
(documented here) will create a new transaction (if none exists for the current thread), or will join an existing transaction (if one exists).When the method exits, then the transaction will be completed (if entering the method caused a transaction to be created), or will leave the transaction open (if a transaction already existed when the method was entered). In other, words, it's symmetrical, and will leave the thread's transactional state in the same state it was before the method was entered.