eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

Partner – LambdaTest – NPI EA (cat= Testing)
announcement - icon

Distributed systems often come with complex challenges such as service-to-service communication, state management, asynchronous messaging, security, and more.

Dapr (Distributed Application Runtime) provides a set of APIs and building blocks to address these challenges, abstracting away infrastructure so we can focus on business logic.

In this tutorial, we'll focus on Dapr's pub/sub API for message brokering. Using its Spring Boot integration, we'll simplify the creation of a loosely coupled, portable, and easily testable pub/sub messaging system:

>> Flexible Pub/Sub Messaging With Spring Boot and Dapr

1. Introduction

MyBatis Dynamic SQL is a library for generating SQL statements in a typesafe way. It helps us ensure that our SQL syntax and parameter bindings are valid. In addition, the SQL generated is based on class definitions representing our database, so we can leverage the compiler to help ensure our SQL is correct.

In this tutorial, we’re going to have a look at MyBatis Dynamic SQL. We’ll see what it is, what we can do with it, and how to use it.

2. Dependencies

Before using MyBatis Dynamic SQL, we need to include the latest version in our build, which is 1.5.2 at the time of writing.

If we’re using Maven, we can include this dependency in our pom.xml file:

<dependency>
    <groupId>org.mybatis.dynamic-sql</groupId>
    <artifactId>mybatis-dynamic-sql</artifactId>
    <version>1.5.2</version>
</dependency>

At this point, we’re ready to start using it in our application.

3. Database Objects

MyBatis Dynamic SQL ensures our SQL generation is typesafe by using types that represent the structure of our database. This includes classes that we write in our application to represent the database tables and columns we’re working with.

All of these database objects are simple classes that extend the SqlTable class:

public class User extends SqlTable {

    public User() {
        super("users");
    }
}

The constructor provides the name of the table within the database. In this case, we represent a table called users in the database.

We can then add column definitions to our table. These are done as fields in our class, generated with the column helper:

public class User extends AliasableSqlTable<User> {
    public final SqlColumn<Integer> userId = column("user_id");
    public final SqlColumn<String> userName = column("username");
}

Making these fields public allows us to reference them from outside the class, which we need to do to generate SQL.

4. Generating SQL

Once we’ve built our database objects, we need to be able to generate SQL. MyBatis Dynamic SQL starts all query building using a static method from the SqlBuilder class – for example, SqlBuilder.countFrom(). This determines the type of query we’re going to build, and returns suitable types for us to use:

CountDSL<SelectModel> count = SqlBuilder.countFrom(user);

Here we’re providing an instance of our database object to the countFrom() method. This indicates the table that we’re going to work with.

We can further chain the result of this with appropriate methods, as we’ll see later. Ultimately, we call a build() method that returns a model class for our SQL statement:

SelectModel model = SqlBuilder.countFrom(user)
  .build();

From here, we can then call a render() method that will actually render the SQL statement:

SelectStatementProvider sqlStatement = SqlBuilder.select(user.allColumns())
  .from(user)
  .build()
  .render(RenderingStrategies.SPRING_NAMED_PARAMETER);

This method takes a rendering strategy to use. Two standard options are available to us – one for working with MyBatis3 and one for working with Spring.

The statement provider that’s returned from the render() call then gives us access to the actual SQL and to the bind parameters we need to call the SQL:

String sql = sqlStatement.getSelectStatement();
Map<String, Object> binds = sqlStatement.getParameters();

We can then provide these to our database call to actually perform our query.

5. Select Statements

Now that we know how to generate our SQL and bind parameters with our database objects, we’re ready to start building some statements.

Select statements are some of the more complex we can build, since they have various parts and ways they can work.

All select statements start with one of a few static methods:

  • select() – starts building a normal SELECT statement to return a set of columns.
  • selectDistinct() – starts building a SELECT DISTINCT statement to return a set of columns.
  • countFrom() – starts building a SELECT COUNT(*) statement to return a count from a table.
  • countColumn() – starts building a SELECT COUNT(column) statement to count non-null values in the specified column.
  • countDistinctColumn() – starts building a SELECT COUNT(DISTINCT column) statement to count distinct non-null values in the specified column.

With the exception of countFrom(), each of these then needs to be provided with the table to select from:

User user = new User();
SelectStatementProvider sql = SqlBuilder.select(user.allColumns())
  .from(user)
  .build()
  .render(RenderingStrategies.SPRING_NAMED_PARAMETER);
// SELECT * FROM users

We also need to specify which columns we want to return. This can be any of the column definitions we created earlier, or the special value allColumns().

We can also use some special aggregate values for our selected columns. The SqlBuilder class provides access to methods such as count()max()sum(), and so on. We can use these exactly the same as any other column definition, and the result will be exactly as expected.

5.1. Where Clauses

By default, our select statements will just return everything. We can add WHERE clauses to filter the responses, though. These start using the where() method after the table has been specified:

SelectStatementProvider sql = SqlBuilder.select(user.allColumns())
  .from(user)
  .where(user.userName, SqlBuilder.isEqualTo("baeldung"))
  .build()
  .render(RenderingStrategies.SPRING_NAMED_PARAMETER);

Here, the where() method takes the column to compare and the comparison to use. The SquBuilder class has static methods for all the comparisons we want to make. There are also alternative versions that can be used for more complex conditions, such as for EXISTS clauses with sub-selects.

MyBatis Dynamic SQL uses generics here to ensure comparisons are typesafe. The types we can use for the comparisons must be suitable for the type of column we’re comparing. Here, the userName column is a SqlColumn<String>, so the isEqualTo() method must also take a string.

When generating this SQL, we now get a bind value. The SQL generated from the above would be:

SELECT * FROM users WHERE username = :p1

and the SelectStatementProvider.getParameters() method returns a map containing the entry p1 with our value.

We can also build more complex WHERE clauses using and() and or():

SelectStatementProvider sql = SqlBuilder.select(user.allColumns())
  .from(user)
  .where(user.userName, SqlBuilder.isEqualTo("baeldung"))
  .or(user.userId, SqlBuilder.isGreaterThan(5))
  .build()
  .render(RenderingStrategies.SPRING_NAMED_PARAMETER);
// SELECT * FROM users WHERE username = :p1 OR user_id > :p2

With this, we can build statements that are as complex as needed whilst still being typesafe and correctly balanced.

5.2. Joins

So far, we’ve seen how to write SQL statements that reference a single table, but we often need to join multiple tables. MyBatis Dynamic SQL allows us to do this using the join() method when we’re building the SQL:

SelectStatementProvider sql = SqlBuilder.select(post.allColumns())
  .from(post)
  .join(user).on(user.userId, SqlBuilder.equalTo(post.posterId))
  .build()
  .render(RenderingStrategies.SPRING_NAMED_PARAMETER);
// SELECT posts.* FROM posts JOIN users ON users.user_id = posts.poster_id

Here, the join() method takes the database object for the table we’re joining to. We then need to use the on() method to indicate how the join works. Note that this uses equalTo instead of isEqualTo. This is because it’s a different type of condition and therefore needs different Java types for type safety.

We also have a few different join methods that we can use, depending on exactly what we’re trying to achieve:

  • join() – this builds a standard JOIN … ON … statement.
  • leftJoin() – this builds a LEFT JOIN … ON … statement.
  • rightJoin() – this builds a LEFT JOIN … ON … statement.
  • fullJoin() – this builds a FULL JOIN … ON … statement.

All of these work in the same way, with the only difference being the type of join that’s generated.

6. Other Statements

In addition to SELECT statements, we can also build other SQL statements.

DELETE statements are the most similar to what we’ve already seen. We start these using the SqlBuilder.deleteFrom() method and then provide a suitable WHERE clause:

DeleteStatementProvider sql = SqlBuilder.deleteFrom(user)
  .where(user.userId, isEqualTo(1))
  .build()
  .render(RenderingStrategies.SPRING_NAMED_PARAMETER);
// DELETE FROM users WHERE user_id = :p1

UPDATE statements are similar, though they also have support for setting the values of fields as part of the statement too:

UpdateStatementProvider sql = SqlBuilder.update(user)
  .set(user.userName).equalTo("Baeldung")
  .where(user.userId, isEqualTo(1))
  .build()
  .render(RenderingStrategies.SPRING_NAMED_PARAMETER);
// UPDATE users SET username = :p1 WHERE user_id = :p2

Finally, INSERT statements require us to provide the new values but don’t support a WHERE clause:

GeneralInsertStatementProvider sql = SqlBuilder.insertInto(user)
  .set(user.userId).toValue(2)
  .set(user.userName).toValue("Baeldung")
  .build()
  .render(RenderingStrategies.SPRING_NAMED_PARAMETER);
// INSERT INTO users(user_id, username) VALUES (:p1, :p2)

All of these ultimately return an appropriate statement provider instance that we can then use to get the SQL and bind parameters from to execute our statement against the database.

8. Summary

This was a quick introduction to MyBatis Dynamic SQL. There’s a lot more that we can do with this library. Next time we need to generate SQL statements safely, why not give it a try?

As usual, all of the examples from this article are available over on GitHub.

Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

eBook Jackson – NPI EA – 3 (cat = Jackson)