Skip to content
SparkTime, home
Java From Zero to Job-Ready
Lessons
1 · Java Foundations
2 · Object-Oriented Java
3 · Core Java
4 · Collections and Generics
5 · Functional Java
6 · The JVM and Concurrency
7 · Tools and Craft
8 · Databases and JDBC
9 · Web and Spring
10 · Persistence with JPA
11 · Secure, Tested, Production-Ready
12 · Interviews and Readiness
Lesson 41 of 41

Lesson 41 · Interviews and Readiness

Java Interview Concepts, Part 2: SQL, Spring, JPA, Security, Testing and the Job-Ready Checklist

Prepare for backend interviews by concept: SQL joins, indexes and transactions, REST design, dependency injection and Spring Boot, JPA problems such as N+1 and lazy loading, security, testing, and a final job-ready checklist you can use to verify your own readiness.

On this page

Key takeaways

  • Backend interviews test whether you understand why a design works, not only how to type it. Prepare explanations backed by something you built: the N+1 count, the lost update, the ownership check.
  • SQL answers need three things: how the query is evaluated, what NULL does, and when an index helps. Transaction answers need atomicity, isolation levels and the lost update.
  • Spring answers start from the container: beans, constructor injection, scopes, proxies and auto-configuration that backs off.
  • JPA answers are about the persistence context, lazy loading, N+1, the transaction boundary and optimistic locking.
  • Use the checklist at the end to find honest gaps, and close them by building and running something, not by re-reading.

This article is the second half of the interview preparation. It uses the same format as Part 1: short answer, explanation, example, follow-up and common wrong answer. It ends with a job-ready checklist so you can test yourself honestly against what the course taught.

SQL and databases

#

INNER, LEFT and other joins

#
  • Short answer: INNER JOIN keeps rows that match on both sides, LEFT JOIN keeps every left row with NULLs where nothing matches.
  • Example: Categories with no expenses appear only through a left join with WHERE e.id IS NULL. In the course, OTHER was the one category with zero expenses.
  • Follow-up: "Why did my left join lose rows?" A condition on the right table in WHERE turns it into an inner join. Put it in ON.
  • Common wrong answer: Describing joins only with Venn diagrams and no example.
  • Learn more: article 27.

NULL

#
  • Short answer: NULL means unknown, so NULL = NULL is not true. Use IS NULL.
  • Explanation: WHERE keeps only rows where the condition is true. Aggregates ignore NULL (except COUNT(*)).
  • Follow-up: "NOT IN with a NULL in the list?" It returns no rows, because the comparison is unknown. Prefer NOT EXISTS.

WHERE versus HAVING, GROUP BY

#
  • Short answer: WHERE filters rows before grouping, HAVING filters groups after aggregation. Every selected column must be grouped or aggregated.

Indexes

#
  • Short answer: An index (usually a B-tree) lets the database find rows without scanning the table, at the cost of slower writes and more disk.
  • Explanation: Check with EXPLAIN. PostgreSQL does not index foreign keys automatically. A function on an indexed column usually prevents its use. The planner chooses from statistics, and may rightly choose a sequential scan for a small table.
  • Follow-up: "A composite index on (a, b) and a query on b only?" Generally not used effectively.
  • Common wrong answer: "Add an index to every column."
  • Learn more: article 27.

Transactions, ACID and isolation

#
  • Short answer: A transaction is all-or-nothing. ACID is atomicity, consistency, isolation, durability.
  • Explanation: In PostgreSQL, the default Read Committed level never shows uncommitted data. A statement sees only data committed before it began. After an error in a transaction, the transaction is aborted until rollback. The lost update (two read-modify-write cycles) needs locking, an atomic UPDATE, or a version check.
  • Follow-up: "Non-repeatable read versus phantom read?" The same row read twice differs, versus the same query returning different rows.
  • Learn more: article 27.

Normalisation

#
  • Short answer: Store each fact once, so an update changes it everywhere. Denormalise only for a measured reason.
  • Example: The flat table in the course had FOOD and FOOD & DRINK after one update: an update anomaly.

JDBC and SQL injection

#
  • Short answer: Use PreparedStatement with ? parameters. The value is sent separately, so it can never change the query.
  • Example: x' OR '1'='1 returned every row from the concatenated query and none from the prepared one.
  • Follow-up: "Can you parameterise a table name?" No. Choose it from a fixed list in code.
  • Common wrong answer: "Escape the quotes."
  • Learn more: article 28.

Connection pools

#
  • Short answer: Opening connections is slow, so a pool lends a few shared ones. close() returns the connection to the pool.
  • Follow-up: "What is a connection leak?" Not closing a borrowed connection. The pool is exhausted and requests time out. A larger pool is rarely the answer.

REST and HTTP

#

Methods, idempotency and status codes

#
  • Short answer: GET is safe and idempotent, PUT and DELETE are idempotent, POST is neither.
  • Example: Two identical POSTs created two resources, two PUTs left one state, and the second DELETE returned 404 with the same final state.
  • Follow-up: "How make a POST safe to retry?" An idempotency key.
  • Follow-up: "401 versus 403?" Not authenticated versus authenticated but not allowed.
  • Common wrong answer: "PUT creates and POST updates."
  • Learn more: article 30.

Good API design

#
  • Short answer: Nouns in URLs, standard methods, correct status codes, consistent errors (problem details), paging with a maximum size, versioning for breaking changes.
  • Follow-up: "400 or 409 for a rule violation?" 400 for invalid input, 409 when the request conflicts with the current state.

Spring and Spring Boot

#

Dependency injection and IoC

#
  • Short answer: An object receives its collaborators instead of creating them. With IoC, the container creates and wires the objects.
  • Explanation: Prefer constructor injection: dependencies are explicit, fields can be final and tests need no Spring.
  • Follow-up: "Why not field injection?" It hides dependencies, blocks final and makes unit tests need reflection or a container.
  • Learn more: article 31.

Beans, scopes and the lifecycle

#
  • Short answer: Beans are singletons by default, so they must be stateless or thread-safe.
  • Explanation: @Component, @Service and @Repository mark classes, @Bean methods build objects you do not own. Two candidates need @Primary or @Qualifier. Constructor cycles fail at start-up.
  • Follow-up: "A field int count in a service?" A data race shared by all requests.

Spring versus Spring Boot, auto-configuration

#
  • Short answer: Boot adds starters, auto-configuration, an embedded server and externalised configuration to Spring.
  • Explanation: Auto-configuration is conditional (on a class, a missing bean or a property), so your own bean makes Boot back off. --debug prints the conditions report.
  • Learn more: article 32.

Configuration and profiles

#
  • Short answer: The command line beats environment variables, which beat application.properties, and a profile file beats the default file. Secrets come from the environment.

Validation and error handling

#
  • Short answer: Bean Validation on request DTOs with @Valid, business rules in the service, and one @RestControllerAdvice returning problem details.
  • Follow-up: "Why not return the entity?" Mass assignment, leaking internals and coupling the API to the schema.
  • Learn more: article 33.

JPA and Hibernate

#

JPA, Hibernate and Spring Data

#
  • Short answer: JPA is the specification, Hibernate the implementation, Spring Data generates repositories on top.
  • Learn more: article 34.

The persistence context and dirty checking

#
  • Short answer: Inside a transaction, Hibernate tracks managed entities and writes the changes at flush, without an explicit save.
  • Example: A status change produced a single UPDATE for only the changed columns.

Lazy loading and LazyInitializationException

#
  • Short answer: Lazy associations load on first access, only while the session is open.
  • Explanation: Map to DTOs inside the transaction, or fetch what you need with a join fetch or batch fetching. Avoid EAGER.

The N+1 problem

#
  • Short answer: One query for the list plus one per row for an association.
  • Example: Measured on PostgreSQL: 6 statements for 5 rows, 1 with a join fetch, 2 with @BatchSize.
  • Follow-up: "Join fetch with paging?" Risky for collections, because the limit cannot apply to parent rows after a join.
  • Common wrong answer: "Make it eager."
  • Learn more: article 35.

@Transactional

#
  • Short answer: A proxy opens a transaction around a public method called from another bean.
  • Explanation: Self-invocation bypasses it, unchecked exceptions roll back and checked ones do not by default (rollbackFor). Put it on services and keep transactions short.

Optimistic versus pessimistic locking

#
  • Short answer: Optimistic uses a version column and detects a conflict at write time, pessimistic locks the row on read.
  • Example: update ... where id=? and version=?. The second of two concurrent edits failed.

Security

#

Password storage

#
  • Short answer: A salted, slow hash such as bcrypt. Never plain text, never reversible encryption, never a fast hash.
  • Follow-up: "Why a salt?" Identical passwords get different hashes and precomputed tables fail.

JWT

#
  • Short answer: A signed (not encrypted) token with claims. The server verifies it without a lookup.
  • Explanation: Anyone can read the payload, so keep it minimal. It is valid until it expires, so keep it short-lived. The signing secret must be strong and secret.
  • Common wrong answer: "JWTs are encrypted."

Authentication versus authorisation, access control

#
  • Short answer: Who you are versus what you may do. Every query must be scoped to the caller. Return 404 for other users' data so existence is not revealed.

CSRF and CORS

#
  • Short answer: CSRF abuses cookies the browser sends automatically, so it is irrelevant to a bearer-token API and essential to cookie sessions. CORS tells browsers which origins may call the API, and it is not an access control.
  • Learn more: article 36.

Testing and delivery

#

The test pyramid and Spring test slices

#
  • Short answer: Many unit tests, some slice tests, few integration tests. @WebMvcTest loads the web layer, @DataJpaTest the JPA layer, @SpringBootTest everything.
  • Follow-up: "When do you mock?" For collaborators in unit tests, never for the thing under test, and prefer a real database for repository tests.
  • Learn more: article 37.

Configuration, logging and containers

#
  • Short answer: One artifact, configured by the environment. Log to standard output without secrets. Health checks for liveness and readiness. A multi-stage image run as non-root.
  • Learn more: article 38.

Practice questions

#

Exercise 1

  1. Explain why @Transactional may not work on a method called from the same class.
  2. A list endpoint is slow and the data is correct. How do you diagnose it?
  3. Why does a user get 404 for another user's resource?
  4. What does a 409 tell the client that a 400 does not?
  5. How do you stop two clients overwriting each other?
  6. What belongs in a JWT, and what must not?
  7. How would you test that a user cannot read another user's data?
Sample answers
  1. The annotation works through a proxy, and a call on this does not pass through it.
  2. Look at the SQL and count statements per request (an N+1 is the usual suspect), then check the plan with EXPLAIN and the indexes.
  3. A 403 would confirm the resource exists. 404 reveals nothing, and the data is never returned.
  4. 409 says the request is valid but conflicts with the current state, and may succeed later. 400 says the request itself is wrong.
  5. Optimistic locking with a version column, with the client sending back the version it read.
  6. A user id, an expiry and little else, because it is readable. Never passwords or private data.
  7. An integration test with two users: create as one and assert 404 and an empty list for the other, and a mutation check that removing the owner filter breaks the test.

The job-ready checklist

#

Be honest: tick an item only if you could do it now, without notes, and ideally have done it in code you can show.

Language and tools

#
  • I can write, compile and run a Java program, and explain bytecode, the JVM and the JDK.
  • I can model a domain with classes, interfaces, records and enums, and explain equals/hashCode, immutability and pass-by-value.
  • I can choose between List, Set, Map and queue types and explain how HashMap works.
  • I can use lambdas and streams, and explain when a stream is the wrong tool.
  • I can handle exceptions properly, including try-with-resources.
  • I can explain the stack, the heap and garbage collection, and read a stack trace.
  • I can explain a race condition, volatile, synchronized, executors and virtual threads, and fix a racy counter.
  • I can build a Maven project, add dependencies and explain scopes, and use Git branches, merges and pull requests.
  • I can write unit tests with JUnit and Mockito, and explain the limits of mocking.

Databases and the web

#
  • I can write SQL with joins, grouping, NULL handling and transactions, and read an EXPLAIN plan.
  • I can use JDBC safely with PreparedStatement, transactions and a pool, and explain SQL injection.
  • I can design a REST API with correct methods, status codes, validation and error responses.
  • I can explain dependency injection and the Spring container, and build a layered Spring Boot API.
  • I can map JPA entities and relationships, manage the schema with migrations, and explain lazy loading.
  • I can find and fix an N+1 problem, and explain @Transactional and optimistic locking.

Security, quality and delivery

#
  • I can store passwords safely, issue and verify tokens, and enforce per-user access.
  • I can explain CORS, CSRF and why secrets stay out of the code.
  • I can test at several levels, use slices, and explain what coverage does and does not show.
  • I can configure an application from the environment, expose health checks and write a Dockerfile.

Projects and communication

#
  • I have three projects in a repository with READMEs that let a stranger run them.
  • I can explain each design decision in each project, and its limits, honestly.
  • I can describe a real bug I found and how I fixed it.
  • I can say "I do not know, here is how I would find out".

Reading your result

#
  • Mostly ticked: practise explaining aloud, and apply for roles.
  • Gaps in one area: re-run the programs and projects of that module, change them, and break them on purpose.
  • Gaps everywhere: that is normal at this stage. Pick the earliest unticked item and work forward, building rather than re-reading.

Common mistakes when preparing

#
  • Reading answers instead of explaining them aloud.
  • Practising only puzzles and not describing your own projects.
  • Claiming experience you cannot defend with a follow-up.
  • Ignoring the unglamorous topics (SQL, HTTP, testing), which interviews ask about most.
  • Waiting until you feel ready. Apply, learn from each interview and keep building.

Where to go next

#
  • Back to the start: the learning path overview lists every article by module.
  • To keep growing: add a feature to Project 3 from its extension list, deploy it, and write about what you learned.

Sources & further reading

  1. postgresql.org
  2. docs.spring.io
  3. top10.owasp.org
  • Guide · Technology

    Heaps and Priority Queues

  • Guide · Technology

    Dynamic Programming Practice: From 1D to 2D

  • Course · Technology

    DSA From Zero to Job-Ready: A Practical Guide with Java