51/60 = 85%, against a 76% pass mark. Your best score in seventeen papers, and
+2 on #16. Spring Core 71% → 96%. Spring Boot 75% → 89%.
Those were the two sections the last page told you to drill. Finished in 44m 22s, your fastest pass.
The margin is now five questions (46/60 is the lowest pass), up from three. This page is about
the three sections that slipped while you worked on the other two, Data (70%), MVC (71%) and Testing
(75%), and about the seven misses that were already answered on this site.
Section sizes: Data 7/10 = 70%, MVC 5/7 = 71.4%, Testing 6/8 = 75% and Boot 8/9 = 88.9% are the
only sizes that give those percentages with those miss counts. That leaves 26 questions for Core and Security.
Core is 24 or 25 (23/24 and 24/25 both round to 96%), so Security is 2 or 1.
Small sections swing hard. MVC had seven questions, so each miss costs it 14 points. Its drop from
89% to 71% is one extra miss. Data's drop is two.
| Attempt #16 | Attempt #17 | Result | |
|---|---|---|---|
| Score | 49/60 (82%) | 51/60 (85%) | Best of seventeen |
| Misses | 11 | 9 | −2 |
| Questions with a false tick | 8 | 7 | −1 |
| Stopped short (no false tick) | 3 | 2 | −1 |
| Left blank | 0 | 0 | Held |
| Spring Core misses | 6 | 1 | −5 |
| Margin over the pass mark | 3 questions | 5 questions | +2 |
| Time | 51m 32s | 44m 22s | 86 minutes unused |
Spring Core went from six misses to one. It was the biggest section on #16 and the one the page spent most time on. Across the nine questions you missed there were 23 false options, and you rejected 16 of them. No question had more than one false tick.
The 30-minute routine on #16 pointed at Core and Boot. Here's what each section did next:
| Section | In #16's routine? | #16 → #17 | Misses |
|---|---|---|---|
| Spring Core | Yes — 15 minutes | 71% → 96% | 6 → 1 |
| Spring Boot | Yes — 5 minutes | 75% → 89% | 2 → 1 |
| Data Management | No | 91% → 70% | 1 → 3 |
| Spring MVC | No | 89% → 71% | 1 → 2 |
| Testing | No | 89% → 75% | 1 → 2 |
This is the same pattern as the mixed practice set: a section goes up after its clinic and slides back within a week or two. You gained six marks in Core and Boot and gave four back in the other three. So the routine below covers all five sections, weighted to the three that fell.
Two are the same question word for word. One was a drill on the last page, four days before this paper. These seven marks need no new study, only recall.
| Q | Topic | Already on this site | Then → now |
|---|---|---|---|
| Q41 | @RequestMapping statements | Review #10 Q37 — same question | Stopped short → ticked a false option |
| Q35 | matchIfMissing | Review #16, a drill: "property not set → does not match" | Four days later, ticked "still loaded" |
| Q33 | execute(String) returns void | Data clinic §5 — "caught you twice" | Third time, ticked Map |
| Q52 | Commit a test transaction | Review #10 Q5 | @NoRollback → @Transactional(commit=true) |
| Q43 | Prototypes get no destroy callback | Review #9 Q57 | Ticked "All of the above" |
| Q19 | Using a Specification | Review #8 Q1 | Ticked @Query |
| Q2 | Controller parameter annotations | MVC round 3 §2 — lists all five | Stopped short — all five were true |
Q35 is the one to learn from. The drill on #16 was "@ConditionalOnProperty(name = "enable.x", havingValue = "true") — the property is not set at all. What happens?" Four days later the same fact came back in a longer question and went the other way. A fact you've drilled once isn't reliable yet. That's why every repeat here has drills that ask it from a different angle.
| Bucket | Marks | What it costs you |
|---|---|---|
| The seven repeats | 7 | No new material. Q2, Q19, Q33, Q35, Q41, Q43, Q52 |
| Real annotation, invented job | 3 | Q19, Q41, Q52 — the trap drill below |
| Take them all when all check out | 2 | Q2 (five of five), Q56 (three of four) |
| "All of the above" needs every option | 1 | Q43 — one false option kills it |
| Genuinely new | 2 | Q51 (keyword order), Q56 (@SpringBootTest's Javadoc list) |
The rows overlap, so don't add them up. Only two of the nine were new to you. Aim for 54+ next time, which is 90%.
| Q | Topic | What went wrong | The answer |
|---|---|---|---|
| Q2 | Controller parameter annotations | Stopped short — all 5 were true | @MatrixVariable · @RequestParam · @PathVariable · @RequestHeader · @CookieValue |
| Q19 | Using a Specification | + "use @Query" | findAll(spec) from JpaSpecificationExecutor |
| Q33 | JdbcTemplate.execute(String) return type | + Map | void |
| Q35 | matchIfMissing = false | + "property not defined → still loaded" | Set matchIfMissing = true to load it when missing |
| Q41 | @RequestMapping statements | + "redundant with @RestController" | All HTTP methods by default · path takes an array |
| Q43 | Which one is true about scopes | + "All of the above" | Prototypes get no destroy callbacks |
| Q51 | Derived query name order | + ordering before comparison | find(First[n])By + property + comparison + ordering |
| Q52 | Commit a test transaction | + @Transactional(commit=true) — invented | @Commit · @Rollback(false) |
| Q56 | What @SpringBootTest configures | Stopped short — 3 were true | Finds @SpringBootConfiguration · TestRestTemplate/WebTestClient · default context loader |
Six of the nine misses were one of three shapes of question. The other three (Q33, Q35, Q51) are plain facts.
On #15 and #16 the fakes were invented names: TransactionStatusAdvice,
BeanFactoryApplicationContext. You now reject those. This paper hid the fakes inside real
annotations instead, and you ticked all three:
| Q | Real part | Invented job | The test that exposes it |
|---|---|---|---|
| Q52 | @Transactional | a commit attribute | List its attributes: propagation, isolation, timeout, readOnly, rollbackFor, noRollbackFor. No commit |
| Q19 | @Query | holds a Specification | @Query takes a JPQL string. A Specification is a Java object, so it can only be passed in as an argument |
| Q41 | @RestController | maps URLs from method names | Name one controller you've written with no @GetMapping. There isn't one |
@Transactional(commit = true) doesn't compile.Q2 had five options and all five were true. Q56 had four and three were true. You ticked no false option on either question, so you stopped before you'd taken every true one. It's the third paper in a row with this shape: #16 had Q30 (four of four), Q4 and Q55.
The routine: go through the options one by one and mark each true or false on its own. Then tick every one you marked true, even if that's all of them. Don't decide in advance how many answers there "should" be.
Q43 asked for one true statement, and "All of the above" was one of the options. Ticking it says A and B and C are all true. Find one false option and "All of the above" is out. Here there were two: singletons are created at startup, not lazily (A), and the stateless/stateful advice is the wrong way round (C).
JdbcTemplate.execute(String sql) ticked a false option 3rd time · data clinic §5execute(String sql) returns void. It's for DDL.Map. You correctly rejected "an entity" and
int. The Data clinic says of this exact
distinction: "it caught you twice". This is the third.| Method | Returns | For |
|---|---|---|
execute(String sql) | void | DDL: CREATE, ALTER, DROP |
update(sql, args…) | int — rows affected | INSERT, UPDATE, DELETE |
batchUpdate(…) | int[] | Many updates |
queryForObject(sql, Class) | T | One value or one row |
queryForMap(sql) | Map<String, Object> | One row as a map |
queryForList(sql) | List<Map<String, Object>> | Many rows as maps |
query(sql, RowMapper) | List<T> | Many rows as objects |
Why Map was tempting: queryForMap is a real method. But
only the query… methods return data. The name tells you the return type: query returns
rows, update returns a count, execute returns nothing. (The execute overloads that
take a callback return whatever the callback returns. The one that takes a plain String returns
void.)
Specification real part, invented job review #8 Q1findAll(spec). @Query has nothing to do with it.@Query to define a specification-based query".
You correctly rejected "annotate specifications with @Query" and "a custom repository
implementation".public interface ProductRepository
extends JpaRepository<Product, Long>, JpaSpecificationExecutor<Product> {}
// the Specification is an ARGUMENT, built at runtime
List<Product> cheap = repo.findAll(hasPriceGreaterThan(new BigDecimal("50")));
List<Product> both = repo.findAll(
hasPriceGreaterThan(new BigDecimal("50")).and(hasCategory("Electronics")));
| Way to query | Where the query lives |
|---|---|
| Derived query | The method name — findByPriceGreaterThan |
@Query | A JPQL or SQL string on the method |
| Specification | A Criteria API object passed to findAll / findOne / count |
| Query by Example | An Example<T> object passed to findAll (QueryByExampleExecutor) |
JpaSpecificationExecutor<T> gives you:
findOne(spec) → Optional<T> · findAll(spec) → List<T> ·
findAll(spec, Pageable) → Page<T> · findAll(spec, Sort) →
List<T> · count(spec) → long. Specifications combine with
and(), or() and Specification.not().
JpaSpecificationExecutor, then call findAll(spec).find(First[count]) By[Property] [ordering] [comparison], with
ordering and comparison swapped. You correctly rejected both options that put operators before
By.List<User> findFirst3ByAgeGreaterThanOrderByLastNameAsc(int age);
// ^^^^^^ ^^ ^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^
// limit By prop comparison ordering
// select u from User u where u.age > ?1 order by u.lastName asc (limit 3)
Why the order can't be swapped: the comparison keyword belongs to its property.
AgeGreaterThan is one piece, the same as age > ?1 in SQL. OrderBy starts a
new clause and always comes last, as in SQL. Put OrderBy between Age and
GreaterThan and Spring would try to sort by a property called AgeGreaterThan.
@RequestMapping statements real part, invented job same question · review #10 Q37path takes an array@RestController, as such
classes create mappings by their method names". You correctly rejected "method-level
only". On #10 you stopped short on this same question; this time you ticked a false option.| Option | Verdict | Why |
|---|---|---|
| Method-level only | False | Class level sets a base path for every method |
| Maps all HTTP methods unless you say otherwise | True | Add method = RequestMethod.GET to restrict it |
Redundant with @RestController; methods map by name | False | @RestController = @Controller + @ResponseBody. That's all it adds |
path takes an array of Strings | True | @RequestMapping({"/users", "/members"}) |
Where "mapped by method name" comes from: method names do mean something
in Spring Data. findByLastName becomes a query. In Spring MVC a method name means nothing. A URL
exists only if an annotation declares it. Also note that @GetMapping and its siblings can only go on
methods. @RequestMapping is the only one that also works on a class.
@RequestMapping = every verb; path = an array.| Annotation | Binds from | Example |
|---|---|---|
@PathVariable | A URI template variable | /orders/{id} |
@RequestParam | Query string or form field | ?page=2 |
@MatrixVariable | Name-value pairs inside a path segment | /cars;color=red;year=2012 |
@RequestHeader | An HTTP header | Accept-Language |
@CookieValue | A cookie | JSESSIONID |
@RequestBody | The body, through an HttpMessageConverter | JSON |
@ModelAttribute | Several parameters bound onto one object | A form |
@SessionAttribute · @RequestAttribute | Session / request attributes | — |
Fakes you might meet: @QueryParam, @PathParam,
@HeaderParam, @CookieParam and @FormParam are real, but they're
JAX-RS, not Spring MVC. @MatrixVariable only works if semicolon content isn't stripped from the
URL. Set that with configurePathMatch() in a WebMvcConfigurer.
@QueryParam, @PathParam and @HeaderParam are JAX-RS, not Spring.@Commit and @Rollback(false). @Transactional has no commit attribute.@Transactional(commit=true). You correctly
rejected @Sql(alwaysCommit=true) and @SqlMergeMode(false). On #10 the fakes were
@NoRollback and @NoRollback(true), invented annotations. This time the fake was an
invented attribute on a real annotation.@SpringBootTest
@Transactional // tests roll back by default
class OrderRepositoryTest {
@Test @Commit // commit this one
void savesForReal() { … }
@Test @Rollback(false) // same effect
void alsoSavesForReal() { … }
}
| Annotation | Main attributes |
|---|---|
@Transactional | transactionManager · propagation · isolation · timeout · readOnly · rollbackFor · noRollbackFor |
@Rollback | value — true by default |
@Commit | none. It's meta-annotated with @Rollback(false) |
@Sql | scripts/value · statements · executionPhase · config |
@SqlMergeMode | MergeMode.MERGE or OVERRIDE — an enum, not a boolean |
@Commit and @Rollback(false). @Transactional's attributes control how the transaction runs, not how a test ends.@SpringBootTest configures stopped short@SpringBootTest's Javadoc.PropertySourcesPlaceholderConfigurer.@SpringBootTest's Javadoc says it… | Detail |
|---|---|
Uses SpringBootContextLoader as the default context loader | Unless you set @ContextConfiguration(loader = …) |
Searches for a @SpringBootConfiguration | Unless there's a nested @Configuration or you pass classes |
Registers a TestRestTemplate and/or WebTestClient | Only when a real server runs: RANDOM_PORT or DEFINED_PORT |
| Lets you set Environment properties | properties = "app.x=1" |
| Lets you pass application arguments | args = "--debug" |
Supports the webEnvironment modes | MOCK (default) · RANDOM_PORT · DEFINED_PORT · NONE |
What it does not give you: MockMvc. That needs
@AutoConfigureMockMvc as well (@WebMvcTest includes it). And there's no
TestRestTemplate in the default MOCK mode, because no server is running.
matchIfMissing = false ticked a false option a drill on review #16, 4 days earliermatchIfMissing = false → not loadedcustom.feature.enabled is not defined in the properties file,
CustomFeatureAutoConfiguration will still be loaded." You correctly rejected
"YAML only", "loaded only when set to false" and "always loaded".havingValue = "true" and… | Property = true | Property = false | Property absent |
|---|---|---|---|
matchIfMissing = false (default) | Loaded | Not loaded | Not loaded |
matchIfMissing = true | Loaded | Not loaded | Loaded |
What may be leaking in: without havingValue, any value
except false matches. That rule is about which values match. Whether a missing property
matches is decided by matchIfMissing alone, and it defaults to false. So a feature is off unless
you turn it on.
The bank's answer is loosely worded: "to ensure the configuration always
loads, set matchIfMissing = true". It doesn't make it load every time: with the property set to
false it still won't load. It's the best of the five options, not a precise statement.
matchIfMissing = true. Features are opt-in by default.@PreDestroy for prototypes, is from review #9.| Option | Verdict | Counterexample |
|---|---|---|
| A. A singleton is created lazily, on first request | False | The ApplicationContext creates every non-@Lazy singleton at startup, in refresh() |
| B. Destroy callbacks aren't called on prototypes | True | The container hands a prototype over and stops tracking it |
| C. Prototype for stateless, singleton for stateful | False | The wrong way round. A shared singleton must hold no per-user state |
| D. All of the above | False | A is false, so "all" is false |
Why eager is the default: a missing dependency or a bad config value should fail
at startup, not on the first request an hour later. A plain BeanFactory is lazy. The
ApplicationContext isn't, unless a bean is @Lazy. Prototypes are always created on
demand, and Spring calls @PostConstruct on them but never @PreDestroy.
execute(String) returns void. update returns an int, and only the query… methods return dataJpaSpecificationExecutor, then call findAll(spec). @Query takes stringsfind First3 By Age GreaterThan OrderBy LastName Asc. OrderBy goes last@RequestMapping = every verb; path takes an array. Nothing in MVC maps URLs from method names@Commit or @Rollback(false). @Transactional has no commit attribute@SpringBootTest finds @SpringBootConfiguration, uses SpringBootContextLoader, adds TestRestTemplate for a real servermatchIfMissing = false → not loaded. Opt-in by defaultTen questions with no topic label to prime you. Judge each option on its own, then tick every true one. For each annotation, ask whether you could actually write that line of code.
@Transactional(commit = true), @Query
holding a Specification, URLs mapped by method name: all three were real annotations with an invented job.
(4) Judge each option on its own, then tick every true one. Q2 was five of five.
(5) Aim for 54+. Seven of the nine misses were already on this site.
@RequestMapping.
5 min — Testing. Committing tests and @SpringBootTest.
5 min — the mixed drill. Every section, so Core and Boot stay warm.