What does the sessionTransacted flag do on a JmsTemplate or a JMS listener container, and what is a transacted JMS Session?
answer
- Session = unit of work for JMS
- commit = messages real; rollback = redelivered
- listener throws -> rollback -> redelivery (at-least-once)
- local broker transaction, NOT JTA/DB
- make listeners idempotent
basics
~20 sIt makes the JMS Session transacted: sends/receives are buffered and only take effect when the session commits, or are undone on rollback. On a listener, a failed message is rolled back and redelivered instead of lost.
solid answer
~40 sA JMS Session is the unit of work for producing and consuming messages. When it is transacted, the JMS provider groups all sends and receives until you explicitly commit (make them real) or rollback (discard sends, put received messages back for redelivery). Spring's `sessionTransacted=true` on `JmsTemplate` wraps each send in such a session commit. On a `DefaultMessageListenerContainer`, `setSessionTransacted(true)` means the container commits the session after your listener returns normally and rolls back (triggering broker redelivery) if it throws. This gives at-least-once delivery for consumers without any external transaction manager — it's a purely local, broker-side transaction, not JTA/XA. It does NOT coordinate with a database.
code
java · 31 lines@Configuration
@EnableJms
class JmsConfig {
@Bean
DefaultJmsListenerContainerFactory jmsListenerContainerFactory(
ConnectionFactory cf,
DefaultJmsListenerContainerFactoryConfigurer configurer) {
var factory = new DefaultJmsListenerContainerFactory();
configurer.configure(factory, cf);
// transacted session: commit after listener returns, rollback+redeliver on throw
factory.setSessionTransacted(true);
return factory;
}
@Bean
JmsTemplate jmsTemplate(ConnectionFactory cf) {
var template = new JmsTemplate(cf);
template.setSessionTransacted(true);
return template;
}
}
@Component
class OrderListener {
@JmsListener(destination = "orders")
void handle(OrderMessage msg) {
// throwing here -> session rollback -> broker redelivers the message
process(msg); // must be idempotent (duplicates possible)
}
}go deeper
Know that a transacted session commits on success and rolls back (redelivers) on failure, so messages aren't lost.
Explain it is a local broker transaction giving at-least-once, requiring idempotent listeners and a DLQ for poison messages.
Contrast with acknowledge modes, note it does not span the DB, and know how it composes with JmsTransactionManager.
Frame delivery guarantees (at-least-once vs exactly-once), redelivery/poison handling, and where transacted sessions fit an overall reliability design.
**JMS Session.** In JMS, a `Session` is a single-threaded context created from a `Connection`; it is where you create producers/consumers and it is the boundary of a *local transaction*. A session has an acknowledgement/transaction mode chosen at creation. If created transacted, the messages you send are not visible to consumers and the messages you receive are not acknowledged until you call `Session.commit()`. Calling `Session.rollback()` discards buffered sends and causes received-but-uncommitted messages to be *redelivered* by the broker. **Spring's `sessionTransacted`.** - On **`JmsTemplate`**: `setSessionTransacted(true)` (Spring Boot: `spring.jms.template.*` does not expose it; set it on the bean, or via `JmsMessagingTemplate`) makes each template operation create a transacted session and commit it before returning. If the send throws, the session rolls back so nothing is sent. - On a **listener container** (`DefaultMessageListenerContainer`, `SimpleMessageListenerContainer`) or via `@JmsListener` factory `DefaultJmsListenerContainerFactory.setSessionTransacted(true)`: the container starts a transacted session, invokes your listener, then **commits** if the listener returns normally and **rolls back** if it throws. Rollback returns the message to the broker for redelivery, so the message is not lost — you get **at-least-once** delivery. **What it is NOT.** This is a *local* JMS transaction owned by the JMS provider. It only spans messaging. It does **not** enroll a database or any other resource — a DB write inside your listener commits/rolls back completely independently. Coordinating JMS + DB atomically needs either XA (a `JtaTransactionManager`) or a best-effort pattern (see below). `sessionTransacted` also has no relationship to Spring's `@Transactional`/`PlatformTransactionManager` by itself; however you can bind a `JmsTransactionManager` so that a Spring-managed transaction commits the JMS session. **Alternative to transacted sessions: acknowledge modes.** Without transactions you can use `AUTO_ACKNOWLEDGE`, `CLIENT_ACKNOWLEDGE`, or `DUPS_OK_ACKNOWLEDGE`. Spring's container default `sessionAcknowledgeMode` is `AUTO_ACKNOWLEDGE`, which acks *before/around* listener invocation depending on config and can lose a message on failure. Setting `sessionTransacted=true` is the simplest way to guarantee redelivery on listener failure. **Gotchas.** (1) At-least-once means **duplicates are possible** on redelivery — make listeners idempotent. (2) Poison messages can redeliver forever; configure a redelivery limit / dead-letter queue on the broker. (3) Transacted sessions have some throughput cost versus plain auto-ack. (4) With `JmsTemplate`, a transacted send that is never followed by a receive still just commits the send immediately — the benefit shows mainly on the consumer side and when combined with a transaction manager.
- If the listener succeeds but the JVM crashes after processing and before the session commit, what happens?The session never committed, so the broker redelivers the message on restart. The work is repeated — hence at-least-once and the need for idempotency. Nothing is lost, but duplicates are possible.
- Does sessionTransacted on JmsTemplate roll back a database write in the same method?No. It only governs the JMS session. A DB write is a separate resource and commits independently unless you use XA or bind a JmsTransactionManager alongside the DB transaction via best-effort chaining.
saying these in an interview costs you the question
- Thinking sessionTransacted also rolls back the database
- Believing transacted sessions give exactly-once delivery
- Confusing sessionTransacted with @Transactional / JTA
- Assuming AUTO_ACKNOWLEDGE never loses a message on listener failure