{"id":19327823,"url":"https://github.com/kenshoo/persistence-layer","last_synced_at":"2025-04-22T21:30:32.609Z","repository":{"id":39711313,"uuid":"312227526","full_name":"kenshoo/persistence-layer","owner":"kenshoo","description":"Command-based bulk library for Java+MySQL with Business Layer support","archived":false,"fork":false,"pushed_at":"2025-03-13T17:33:22.000Z","size":1720,"stargazers_count":3,"open_issues_count":10,"forks_count":2,"subscribers_count":45,"default_branch":"master","last_synced_at":"2025-04-02T04:03:43.399Z","etag":null,"topics":["bulk","flow","framework","jooq","library","mysql","persistence","persistence-layer","public-repo"],"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/kenshoo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"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":"2020-11-12T09:27:28.000Z","updated_at":"2025-03-13T17:33:24.000Z","dependencies_parsed_at":"2025-03-13T14:41:51.814Z","dependency_job_id":null,"html_url":"https://github.com/kenshoo/persistence-layer","commit_stats":{"total_commits":129,"total_committers":14,"mean_commits":9.214285714285714,"dds":0.6666666666666667,"last_synced_commit":"0b9e5cecaf9e4cb929deb5229efcb9cacf9a0df2"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kenshoo%2Fpersistence-layer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kenshoo%2Fpersistence-layer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kenshoo%2Fpersistence-layer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kenshoo%2Fpersistence-layer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kenshoo","download_url":"https://codeload.github.com/kenshoo/persistence-layer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250328095,"owners_count":21412558,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["bulk","flow","framework","jooq","library","mysql","persistence","persistence-layer","public-repo"],"created_at":"2024-11-10T02:18:54.924Z","updated_at":"2025-04-22T21:30:31.810Z","avatar_url":"https://github.com/kenshoo.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cimg src=\"https://user-images.githubusercontent.com/10692534/99590710-6352da80-29f6-11eb-88bb-caaa3d87e0ee.png\" width=\"150\" align=\"right\" /\u003e \n\n## High performance Java/MySQL library built on JOOQ\n\nIs Hibernate too slow for you?\n\nDo you need high performance for bulk operations?\n\nDo you have a flow of business rules to run upon persistence?\n\nIf so, then PL may be the library you need.\n\nPL is a Java mutation layer for business entities where you can define a flow to be executed upon every mutation.\nThe flow can contain enrichments and validations.\nPL was designed to be fast for bulk operations and do every SQL operation in bulks, including fetching of all fields required by the flow.\n\n## How fast is it\n\nAt Kenshoo, after migrating our heavy bulk operations to PL, we got a performance boost factor of at least 2, and even up to 50 in some cases.\n\n## Why is it faster\n\nPersisting in bulks is always faster than persisting one entity at a time within a loop.  \nSo first of all, working in bulks with Hibernate is kind of a hidden feature. It exists, but few developers are aware of it.  \nThe PL interface, on the other hand, only accepts collections of commands to encourage you to work effectively.  \nBut let's suppose you are a \"performance driven\" developer and you do know how to pre-fetch a bulk of entities using Hibernate and now you want to validate the changes and persist them.  \nEach validator may require a different set of fields to fetch.  \n**Hibernate could be either eager or lazy. Neither of them is good.** Being eager may fetch too much, but being lazy is much worse as it shall query the DB multiple times within the loop.  \nPL precalculates the fields required by the flow components and fetches exactly what is needed in a single query.  \nIt also precalculates which validators are really required by your commands and there are more optimizations along the way. Oh, and it does not use reflection.\n\n## Compatibility\n* Java 11 or greater (version **0.1.52** or greater)\n* Java 8 (up to version **0.1.51**)\n* MySQL Database (5.6 or greater)\n* PL depends on [JOOQ](https://www.jooq.org). Each PL release version specifies the JOOQ version it was built with. E.g. release \"0.1.40-jooq-3.10.4\" was built with JOOQ 3.10.4. If you need a very specific JOOQ version, we can add it to our build process.\n\n## [Try it Online](https://repl.it/@GalKoren2/PersistenceLayer#Main.java)\n\nThe above link is a **repl.it** online project where you can edit and execute code samples by only using your browser.\n\n## Code Samples\n\n### Retrieve auto generated ID\nAfter defining and wiring the required infra (as described in the [wiki](https://github.com/kenshoo/persistence-layer/wiki/Setting-up-an-Entity-Persistence)), you can execute PL commands as follows:\n```java\nvar cmd = new CreateCampaignCommand();\n\ncmd.set(Campaign.NAME, \"bla bla\");\ncmd.set(Campaign.DAILY_BUDGET, 150);\ncmd.set(Campaign.STATUS, Status.ToPause);\n\nvar results = campaignPersistence.update(asList(cmd));\nvar ids = seq(results).map(r -\u003e r.getIdentifier().get(Campaign.ID)).toList();\n```\n\n### Update the list of cities of the campaign (one to many relation)\n\nWe want to completely replace the existing cities of our campaign:\n\n```java\nvar campaignCmd = new UpdateCampaignCommand(someId);\n\ncampaignCmd.addChild(new UpsertCityCommand(\"London\"));\ncampaignCmd.addChild(new UpsertCityCommand(\"Tel Aviv\"));\ncampaignCmd.addChild(new UpsertCityCommand(\"Miami\"));\ncampaignCmd.add(new DeletionOfOther(CityEntity.INSTANCE));\n\ncampaignPersistence.update(asList(campaignCmd));\n```\n* The campaign ID is automatically populated into the campaign_cities table.\n* All other cities of this campaign are deleted from the DB.\n\n### Define a Simple Validator\n\n```java\nclass CampaignBudgetValidator implements FieldValidator\u003cCampaign, Integer\u003e {\n\n    @Override\n    public EntityField\u003cCampaign, Integer\u003e validatedField() {\n        return Campaign.BUDGET;\n    }\n\n    @Override\n    public ValidationError validate(Integer newBudgetValue) {\n        return newBudgetValue \u003e 5000\n            ? new ValidationError(\"budget is too big\", Campaign.BUDGET)\n            : null;\n        }\n    }\n}\n```\nSee the wiki for more details:\n* How to add validators to the flow.\n* How to define more complex validators.\n\n### Entity Definition and Annotations\n\n```java\npublic class Campaign extends AbstractEntityType\u003cCampaign\u003e {\n\n    public static final Campaign INSTANCE = new Campaign();\n\n    private Campaign() { super(\"campaigns\"); }\n\n    @Override\n    public DataTable getPrimaryTable() { return CampaignJooq.TABLE; }\n    \n    @Immutable\n    public static final EntityField\u003cCampaign, Integer\u003e ID = INSTANCE.field(CampaignJooq.TABLE.id.identity(true));\n\n    @Required\n    public static final EntityField\u003cCampaign, Integer\u003e BUDGET = INSTANCE.field(CampaignJooq.TABLE.budget);\n    \n    @Required(RELATION)\n    public static final EntityField\u003cCampaign, Integer\u003e ACCOUNT_ID = INSTANCE.field(CampaignJooq.TABLE.account_id);\n    \n    public static final EntityField\u003cCampaign, Integer\u003e BIDDING_STRATEGY = INSTANCE.field(CampaignBiddingStrategy.TABLE.strategy_type);\n}\n```\n\nThere are multiple features in this examples:\n* ID field is auto incremented by the database (by JOOQ expression ```identity.true()```).\n* ID field cannot be changed by an UPDATE command.\n* BUDGET field must be provided upon creation.\n* ACCOUNT_ID must be provided upon creation and the referenced Account entity must exist in DB.\n* BIDDING_STRATEGY is a field from another JOOQ table (usages of the campaign entity should not care about it). This requires that the \"secondary\" table refer to the campaign table by a one-to-one relation.\n\n## [The Wiki](https://github.com/kenshoo/persistence-layer/wiki)\n\nThere you can find a full tutorial with theory and examples.\n\n## Download\n\nFrom maven\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.kenshoo\u003c/groupId\u003e\n    \u003cartifactId\u003epersistence-layer\u003c/artifactId\u003e\n    \u003cversion\u003e0.1.51-jooq-3.10.4\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n## Licensing\n\nPL is licensed under the Apache License, Version 2.0.\n\n## Credits\n\nPL was originally created at Kenshoo by Victor Bronstein.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkenshoo%2Fpersistence-layer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkenshoo%2Fpersistence-layer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkenshoo%2Fpersistence-layer/lists"}