Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Use Eclipse as the IDE, EclipseLink as the JPA provider, MySQL Connector/J as the JDBC driver, and Jakarta Persistence for the API. This Java SE example creates a Maven project, stores JDBC settings programmatically, keeps the persistence-unit declaration in META-INF/persistence.xml, and demonstrates insert, query, update, delete, transaction rollback, and resource cleanup.

This guide uses the modern jakarta.persistence namespace. Do not mix it with older javax.persistence dependencies, imports, or XML.

What each technology does

Technology Role
Jakarta Persistence (formerly JPA) Standard API and object-relational mapping model for Java persistence. See the Jakarta Persistence specification.
EclipseLink A provider that implements the Jakarta Persistence standard.
Eclipse IDE The development environment. It does not provide a JPA runtime. The Java package includes Maven integration; see the Eclipse IDE for Java Developers package.
MySQL Connector/J The JDBC driver that lets Java communicate with MySQL.
MySQL Server The relational database that stores rows.
EntityManagerFactory An expensive, application-wide factory used to create entity managers.
EntityManager A short-lived unit of work used to persist, find, query, update, and remove entities.

“Java configuration” here means plain Java SE bootstrapping: JDBC properties are supplied to Persistence.createEntityManagerFactory, and transactions are controlled with EntityTransaction. It does not mean Spring’s @Configuration model.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose one compatible Jakarta generation

Jakarta Persistence 3.x and EclipseLink 4.x use jakarta.persistence.*. Older EclipseLink 2.7 applications generally use javax.persistence.*. Align all of these as one set:

  • API dependency and Java imports
  • EclipseLink provider version
  • persistence.xml namespace and schema version
  • Java level and provider-supported runtime

Jakarta Persistence 3.2 is associated with Jakarta EE 11, while 4.0 is listed as under development; treat 4.0 as non-final rather than a stable tutorial target. See the release information.

Prerequisites

  • A supported JDK; this example uses Java 21 as its intended compilation level.
  • Eclipse IDE for Java Developers.
  • Maven, either installed separately or used through Eclipse’s Maven integration.
  • A running MySQL Server, local, containerized, or managed.

The Eclipse project currently advertises Java 26 tooling, but IDE support does not mean every provider, driver, plugin, and application has been tested on Java 26. Keep the JDK version used for this project explicit.

Create the MySQL database

The following script is suitable for a local development database. Use a stronger secret and narrower privileges outside a disposable environment.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
CREATE DATABASE jpa_demo
  CHARACTER SET utf8mb4
  COLLATE utf8mb4_unicode_ci;

CREATE USER 'jpa_user'@'localhost'
  IDENTIFIED BY 'change_this_password';

GRANT ALL PRIVILEGES
  ON jpa_demo.*
  TO 'jpa_user'@'localhost';

USE jpa_demo;

CREATE TABLE users (
    id BIGINT NOT NULL AUTO_INCREMENT,
    name VARCHAR(100) NOT NULL,
    age INT NOT NULL,
    PRIMARY KEY (id)
);

users avoids the ambiguity of using user as a table name, and age is an integer rather than text. In production, grant only the operations the application needs and create tables with a migration tool such as Flyway or Liquibase.

Create the Maven project in Eclipse

  1. Choose File → New → Maven Project.
  2. Select a simple project, then set a group ID such as example and artifact ID jpa-demo.
  3. Set the compiler release to the JDK you will actually run.
  4. Use this layout:
jpa-demo/
├── pom.xml
└── src/
    └── main/
        ├── java/
        │   └── example/
        │       ├── JpaUtil.java
        │       ├── Main.java
        │       └── User.java
        └── resources/
            └── META-INF/
                └── persistence.xml

Add aligned dependencies

Use the current mutually compatible releases documented by the provider and driver at the time you build the project. The important modern coordinates are shown below; pin the exact versions you select rather than copying the obsolete Connector/J 5.1-era coordinate.

<dependencies>
  <dependency>
    <groupId>jakarta.persistence</groupId>
    <artifactId>jakarta.persistence-api</artifactId>
    <version>3.x-compatible-version</version>
  </dependency>

  <dependency>
    <groupId>org.eclipse.persistence</groupId>
    <artifactId>eclipselink</artifactId>
    <version>4.x-compatible-version</version>
  </dependency>

  <dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>current-compatible-version</version>
  </dependency>
</dependencies>

The MySQL artifact is now com.mysql:mysql-connector-j, as documented in the Connector/J Maven installation guide. The old mysql:mysql-connector-java coordinate should not be copied into a new project. Record the exact API, EclipseLink, driver, JDK, Eclipse, and MySQL versions you test together.

Declare the persistence unit

Create src/main/resources/META-INF/persistence.xml. Keeping this file does not contradict Java configuration: the file declares the persistence unit, while Java code supplies environment-specific JDBC values.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<?xml version="1.0" encoding="UTF-8"?>
<persistence
    xmlns="https://jakarta.ee/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        https://jakarta.ee/xml/ns/persistence
        https://jakarta.ee/xml/ns/persistence/persistence_3_1.xsd"
    version="3.1">

    <persistence-unit name="jpaDemo" transaction-type="RESOURCE_LOCAL">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <class>example.User</class>
        <properties>
            <property name="jakarta.persistence.schema-generation.database.action" value="none"/>
            <property name="eclipselink.logging.level" value="INFO"/>
        </properties>
    </persistence-unit>
</persistence>

RESOURCE_LOCAL is appropriate for a standalone Java SE application. The none schema action assumes the SQL table already exists. Never make drop-and-create the default for data you care about.

Map the entity

package example;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 100)
    private String name;

    @Column(nullable = false)
    private int age;

    protected User() {
        // Required by JPA
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Long getId() { return id; }
    public String getName() { return name; }
    public int getAge() { return age; }
    public void setName(String name) { this.name = name; }
    public void setAge(int age) { this.age = age; }
}
  • @Entity makes the class persistent, and @Table selects the table.
  • @Id identifies the primary key; IDENTITY uses MySQL’s auto-increment value.
  • The protected no-argument constructor is required by JPA. Applications can use the public constructor.
  • Annotations on fields select field access.
  • @Column describes mapping and expected constraints; the database remains the final constraint enforcer.

Create one factory and short-lived entity managers

package example;

import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Persistence;

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

public final class JpaUtil {
    private JpaUtil() { }

    private static final EntityManagerFactory EMF = createFactory();

    private static EntityManagerFactory createFactory() {
        Map<String, Object> properties = new HashMap<>();
        properties.put("jakarta.persistence.jdbc.driver", "com.mysql.cj.jdbc.Driver");
        properties.put(
            "jakarta.persistence.jdbc.url",
            "jdbc:mysql://localhost:3306/jpa_demo?useSSL=false&serverTimezone=UTC"
        );
        properties.put(
            "jakarta.persistence.jdbc.user",
            System.getenv().getOrDefault("DB_USER", "jpa_user")
        );
        properties.put(
            "jakarta.persistence.jdbc.password",
            System.getenv().getOrDefault("DB_PASSWORD", "change_this_password")
        );
        return Persistence.createEntityManagerFactory("jpaDemo", properties);
    }

    public static EntityManager createEntityManager() {
        return EMF.createEntityManager();
    }

    public static void close() {
        if (EMF.isOpen()) {
            EMF.close();
        }
    }
}

com.mysql.cj.jdbc.Driver is the modern Connector/J driver class. The URL’s host, port, database, timezone, SSL, and TLS behavior depend on your server and driver. Disabling SSL can simplify local development; configure certificate validation and a trust store for production.

Create the factory once and reuse it. Create an EntityManager for a unit of work, never share one between threads, and close it when finished. The fallback password above is for a local exercise only; environment variables are safer than committed source code.

Persist, read, update, and delete data

package example;

import jakarta.persistence.EntityManager;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        EntityManager entityManager = JpaUtil.createEntityManager();
        Long id;

        try {
            entityManager.getTransaction().begin();
            User user = new User("Ada", 36);
            entityManager.persist(user);
            entityManager.getTransaction().commit();
            id = user.getId();
            System.out.println("Saved user ID: " + id);

            User found = entityManager.find(User.class, id);
            if (found != null) {
                entityManager.getTransaction().begin();
                found.setAge(37);
                entityManager.getTransaction().commit();
            }

            List<User> users = entityManager
                .createQuery("SELECT u FROM User u ORDER BY u.id", User.class)
                .getResultList();
            users.forEach(u -> System.out.println(u.getId() + ": " + u.getName()));

            entityManager.getTransaction().begin();
            User removable = entityManager.find(User.class, id);
            if (removable != null) {
                entityManager.remove(removable);
            }
            entityManager.getTransaction().commit();
        } catch (RuntimeException exception) {
            if (entityManager.getTransaction().isActive()) {
                entityManager.getTransaction().rollback();
            }
            throw exception;
        } finally {
            entityManager.close();
            JpaUtil.close();
        }
    }
}

Every write occurs inside an active transaction. If an exception interrupts a transaction, roll it back before propagating the error. JPQL uses the entity name and Java attributes—User, u.id, and u.name—not the SQL table and column names.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Build, run, and verify

  1. Set credentials in the shell. For POSIX shells: export DB_USER=jpa_user and export DB_PASSWORD='your-password'. In PowerShell: $env:DB_USER = "jpa_user" and $env:DB_PASSWORD = "your-password".
  2. From the project directory run mvn clean compile.
  3. Run mvn exec:java -Dexec.mainClass=example.Main, or run Main from Eclipse after Maven resolves dependencies.
  4. Verify rows in MySQL:
SELECT id, name, age
FROM users
ORDER BY id;

If you delete the sample row as shown, the final query will be empty; remove the delete block when you want to inspect the inserted record.

Troubleshoot common failures

No Persistence provider for EntityManager named jpaDemo

  • Confirm the file is exactly src/main/resources/META-INF/persistence.xml.
  • Ensure the unit name exactly matches jpaDemo.
  • Check that Maven copied the file to target/classes/META-INF.
  • Verify EclipseLink is on the runtime classpath and matches the Jakarta namespace.

javax/jakarta compilation or runtime errors

Choose one generation, then align every import, API dependency, provider, XML namespace, schema version, and property prefix. An entity importing jakarta.persistence.Entity cannot run with only a javax.persistence API.

JDBC connection errors

  • Check that MySQL is running and listening on the configured host and port.
  • Confirm that jpa_demo exists and that jpa_user has access from the connecting host.
  • Check the password, firewall, container networking, and database name in the URL.
  • Ensure Connector/J is present at runtime, not only available to the compiler.

Transaction and lifecycle errors

  • Call begin() before persist, update, or remove.
  • Call commit(); rollback active transactions in error handling.
  • Do not reuse a closed entity manager or share one across threads.
  • Do not create an entity-manager factory for each record or request.

Boundaries for production use

  • Use Flyway, Liquibase, or another controlled migration process instead of destructive automatic schema generation.
  • Use a connection pool in a server application; this small Java SE example is not a pooling strategy.
  • Store secrets in environment variables or a secrets manager, not source control.
  • Configure TLS and certificate validation rather than relying on local-development URL shortcuts.
  • Design entity equality, lazy loading, detached-object handling, and fetch plans deliberately; careless mappings can cause N+1 queries or lazy-loading failures.
  • Keep provider-specific EclipseLink properties labeled as non-portable. The EclipseLink documentation and its JPA extensions reference describe those options.

When another approach is better

Option Use it when Trade-off
Hibernate You need its large ecosystem or existing integrations. Provider-specific behavior differs; do not mix Hibernate settings into an EclipseLink tutorial.
Spring Java configuration You want dependency injection, a managed data source, JpaTransactionManager, and repository beans. It is a different lifecycle from this application-managed Java SE example.
Spring Boot with Spring Data JPA You want convention-based configuration and repositories. It hides much of the standard bootstrap this tutorial is intended to teach; see Spring Boot.
JDBC or jOOQ You need precise SQL and ORM would add unnecessary complexity. You give up some entity lifecycle and mapping conveniences.

For a standalone Java program, the combination shown here keeps the standard persistence API visible while separating the IDE, provider, driver, and database responsibilities.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.