{"id":44400376,"url":"https://github.com/jerolba/jfleet","last_synced_at":"2026-02-12T06:15:03.821Z","repository":{"id":45214134,"uuid":"101422199","full_name":"jerolba/jfleet","owner":"jerolba","description":"JFleet persist to database large collections of Java POJOs using alternate persistence methods from JDBC","archived":false,"fork":false,"pushed_at":"2024-06-08T16:32:22.000Z","size":1326,"stargazers_count":44,"open_issues_count":0,"forks_count":4,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-06-08T17:52:52.061Z","etag":null,"topics":["batching","load-data","mysql","performance","postgresql"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jerolba.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-08-25T16:28:44.000Z","updated_at":"2024-06-08T16:32:26.000Z","dependencies_parsed_at":"2024-02-18T17:43:20.088Z","dependency_job_id":"d63fe4df-c5de-4911-8efa-ba6d6967b173","html_url":"https://github.com/jerolba/jfleet","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/jerolba/jfleet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jerolba%2Fjfleet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jerolba%2Fjfleet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jerolba%2Fjfleet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jerolba%2Fjfleet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jerolba","download_url":"https://codeload.github.com/jerolba/jfleet/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jerolba%2Fjfleet/sbom","scorecard":{"id":515884,"data":{"date":"2025-08-11","repo":{"name":"github.com/jerolba/jfleet","commit":"a5cc3a683c8b88c59526089417f5aeedb799b4f2"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.5,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":2,"reason":"Found 3/13 approved changesets -- score normalized to 2","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/ci.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:38: update your workflow using https://app.stepsecurity.io/secureworkflow/jerolba/jfleet/ci.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:40: update your workflow using https://app.stepsecurity.io/secureworkflow/jerolba/jfleet/ci.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/jerolba/jfleet/ci.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/jerolba/jfleet/ci.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:26: update your workflow using https://app.stepsecurity.io/secureworkflow/jerolba/jfleet/ci.yml/master?enable=pin","Info:   0 out of   4 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":9,"reason":"binaries present in source code","details":["Warn: binary detected: gradle/wrapper/gradle-wrapper.jar:1"],"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.txt:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE.txt:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 23 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-20T01:48:59.527Z","repository_id":45214134,"created_at":"2025-08-20T01:48:59.527Z","updated_at":"2025-08-20T01:48:59.527Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29360527,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-12T01:03:07.613Z","status":"online","status_checked_at":"2026-02-12T02:00:06.911Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["batching","load-data","mysql","performance","postgresql"],"created_at":"2026-02-12T06:15:02.486Z","updated_at":"2026-02-12T06:15:03.815Z","avatar_url":"https://github.com/jerolba.png","language":"Java","readme":"\n[![Maven Central](https://img.shields.io/maven-central/v/org.jfleet/jfleet.svg)](https://maven-badges.herokuapp.com/maven-central/org.jfleet/jfleet)\n[![Build Status](https://github.com/jerolba/jfleet/actions/workflows/ci.yml/badge.svg)](https://github.com/jerolba/jfleet/actions)\n[![codecov](https://codecov.io/gh/jerolba/jfleet/graph/badge.svg?token=RidwciHvFy)](https://codecov.io/gh/jerolba/jfleet)\n[![License](http://img.shields.io/:license-apache-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0.html)\n\n# JFleet\n\nJFleet is a Java library which persist in database large collections of Java POJOs as fast as possible, using the best available technique in each database provider, achieving it with alternate persistence methods from each JDBC driver implementation.\n\nIts goal is to store a large amount of information in a **single table** using available batch persistence techniques.\n\ndespite being able to use JPA annotations to map Java objects to tables and columns, **JFleet is not an ORM**.\n\n## Table of Contents\n\n- [Supported databases](#supported-databases)\n- [Benchmark](#benchmark)\n- [Usage](#usage)\n- [Dependency](#dependency)\n- [Supported database versions](#supported-database-versions)\n- [Running the tests](#running-the-tests)\n- [Contribute](#contribute)\n- [License](#license)\n\n## Supported databases\n\nEach database provides some technique to insert a bulk of information bypassing standard JDBC commands, but accessible from Java:\n - **MySQL** : Using the [LOAD DATA](https://dev.mysql.com/doc/refman/5.7/en/load-data.html) statement.\n - **PostgreSQL**: Using the [COPY](https://www.postgresql.org/docs/9.6/static/sql-copy.html) command.\n\nIn both cases, and in unsupported databases, you can use the default implementation based on the standard [JDBC executeBatch](https://docs.oracle.com/javase/8/docs/api/java/sql/Statement.html#executeBatch--) statement.\n\n## Benchmark\n\n**JFleet performance is comparable to using the native database import tool, and is between 2.1X and 3.8X faster than using the JDBC driver directly.**\n\n[![mysql vs postgres](https://docs.google.com/spreadsheets/d/e/2PACX-1vTx61C0YNYlczo0S-ZTN56FH2mxvHPHf4jamTnY4wdMwjjF3TvxcW3Ti7VR83dd1R5EznB7xVhD1HD6/pubchart?oid=485493047\u0026format=image)](https://docs.google.com/spreadsheets/d/e/2PACX-1vTx61C0YNYlczo0S-ZTN56FH2mxvHPHf4jamTnY4wdMwjjF3TvxcW3Ti7VR83dd1R5EznB7xVhD1HD6/pubchart?oid=485493047\u0026format=interactive)\n\nYou can find all the benchmarks numbers and results [here](https://github.com/jerolba/jfleet-benchmark#jfleet-benchmark)\n\n\n## Usage\n\nJFleet needs to know how to map your Java objects or entities to a table. JFleet provides two mechanisms to map your objects to a table:\n\n- Using standard [JPA annotations](https://docs.oracle.com/javaee/6/api/javax/persistence/package-summary.html) like [@Entity](https://docs.oracle.com/javaee/6/api/javax/persistence/Entity.html), [@Column](https://docs.oracle.com/javaee/6/api/javax/persistence/Column.html) or [@ManyToOne](https://docs.oracle.com/javaee/6/api/javax/persistence/ManyToOne.html).\n- Mapping manually each column to one object field or with a `Function`\n\n### Using standard JPA annotations\n\nUsing the usual JPA annotations, JFleet extract their information and creates a map between fields and columns:\n\n```java\n\nimport javax.persistence.*;\n\n@Entity\n@Table(name = \"customer_contact\")\npublic class Customer {\n\n    private Long id;\n\n    private String telephone;\n\n    @Column(name=\"customer_name\")\n    private String name;\n\n    @ManyToOne\n    @JoinColumn(name = \"city_id\")\n    private City city;\n\n    @Temporal(TemporalType.DATE)\n    private Date birthDate;\n\n    //Getters and setters\n}\n\n@Entity\n@Table(name = \"city\")\npublic class City {\n\n    @Id\n    private Integer id;\n\n    private String name;\n\n    //Getters and setters\n}\n\n```\n\nLike JPA, JFleet follows the convention of using the field name if no `@Column` name is provided, or the class name if no `@Table` name is provided.\n\nGiven a collection of objects Customer to persist in MySQL with the Load Data technique, you only need to provide a JDBC Connection:\n\n\n```java\n    try (Connection connection = dataSource.getConnection()){\n        Collection\u003cCustomer\u003e customers = buildLargeAmountOfCustomers();\n        BulkInsert\u003cCustomer\u003e bulkInsert = new LoadDataBulkInsert\u003c\u003e(Customer.class);\n        bulkInsert.insertAll(connection, customers);\n    }\n```\n\nIf you are using PostgreSQL the `BulkInsert` implementation is `PgCopyBulkInsert`.\nJFleet prefers Streams to Collections because it does not force you to instantiate all objects in memory, and allows you to create them lazily in some stream process:\n\n```java\n    try (Connection connection = dataSource.getConnection()){\n        Stream\u003cCustomer\u003e customers = createLongStreamOfCustomers();\n        BulkInsert\u003cCustomer\u003e bulkInsert = new PgCopyBulkInsert\u003c\u003e(Customer.class);\n        bulkInsert.insertAll(connection, customers);\n    }\n```\n\nMore information about supported annotations and limitations can be found in the project [wiki page](https://github.com/jerolba/jfleet/wiki/Supported-JPA-annotations)\n\n### Maping manually each column\n\nIf you have any problem using JPA annotations in your domain objects or directly you don't want to add `javax.persistence` dependency to your project, you can configure it manually mapping each column to a field path. If a field has a reference to other object, separate each field name in the path with `.`.\n\nThis mechanism is much more powerfull than JPA, and allows you also to map values that are not present in the object or transform it.\n\nGiven a similar domain object, we need to persist the customer age. The object only have the country name, but not a needed country code:\n\n```java\n\npublic class Customer {\n\n    private Long id;\n    private String telephone;\n    private String name;\n    private City city;\n    private String countryName;\n    private Date birthDate;\n\n    //Getters and setters\n}\n\n```\n\nYou configure JFleet with the mapping info:\n\n```java\n\nDate today = new Date();\n\nEntityInfo customerMap = new EntityInfoBuilder(Customer.class, \"customer_contact\")\n\t.addField(\"id\", \"id\")\n\t.addField(\"telephone\")\n\t.addField(\"name\", \"customer_name\")\n\t.addField(\"city_id\", \"city.id\")\n\t.addColumn(\"country_id\", INT, customer -\u003e mapCountryName2CountryId.get(customer.getCountryName()))\n\t.addColumn(\"age\", INT, custormer -\u003e calculateDifferenceYears(customer.getBirthDate(), today));\n\t.build();\n```\n\nAnd instead of instantiate the BulkInsert with the annotated class, use the created EntityInfo:\n\n```java\n    try (Connection connection = dataSource.getConnection()){\n        Collection\u003cCustomer\u003e customers = buildLargeAmountOfCustomers();\n        BulkInsert\u003cCustomer\u003e bulkInsert = new LoadDataBulkInsert\u003c\u003e(customerMap);\n        bulkInsert.insertAll(connection, customers);\n    }\n```\n\nYou can find more examples on how to map objects in the [example project](https://github.com/jerolba/jfleet/tree/master/jfleet-samples/src/main/java/org/jfleet/samples).\n\n\nIn both cases, transactionality, batch size or error management in MySQL can be configured in the same way. You can find more information in the project [wiki page](https://github.com/jerolba/jfleet/wiki/BulkInsert-configuration).\n\n## Dependency\n\nJFleet is uploaded to Maven Central Repository and to use it, you need to add the following Maven dependency:\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eorg.jfleet\u003c/groupId\u003e\n    \u003cartifactId\u003ejfleet\u003c/artifactId\u003e\n    \u003cversion\u003e0.6.9\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n```\nimplementation 'org.jfleet:jfleet:0.6.9'\n```\n\nYou can always find the latest published version in the [MvnRepository searcher](https://mvnrepository.com/artifact/org.jfleet/jfleet).\n\nBy default JFleet uses basic `javax.persistence` annotations. If you use it, and you don't have any JPA implementation as a dependency in your project, you must add the Javax Persistence API dependency:\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ejavax.persistence\u003c/groupId\u003e\n    \u003cartifactId\u003epersistence-api\u003c/artifactId\u003e\n    \u003cversion\u003e1.0.2\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nApart from `persistence-api` and [SLF4J](https://www.slf4j.org/) for logging, JFleet does not have any dependency.\nJFleet has not been tested against all JDBC driver versions, but it is expected that any modern version will work properly.\n\n\n## Supported database versions\n\nJFleet is configured to execute continuous integration tests against with [GihubActions](https://github.com/jerolba/jfleet/actions/workflows/ci.yml), using the latest stable release of **MySQL 5.7** and  **MySQL 8.0**, and the latest stable release of **PostgreSQL 12.5** and **PostgreSQL 13.1**.\n\n**JFleet is currently running in production against AWS Aurora MySQL and Aurora PostgreSQL**, and has been tested for [benchmarks](https://github.com/jerolba/jfleet-benchmark#jfleet-benchmark) with the Google Cloud managed versions of MySQL and Postgres.\n\nAny database engine with a standard JDBC driver should be used with the `JdbcBulkInsert` implementation.\n\n## Running the tests\n\nThe tests utilize [TestContainers](https://testcontainers.com/) to create lightweight, temporary instances of common databases.\nTestContainers automatically handles downloading the required database images and managing their lifecycle.\n\nDuring test execution, two databases—MySQL and PostgreSQL—are instantiated and will automatically shut down once the tests complete.\nFor details on how these containers are configured, refer to the `DatabaseContainers` class in the `jfleet-core` module.\n\nTo run the tests locally, ensure that Docker or a compatible Docker-API runtime is installed. For more information on configuring TestContainers, please consult the [System Requirements guide](https://java.testcontainers.org/supported_docker_environment/).\n\nTo execute all test you must execute the command:\n\n```bash\n$ .\\gradlew test\n```\n\nBecause gradle can not resolve both drivers version concurrently, by default tests are executed with the MySQL 5.X JDBC driver.\nTo execute it with MySQL 8.0 driver, you must add a modifier to gradle command:\n\n```bash\n$ .\\gradlew test -Pmysql8\n```\n\nYou can also fork the project and test it in your own repository's [Github Actions](https://docs.github.com/en/actions).\n\n## Contribute\nFeel free to dive in! [Open an issue](https://github.com/jerolba/jfleet/issues/new) or submit PRs.\n\nAny contributor and maintainer of this project follows the [Contributor Covenant Code of Conduct](https://github.com/jerolba/jfleet/blob/master/CODE_OF_CONDUCT.md).\n\n## License\n[Apache 2](https://github.com/jerolba/jfleet/blob/master/LICENSE.txt) © Jerónimo López\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjerolba%2Fjfleet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjerolba%2Fjfleet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjerolba%2Fjfleet/lists"}