Java Database Connectivity (JDBC) – A Simple Tutorial for Students

Welcome, future developers! If you’re looking to connect your Java applications with databases, you’re in the right place. Java Database Connectivity, commonly known as JDBC, is the bridge that links them together. It’s a powerful tool in a developer’s toolkit, allowing seamless interaction between Java and various databases. Dive into this tutorial, and by the end, you’ll have a clear understanding of the basics. Let’s embark on this journey together!

JDBC Architecture

When you think of JDBC, visualize it as a bridge. This bridge connects your Java application to a database. Central to this are the JDBC Drivers – they facilitate the actual connection. As for the inner workings, the JDBC API provides key components: DriverManager manages a list of database drivers, Connection connects to the actual database, Statement lets you run SQL queries, ResultSet fetches results, and PreparedStatement helps with pre-compiled SQL statements.

JDBC Architecture Diagram

Setting Up Your Environment

Before we dive deep, let’s set the stage. First, you’ll need to install a JDBC driver specific to the database you’re using. Now, balancing coding with school can be overwhelming. If you’re swamped with assignments, consider using services like Essay Pro to manage your workload. This way, you can allocate more time to coding. Once you’re set, proceed to set up a database for testing. This foundation is crucial for hands-on learning ahead.

Establishing a Connection

First things first: to chat with our database using Java, we need to establish a connection. Begin by loading the JDBC driver – think of it as dialing a friend’s number. Once that’s done, you can actually ‘call’ or connect to the database. It’s like establishing a direct line of communication between your Java code and the database.

try (Connection connection = 
        DriverManager.getConnection(URL, USERNAME, PASSWORD)) {
} catch (Exception e) {

Executing SQL Statements

Now that we’re connected, let’s converse! Start by creating a Statement object. This is your tool to “speak” SQL. Whether you want to ask the database a question (query) or tell it to do something (update), the Statement object has your back.

Statement statement = connection.createStatement();
String query = "SELECT * FROM book";
ResultSet books = statement.executeQuery(query);

Working with ResultSet

So, you’ve asked your database a question. Where’s the answer? Enter ResultSet. As you iterate through the ResultSet, it’s like flipping through the pages of a book, gathering the information you asked for. Want specifics? You can retrieve data based on the column, making it easy to pinpoint exactly what you’re after.

while ( {
    System.out.println(books.getString("isbn") + ", " +
            books.getString("title") + ", " +

Using PreparedStatement

Imagine sending a tailor-made invitation; it’s more efficient and safer. That’s what PreparedStatement offers over regular statements. This nifty tool lets you create SQL statements with placeholders, reducing errors and speeding things up. Once your structure is set, you can easily customize the message by filling in specific parameters. It’s like having a template for smoother, more personalized database interactions.

String orderDetailQuery = """
        INSERT INTO purchase_order_detail (order_id, product_id, quantity, price)
        VALUES (?, ?, ?, ?)

PreparedStatement detailStmt = conn.prepareStatement(orderDetailQuery);
detailStmt.setLong(1, 1L);
detailStmt.setInt(2, 1);
detailStmt.setInt(3, 10);
detailStmt.setBigDecimal(4, new BigDecimal("29.99"));

Handling SQL Exceptions

Even in the world of coding, things don’t always go as planned. When your Java application and the database have a miscommunication, SQL exceptions occur. But don’t fret! By catching an SQLException, you’re prepared to deal with these hiccups. This allows you to gracefully handle any bumps in the conversation. And the best part? You can retrieve specific error information, give you insights to troubleshoot and keep the conversation flowing smoothly.

Batch Processing with JDBC

Ever thought of sending multiple messages at once instead of one by one? That’s batch processing for you. In JDBC, this means executing multiple SQL commands in a single go. The perks? Faster operations and reduced server round trips. With addBatch() you line up your messages, and executeBatch() sends them all together in a neat package.

try (Statement statement = connection.createStatement()) {
    statement.addBatch("INSERT INTO product (code, name) " +
            "VALUE ('P0000006', 'Championship Manager')");
    statement.addBatch("INSERT INTO product (code, name) " +
            "VALUE ('P0000007', 'Transport Tycoon Deluxe')");

    int[] updateCounts = statement.executeBatch();
    System.out.println("updateCounts = " + Arrays.toString(updateCounts));
} catch (SQLException e) {

Transactions in JDBC

Think of a transaction as a promise. You’re telling the database, “I’ll make a series of changes, and if everything goes smoothly, let’s finalize them.” If something’s amiss, you can revert to the start, ensuring data integrity. This commitment is done using the ‘commit’ command. However, if there’s an issue, ‘rolling back’ undoes the changes, keeping your data safe and sound.

try (Connection conn =
             DriverManager.getConnection(URL, USERNAME, PASSWORD)) {

    String orderQuery = """
            INSERT INTO purchase_order (username, order_date)
            VALUES (?, ?)

    try (PreparedStatement stmt = conn.prepareStatement(orderQuery,
            PreparedStatement.RETURN_GENERATED_KEYS)) {
        stmt.setString(1, "jduke");
        stmt.setDate(2, new Date(System.currentTimeMillis()));

        ResultSet keys = stmt.getGeneratedKeys();
        long orderId = 1L;
        if ( {
            orderId = keys.getLong(1);

        // This is an invalid statement that will cause exception to
        // demonstrate a rollback.
        String orderDetailQuery = """
                INSERT INTO purchase_order_detail (order_id, product_id, quantity, price)
                VALUES (?, ?, ?, ?)

        PreparedStatement detailStmt = conn.prepareStatement(orderDetailQuery);
        detailStmt.setLong(1, orderId);
        detailStmt.setInt(2, 1);
        detailStmt.setInt(3, 10);
        detailStmt.setBigDecimal(4, new BigDecimal("29.99"));

        // Commit transaction to mark it as a success database operation
        System.out.println("Transaction commit...");
    } catch (SQLException e) {
        // Rollback any database transaction due to exception occurred
        System.out.println("Transaction rollback...");
} catch (Exception e) {

Connection Pooling

Remember those kiddie pools filled with balls? Connection pooling is kinda like that, but for database connections. Instead of making a new connection each time, you just grab one from the pool. It’s faster and conserves resources. When you’re done, toss it back! And setting up? It’s a one-time thing to ensure a reservoir of ready connections.

Closing Resources and Best Practices

Always tidy up after a chat! In JDBC, this means closing the Connection, Statement, and ResultSet to free up resources. Ever heard of the try-with-resources statement? It’s a Java gem that ensures resources are closed properly. And a quick tip: if managing JDBC feels overwhelming with your academic load, consider the best dissertation writing service to help with school, so you can focus on coding right.

try (Connection conn =
             DriverManager.getConnection(URL, USERNAME, PASSWORD);
     Statement stmt = conn.createStatement();
     ResultSet rs = stmt.executeQuery("SELECT * FROM product")) {

    while ( {
        String code = rs.getString("code");
        String name = rs.getString("name");

        System.out.println("Code: " + code + "; Name: " + name);
} catch (SQLException e) {

JDBC Limitations and Alternatives

JDBC is great, but it’s not perfect. For complex applications, it might feel a bit low-level or verbose. Enter ORM tools. They offer a more intuitive way to interact with databases, abstracting away much of the SQL. Think of them as an evolved, sophisticated version of JDBC for certain use cases.

Summary and Next Steps

And that’s a wrap! You’ve journeyed through JDBC’s landscape, grasped its core, and glimpsed its limitations. What’s next? Dive deeper, explore more advanced resources, and keep building. The coding world awaits your creations!

How do I create a string of repeated characters?

The following code demonstrates how to create a string of repeated characters. We use the String.repeat(int count) method introduced in Java 11. This method takes one parameter of type int which is the number of times to repeat the string. The count must be a positive number, a negative number will cause this method to throw java.lang.IllegalArgumentException.

In the snippet below, we use the method to repeat characters and draw some triangles. We combine the repeat() method with a for loop to draw the triangles.

package org.kodejava.basic;

public class StringRepeatDemo {
    public static void main(String[] args) {
        String star = "*";
        String fiveStars = star.repeat(5);
        System.out.println("fiveStars = " + fiveStars);

        String arrow = "-->";
        String arrows = arrow.repeat(10);
        System.out.println("arrows    = " + arrows);

        String asterisk = "#";
        for (int i = 1; i <= 10; i++) {

        int height = 10;
        for (int i = 1, j = 1; i <= height; i++, j += 2) {
            System.out.println(" ".repeat(height - i) + "*".repeat(j));

The outputs of the code snippet above are:

fiveStars = *****
arrows    = -->-->-->-->-->-->-->-->-->-->

How do I convert datetime string with optional part to a date object?

Since JDK 8, we can create a datetime formatter / parser pattern that can have optional sections. When parsing a datetime string that contains optional values, for example, a date without time part or a datetime without second part, we can create a parsing pattern wrapped within the [] symbols. The [ character is the optional section start symbol, and the ] character is the optional section end symbol. The pattern inside this symbol will be considered as an optional value.

We can use the java.time.format.DateTimeFormatter class to parse the string of datetime or format the datetime object, and use it with the new Java time API classes such as java.time.LocalDate or java.time.LocalDateTime to convert the string into respective LocalDate or LocalDateTime object as show in the code snippet below.

package org.kodejava.datetime;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class DateTimeParseOptionalParts {
    public static final String OPT_TIME_PATTERN = "yyyy-MM-dd[ HH:mm[:ss]]";
    public static final String OPT_SECOND_PATTERN = "yyyy-MM-dd HH:mm[:ss]";

    public static void main(String[] args) {
        DateTimeFormatter optTimeFormatter = DateTimeFormatter.ofPattern(OPT_TIME_PATTERN);
        LocalDate date1 = LocalDate.parse("2023-08-28", optTimeFormatter);
        LocalDate date2 = LocalDate.parse("2023-08-28 17:15", optTimeFormatter);
        LocalDate date3 = LocalDate.parse("2023-08-28 17:15:30", optTimeFormatter);
        System.out.println("date1 = " + date1);
        System.out.println("date2 = " + date2);
        System.out.println("date3 = " + date3);

        DateTimeFormatter optSecondFormatter = DateTimeFormatter.ofPattern(OPT_SECOND_PATTERN);
        LocalDateTime datetime1 = LocalDateTime.parse("2023-08-28 17:15", optSecondFormatter);
        LocalDateTime datetime2 = LocalDateTime.parse("2023-08-28 17:15:30", optSecondFormatter);
        System.out.println("datetime1 = " + datetime1);
        System.out.println("datetime2 = " + datetime2);

Here are the outputs of the code snippet above:

date1 = 2023-08-28
date2 = 2023-08-28
date3 = 2023-08-28
datetime1 = 2023-08-28T17:15
datetime2 = 2023-08-28T17:15:30

How do I split large excel file into multiple smaller files?

A friend of mine told me that he has a large Excel file, and he asked me if I could split the file into multiple smaller Excel files. So I write this little program using Apache POI to do it.

The code snippet below basically contains the following steps:

  1. Load the Excel as an InputStream from the classpath, if the file was not found the program will exit.
  2. Create XSSFWorkbook from the input stream, and get the first XSSFSheet from the workbook.
  3. Iterate the rows of data from the source worksheet.
  4. On the first rownum for each split file we create a new workbook using SXSSFWorkbook and also create the SXSSFSheet.
  5. Read the first row from the source worksheet, store it in headerRow to be used for creating header row in each new sheet.
  6. If we are at the first row, write the header.
  7. Write each row from the source sheet to the destination sheet untuk the max rows is reached.
  8. Write the workbook into a new file.

And here is the complete code that you can try.

package org.kodejava.poi;

import org.apache.poi.xssf.streaming.SXSSFCell;
import org.apache.poi.xssf.streaming.SXSSFRow;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;

public class SplitExcelDemo {
    public static final int MAX_ROWS_PER_FILE = 1000 - 1;
    public static final boolean WITH_HEADER = true;

    public static void main(String[] args) {
        LocalDateTime startTime =;
        String filename = "/stock.xlsx";
        try (InputStream is = SplitExcelDemo.class.getResourceAsStream(filename)) {
            if (is == null) {
                System.out.println("Source file was not found!");

            XSSFWorkbook srcWorkbook = new XSSFWorkbook(is);
            XSSFSheet srcSheet = srcWorkbook.getSheetAt(0);
            int physicalNumberOfRows = srcSheet.getPhysicalNumberOfRows();

            int rownum = 0;
            int splitCounter = 0;

            SXSSFWorkbook destWorkbook = null;
            SXSSFSheet destSheet = null;

            XSSFRow headerRow = null;

            for (Row srcRow : srcSheet) {
                if (rownum == 0) {
                    // At the beginning let's create a new workbook and worksheet
                    destWorkbook = new SXSSFWorkbook();
                    destSheet = destWorkbook.createSheet();

                if (srcRow.getRowNum() == 0 && WITH_HEADER) {
                    // Copy header row to be use in each split file
                    headerRow = (XSSFRow) srcRow;

                if (rownum == 0 && WITH_HEADER) {
                    // Add row header to each split file
                    if (headerRow != null) {
                        SXSSFRow firstRow = destSheet.createRow(rownum);
                        int index = 0;
                        for (Cell cell : headerRow) {
                            SXSSFCell headerCell = firstRow.createCell(index++, cell.getCellType());
                            if (cell.getCellType() == CellType.STRING) {
                } else {
                    // Copy rows from source worksheet into destination worksheet
                    SXSSFRow descRow = destSheet.createRow(rownum);
                    int index = 0;
                    for (Cell cell : srcRow) {
                        SXSSFCell destCell = descRow.createCell(index++, cell.getCellType());
                        switch (cell.getCellType()) {
                            case NUMERIC -> destCell.setCellValue(cell.getNumericCellValue());
                            case STRING -> destCell.setCellValue(cell.getStringCellValue());

                // When a max number of rows copied are reached, or when we are at the end of worksheet, 
                // write data into a new file 
                if (rownum == MAX_ROWS_PER_FILE || srcRow.getRowNum() == physicalNumberOfRows - 1) {
                    rownum = -1;
                    String output = String.format("split-%03d.xlsx", splitCounter++);
                    System.out.println("Writing " + output);
                    try (OutputStream os = new FileOutputStream(output)) {
                    } catch (IOException e){

                rownum = rownum + 1;

            // Display processing time
            LocalDateTime endTime =;
            long minutes = startTime.until(endTime, ChronoUnit.MINUTES);
            startTime = startTime.plusMinutes(minutes);
            long seconds = startTime.until(endTime, ChronoUnit.SECONDS);
            System.out.printf("Splitting finished in %d minutes and %d seconds %n", minutes, seconds);
        } catch (Exception e) {

The output of running this program will look like this:

Writing split-000.xlsx
Writing split-001.xlsx
Writing split-002.xlsx
Writing split-003.xlsx
Writing split-004.xlsx
Writing split-005.xlsx
Writing split-006.xlsx
Writing split-007.xlsx
Writing split-008.xlsx
Writing split-009.xlsx
Splitting finished in 0 minutes and 8 seconds 

Maven Dependencies


Maven Central Maven Central

How do I get the number of processors available to the JVM?

The Runtime.getRuntime().availableProcessors() method returns the maximum number of processors available to the Java virtual machine, the value will never be smaller than one. Knowing the number of available processor you can use it for example to limit the number of thread in your application when you are writing a multi-thread code.

package org.kodejava.lang;

public class NumberProcessorExample {
    public static void main(String[] args) {
        final int processors = Runtime.getRuntime().availableProcessors();
        System.out.println("Number of processors = " + processors);

Running the code snippet give you something like:

Number of processors = 8

How do I show Spring transaction in log / console?

When you use the Spring framework @Transactional annotation in your service layer you might want to see what is happening in your code related to database transaction. You want to see when a transaction is started, when it is committed or rollbacked.

To activate the log for transactional message you can add the following configurations in your application properties file. For example when using the JpaTransactionManager you can set the log level to DEBUG.


Running the spring boot application with these configuration, the JpaTransactionManager will write something line these on your log file or console:

2023-03-29T23:06:52.194+08:00 DEBUG 54056 --- [nio-9090-exec-3] o.s.orm.jpa.JpaTransactionManager        : Creating new transaction with name [org.kodejava.webapp.accounting.service.impl.CalculationServiceImpl.recalculate]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2023-03-29T23:06:52.194+08:00 DEBUG 54056 --- [nio-9090-exec-3] o.s.orm.jpa.JpaTransactionManager        : Opened new EntityManager [SessionImpl(974784570<open>)] for JPA transaction
2023-03-29T23:06:52.195+08:00 DEBUG 54056 --- [nio-9090-exec-3] o.s.orm.jpa.JpaTransactionManager        : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@4150907e]
2023-03-29T23:06:52.195+08:00 DEBUG 54056 --- [nio-9090-exec-3] o.s.orm.jpa.JpaTransactionManager        : Found thread-bound EntityManager [SessionImpl(974784570<open>)] for JPA transaction
2023-03-29T23:06:52.195+08:00 DEBUG 54056 --- [nio-9090-exec-3] o.s.orm.jpa.JpaTransactionManager        : Participating in existing transaction
2023-03-29T23:06:52.237+08:00 DEBUG 54056 --- [nio-9090-exec-3] o.s.orm.jpa.JpaTransactionManager        : Initiating transaction commit
2023-03-29T23:06:52.237+08:00 DEBUG 54056 --- [nio-9090-exec-3] o.s.orm.jpa.JpaTransactionManager        : Committing JPA transaction on EntityManager [SessionImpl(974784570<open>)]
2023-03-29T23:06:52.237+08:00 DEBUG 54056 --- [nio-9090-exec-3] o.s.orm.jpa.JpaTransactionManager        : Closing JPA EntityManager [SessionImpl(974784570<open>)] after transaction

How do I build simple search page using ZK and Spring Boot?

In this example we are going to build a simple search page using ZK framework and Spring Boot. We are going to use the latest available version of Spring Boot (3.0.0) and ZK Framework ( So without taking more time let’s start by creating a new spring boot project with the following pom.xml. You can create the initial project using your IDE or spring initializr.

Create a Spring Boot project and add the following dependencies:

  • zkspringboot-starter
  • zkplus
  • spring-boot-devtools
  • spring-boot-starter-data-jpa
  • mysql-connector-j
  • lombok

The pom.xml File

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="" xmlns:xsi=""
        <relativePath/> <!-- lookup parent from repository -->





            <id>ZK CE</id>
            <name>ZK CE Repository</name>
            <id>ZK EVAL</id>
            <name>ZK Evaluation Repository</name>

</project> File

This properties file configure ZK application homepage and the prefix where the zul files are located. We also configure the datasource to our application database.


spring.datasource.password= Entity Definition

An entity that represent out record label with just two property of id and name. Getters and setters are generated by Lombok library, it also generated to equals() and hashcode() method, and also the toString() method.

package org.kodejava.zk.entity;

import jakarta.persistence.*;
import lombok.Data;

public class Label {
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

The definition.

Create the LabelRepository which extends the JpaRepository and JpaSpecificationExecutor interfaces.

package org.kodejava.zk.repository;

import org.kodejava.zk.entity.Label;
import org.springframework.stereotype.Repository;

public interface LabelRepository extends JpaRepository<Label, Long>, JpaSpecificationExecutor<Label> {
} a base search controller.

A base class that we can use to implements all the search page in an application. Basically it provides the method to search our application data. It defines a couple of abstract method that need to be implemented by the search controller classes such as what repository to use and the specification for searching the data. We can also define the default sort column and the direction of the data sorting.

package org.kodejava.zk.controller;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zul.Listbox;

public abstract class AbstractSearchController<T> extends SelectorComposer<Component> {
    protected Listbox listbox;

    public abstract JpaSpecificationExecutor<T> getRepository();

    public abstract Specification<T> getSpecification();

    public abstract String getCacheKey();

    protected String getDefaultSortColumn() {
        return "id";

    protected Sort.Direction getDefaultSortDirection() {
        return Sort.Direction.ASC;

    protected boolean getMultiple() {
        return false;

    public void doAfterCompose(Component comp) throws Exception {

    public void search() {
        SearchListModel<T> model = new SearchListModel<>(getRepository(), getSpecification(), getCacheKey());

    public void onEnterPressed(Event event) {

    public int getPageSize() {
        return SearchListModel.PAGE_SIZE;

An implementation of ListModel, this class will query the database using the provided repository and specification. It read data page-by-page and cache it so when we navigating the Listbox page it doesn’t read the data that have already been cached.

package org.kodejava.zk.controller;

import org.zkoss.zk.ui.Execution;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zul.FieldComparator;
import org.zkoss.zul.ListModelList;

import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;

public class SearchListModel<T> extends ListModelList<T> {
    public static final int PAGE_SIZE = 5;
    private final JpaSpecificationExecutor<T> repository;
    private final String cacheKey;
    private long totalElements;

    private Comparator<T> comparator;
    private boolean ascending = false;
    private final Specification<T> specification;
    private Sort.Direction defaultSortDirection = Sort.Direction.ASC;
    private String defaultSortColumn = "id";

    public SearchListModel(JpaSpecificationExecutor<T> repository, Specification<T> specification, String cacheKey) {
        this.repository = repository;
        this.specification = specification;
        this.cacheKey = cacheKey;
        this.totalElements = repository.count(specification);

    public T getElementAt(int index) {
        Map<Integer, T> cache = getCache();

        T target = cache.get(index);
        if (target == null) {
            Sort sort =, getDefaultSortColumn());
            if (comparator != null) {
                FieldComparator fieldComparator = (FieldComparator) comparator;
                String orderBy = fieldComparator.getRawOrderBy();
                sort = ? Sort.Direction.ASC : Sort.Direction.DESC, orderBy);
            Page<T> pageResult = repository.findAll(specification, PageRequest.of(getPage(index), PAGE_SIZE, sort));
            totalElements = pageResult.getTotalElements();
            int indexKey = index;
            for (T t : pageResult.toList()) {
                cache.put(indexKey, t);
        } else {
            return target;

        target = cache.get(index);
        if (target == null) {
            throw new RuntimeException("element at " + index + " cannot be found in the database.");
        } else {
            return target;

    public int getSize() {
        return (int) totalElements;

    public void sort(Comparator<T> comparator, boolean ascending) {
        super.sort(comparator, ascending);
        this.comparator = comparator;
        this.ascending = ascending;

    private Map<Integer, T> getCache() {
        Execution execution = Executions.getCurrent();
        Map<Integer, T> cache = (Map<Integer, T>) execution.getAttribute(cacheKey);
        if (cache == null) {
            cache = new HashMap<>();
            execution.setAttribute(cacheKey, cache);
        return cache;

    private int getPage(int index) {
        if (index != 0) {
            return index / PAGE_SIZE;
        return index;

    public Sort.Direction getDefaultSortDirection() {
        return defaultSortDirection;

    public void setDefaultSortDirection(Sort.Direction defaultSortDirection) {
        this.defaultSortDirection = defaultSortDirection;

    public String getDefaultSortColumn() {
        return defaultSortColumn;

    public void setDefaultSortColumn(String defaultSortColumn) {
        this.defaultSortColumn = defaultSortColumn;

Our label search page controller which extends from AbstractSearchController class. We provide the LabelRepository and the Specification to filter the data.

package org.kodejava.zk.controller;

import jakarta.persistence.criteria.Predicate;
import org.kodejava.zk.entity.Label;
import org.kodejava.zk.repository.LabelRepository;
import org.zkoss.zul.Textbox;

import java.util.ArrayList;
import java.util.List;

public class LabelSearchController extends AbstractSearchController<Label> {
    private LabelRepository labelRepository;

    private Textbox labelNameTextbox;

    public JpaSpecificationExecutor<Label> getRepository() {
        return labelRepository;

    public Specification<Label> getSpecification() {
        return (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            String labelName = labelNameTextbox.getValue();
            if (!labelName.isBlank()) {
                predicates.add("name"), "%" + labelName + "%"));
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));

    public String getCacheKey() {
        return "LABEL_CACHE_KEY";

label-search.zul – Label Search Page in ZUL

The search page with a label name Textfield to search by label name. The Listbox will display the data with pagination.

    <window id="labelSearchWin" zclass="none" border="none" visible="true"
            apply="org.kodejava.zk.controller.LabelSearchController" width="100%">

        <grid id="searchForm">
                <column width="200px"/>
                    <label value="Label Name"/>
                    <textbox id="labelNameTextbox" width="450px" maxlength="50"/>


            <button id="searchButton" label="Search"/>


            <listbox id="listbox" mold="paging" pageSize="${labelSearchWin$composer.pageSize}" visible="false">
                    <listheader label="No" hflex="min"/>
                    <listheader label="Label Name" sort="auto(name)"/>
                    <listheader label="Action" hflex="min"/>
                <template name="model">
                        <listcell label="${forEachStatus.index + 1}" hflex="min"/>
                        <listcell label="${}"/>
                        <listcell hflex="min">
                                <button label="Edit" forward="onClick=listbox.onEdit(${each})" tooltiptext="Edit Data"/>

Running the application and access it at localhost:8080 will give you a screen like the screenshot at the beginning of this post.

The complete source code can be found in the following GitHub repository kodejava-zk-search.

10 Best Career Options for Java Developers in 2023


The programming world is changing, and opportunities await aspiring coders. With robust support from major technology giants and an ever-expanding job market, now is the time to start your career as a Java developer.

Whether you’re a professional looking to break into the field or a beginner with no experience who wants to get a java developer job with no experience, we’ve rounded up ten exciting options that are sure to put you on your way to success. From mobile app development to software engineering, this article explores the best career options for you in 2023!

Will Java Developers Still Be in Demand in 2023?

If the ever-increasing number of job posts—specifically for Java developers at Jooble is anything to go by, Java developers will have no shortage of job offers in 2023. Also, as an open-source language, Java is widely used for enterprise development, mobile applications, web applications, and artificial intelligence projects.

In addition, its inventor – Oracle –has regularly added new features to the language over the last two decades. This has allowed Java developers to stay ahead of the curve when it comes to coding skills.

Moreover, as technology advances, more organizations are turning to Java developers to meet their needs. For example, many businesses are transitioning their entire infrastructure from monolithic services and adopting microservices architectures.

This trend requires skilled Java developers who understand how microservices work and can develop them effectively. The same goes for AI-driven systems that rely heavily on Java development expertise.

All things considered, it means there will still be plenty of demand for talented Java developers in 2023 and beyond. Here are ten of the best career options for Java developers in 2023:

1. DevOps Engineer

A DevOps Engineer is a software engineer who works collaboratively with development and operations teams. They ensure that digital projects are delivered quickly, efficiently, and without errors.

They are responsible for deploying software, maintaining application stability, and automating workflows. Essentially, they are the bridge between development and operations teams, ensuring communication flows within an organization.

Becoming a DevOps Engineer requires knowledge of programming languages like Java, and scripting languages like Ruby or Python. It also requires an understanding of automation tools like Jenkins or Chef; source code management platforms like Git, and cloud-based solutions such as Amazon Web Services or Microsoft Azure.

Becoming a DevOps Engineer could be a great choice for Java developers looking for the best career options in 2023. This is because there is an increasing demand for this role in organizations worldwide.

2. Artificial Intelligence Engineer

Artificial Intelligence (AI) is a rapidly growing field. As a result, companies are looking for developers with expertise in AI technologies to help them stay competitive.

AI Engineers are responsible for creating intelligent systems that can perform tasks independently and operate with human-like accuracy and speed. Becoming an AI Engineer requires knowledge of Java, Python, R programming language, Natural Language Processing (NLP), Machine Learning algorithms, and more. For Java developers in 2023, this could be a great option, as AI technologies will only increase in the coming years.

3. Solution Architect

A Solution Architect is a technical leader who designs and implements software solutions to solve business problems. They create high-level designs that integrate various systems, applications, and services, maximizing efficiency and scalability.

To become one, you’ll need to understand software engineering fundamentals such as object-oriented programming (OOP) and system design concepts like architecture patterns and frameworks. Additionally, you’ll need to be well-versed in the latest technologies and trends in the industry, such as Kubernetes and microservices.

To become a Solution Architect, the best approach is to start by gaining experience with coding; then pursue additional training focused on system architecture principles and design patterns. Having some relevant certifications will also give you an edge over other candidates applying for positions.

4. Data Scientist

Data Scientists manage, analyze, and interpret data to extract valuable insights. They use various technologies and methods such as big data analytics, machine learning algorithms, natural language processing (NLP), statistical analysis, etc.

Becoming a successful Data Scientist requires good knowledge of programming languages like Java along with strong mathematical and statistical skills. You’ll also need to be well-versed in databases, machine learning tools, data visualization frameworks, and modern algorithms such as deep learning neural networks. Data Scientists are in high demand these days due to organizations’ ever-increasing use of data-driven decisions.

5. Scrum Master

A Scrum Master is a role that is essential for software development teams that employ agile methodology. A Scrum Master is responsible for organizing and managing a team’s workflow during the phases of development.

They’re responsible for ensuring that tasks and deadlines are met. They also facilitate conversations around potential roadblocks or issues.

For Java developers looking to become a Scrum Master in 2023, it is essential to understand the fundamentals of agile software development, such as its core tenets and techniques, before attempting to take on the role. Being fluent with related frameworks, like Extreme Programming and Lean Development, can also be beneficial. It would also help to have good communication and interpersonal skills and knowledge of your team members’ strengths and weaknesses to manage their assigned tasks effectively.

6. Mobile Developer

Mobile Developers specialize in creating applications for mobile devices such as smartphones and tablets. They use coding languages like Java, HTML5, and CSS3 to create user interfaces and write code that interacts with an application’s backend systems.

In 2023, the demand for Mobile Developers is projected to increase exponentially. Mobile applications will become increasingly crucial for businesses as more organizations move their processes and services online.

To become a successful Mobile Developer in 2023, you’ll need to understand software development and mobile design basics. Additionally, understanding modern trends such as augmented reality (AR) can also be beneficial.

7. Project Manager

Project Managers are responsible for overseeing the development process from start to finish. They work closely with teams of software developers, UX/UI designers, marketers, and other professionals to ensure that each project is completed on time and within budget.

Project managers need strong communication skills and an understanding of software development processes. They should be familiar with popular project management tools like JIRA and Trello.

8. Database Administrator

Database Administrators are responsible for creating, maintaining, and securing databases. They use programming languages like Java to develop database applications.

In 2023, Database Administrators will need to be experts in a wide range of technologies such as SQL, NoSQL, OLTP/OLAP systems, and cloud computing platforms. Additionally, knowledge of related areas such as machine learning and big data can also be beneficial.

9. Security Analyst

Security Analysts are responsible for protecting networks and systems from cyber threats such as malware, viruses, and unauthorized access. They use programming languages like Java to develop security protocols, monitor networks for suspicious activities, investigate incidents and analyze system vulnerabilities.

To succeed in this role in 2023, Security Analysts need to understand cyber-security best practices thoroughly. They should also be knowledgeable about the latest threats and technologies, such as biometrics, artificial intelligence (AI), and blockchain.

10. Cloud Solutions Architect

Cloud Solutions Architects design and implement cloud-based systems for businesses. They need to understand the fundamentals of cloud computing, including infrastructure as a service (IaaS), platform as a service (PaaS), and software as a service (SaaS).

In 2023, Cloud Solutions Architects should have experience with popular cloud computing providers such as Amazon Web Services (AWS), Microsoft Azure, and Google Cloud Platform. Additionally, they should know the principles of DevOps, microservices, and containerization.

In summation, these are the ten best career options for Java Developers in 2023. With a good understanding of software development principles and the latest technologies, you can pursue any of these roles and excel in them. Good luck!

How do I calculate days between two dates excluding weekends and holidays?

The code snippet below shows you a simple way to calculate days between two dates excluding weekends and holidays. As an example, you can use this function for calculating work days. The snippet utilize the java.time API and the Stream API to calculate the value.

What we do in the code below can be described as the following:

  • Create a list of holidays. The dates might be read from a database or a file.
  • Define filter Predicate for holidays.
  • Define filter Predicate for weekends.
  • These predicates will be use for filtering the days between two dates.
  • Define the startDate and the endDate to be calculated.
  • Using Stream.iterate() we iterate the dates, filter it based on the defined predicates.
  • Finally, we get the result as list.
  • The actual days between is the size of the list, workDays.size().
package org.kodejava.datetime;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

public class DaysBetweenDates {
    public static void main(String[] args) {
        List<LocalDate> holidays = new ArrayList<>();
        holidays.add(LocalDate.of(2022, Month.DECEMBER, 26));
        holidays.add(LocalDate.of(2023, Month.JANUARY, 2));

        Predicate<LocalDate> isHoliday = holidays::contains;
        Predicate<LocalDate> isWeekend = date -> date.getDayOfWeek() == DayOfWeek.SATURDAY
                || date.getDayOfWeek() == DayOfWeek.SUNDAY;

        LocalDate startDate = LocalDate.of(2022, Month.DECEMBER, 23);
        LocalDate endDate = LocalDate.of(2023, Month.JANUARY, 3);
        System.out.println("Start date = " + startDate);
        System.out.println("End date   = " + endDate);

        // Days between startDate inclusive and endDate exclusive
        long daysBetween = ChronoUnit.DAYS.between(startDate, endDate);
        System.out.println("Days between = " + daysBetween);

        List<LocalDate> workDays = Stream.iterate(startDate, date -> date.plusDays(1))

        long actualDaysBetween = workDays.size();
        System.out.println("Actual days between = " + actualDaysBetween);

Running the code snippet above give us the following result:

Start date = 2022-12-23
End date   = 2023-01-03
Days between = 11
Actual days between = 5

How do I discover the quarter of a given date?

The following code snippet shows you a various way to get the quarter of a given date. Some methods that we use below are:

  • Using the new java.time API of Java 8 IsoFields.QUARTER_OF_YEAR.
  • Using Java 8 DateTimeFormatter pattern of Q or q. The length of “q” give us a different result.
  • Using java.util.Date.
  • Using java.util.Calendar.
  • Get the quarter from an array of string.

Let’s see the code snippet in action.

package org.kodejava.datetime;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.IsoFields;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

public class DateQuarter {
    public static void main(String[] args) {
        // Using Java 8
        LocalDate now =;
        int quarter = now.get(IsoFields.QUARTER_OF_YEAR);
        System.out.println("quarter  = " + quarter);

        // Using DateTimeFormatter Q / q, set the Locale to get value
        // in local format
        String quarter1 = LocalDate.of(2023, 8, 17)
                .format(DateTimeFormatter.ofPattern("q", Locale.US));
        String quarter2 = LocalDate.of(2023, 8, 17)
                .format(DateTimeFormatter.ofPattern("qq", Locale.US));
        String quarter3 = LocalDate.of(2023, 8, 17)
                .format(DateTimeFormatter.ofPattern("qqq", Locale.US));
        String quarter4 = LocalDate.of(2023, 8, 17)
                .format(DateTimeFormatter.ofPattern("qqqq", Locale.US));
        System.out.println("quarter1 = " + quarter1);
        System.out.println("quarter2 = " + quarter2);
        System.out.println("quarter3 = " + quarter3);
        System.out.println("quarter4 = " + quarter4);

        // Using older version of Java
        Date today = new Date();
        quarter = (today.getMonth() / 3) + 1;
        System.out.println("quarter = " + quarter);

        // Using java.util.Calendar object. For certain date
        // we can set the calendar date using setTime() method.
        Calendar calendar = Calendar.getInstance();
        quarter = (calendar.get(Calendar.MONTH) / 3) + 1;
        System.out.println("quarter = " + quarter);

        // Custom the quarter as text
        String[] quarters = new String[]{"Q1", "Q2", "Q3", "Q4"};
        String quarterString = quarters[quarter - 1];
        System.out.println("quarterString = " + quarterString);

And here are the result of the code snippet above:

quarter  = 1
quarter1 = 3
quarter2 = 03
quarter3 = Q3
quarter4 = 3rd quarter
quarter = 1
quarter = 1
quarterString = Q1