{"id":28317581,"url":"https://github.com/naynecoder/yorm","last_synced_at":"2025-08-30T10:41:50.586Z","repository":{"id":39615199,"uuid":"470336534","full_name":"naynecoder/yorm","owner":"naynecoder","description":"Simple ORM based on Java Records","archived":false,"fork":false,"pushed_at":"2024-09-08T20:43:26.000Z","size":130,"stargazers_count":36,"open_issues_count":3,"forks_count":3,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-07-25T00:47:23.891Z","etag":null,"topics":["database","java","jdbc","mysql","orm","postgres","snowflake","sql"],"latest_commit_sha":null,"homepage":"https://naynecoder.github.io/yorm/","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/naynecoder.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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,"zenodo":null}},"created_at":"2022-03-15T21:25:20.000Z","updated_at":"2025-06-08T17:07:07.000Z","dependencies_parsed_at":"2024-06-23T04:16:58.022Z","dependency_job_id":"0b50bb37-bba6-4510-b7fc-4ce61cddd31e","html_url":"https://github.com/naynecoder/yorm","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/naynecoder/yorm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/naynecoder%2Fyorm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/naynecoder%2Fyorm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/naynecoder%2Fyorm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/naynecoder%2Fyorm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/naynecoder","download_url":"https://codeload.github.com/naynecoder/yorm/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/naynecoder%2Fyorm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272839668,"owners_count":25001862,"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","status":"online","status_checked_at":"2025-08-30T02:00:09.474Z","response_time":77,"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":["database","java","jdbc","mysql","orm","postgres","snowflake","sql"],"created_at":"2025-05-25T06:12:21.658Z","updated_at":"2025-08-30T10:41:50.537Z","avatar_url":"https://github.com/naynecoder.png","language":"Java","readme":"# Yorm\n## _Yet another ORM_\n\n![Build Status](https://travis-ci.org/joemccann/dillinger.svg?branch=master)\n[![Maven metadata URL](https://img.shields.io/maven-metadata/v?color=blue\u0026logo=apache-maven\u0026metadataUrl=https%3A%2F%2Frepo1.maven.org%2Fmaven2%2Forg%2Fyorm%2Fyorm%2Fmaven-metadata.xml)](https://mvnrepository.com/artifact/org.yorm/yorm)\n![Supported JVM Versions](https://img.shields.io/badge/JVM-17-success.svg?style=flat\u0026logo=Java)\n[![License](https://img.shields.io/github/license/naynecoder/yorm?style=flat\u0026logo=apache\u0026color=success)](https://www.apache.org/licenses/LICENSE-2.0)\n\nYorm is a basic ORM-alike framework designed to work with Java Records, without class generation, neither annotations.\nIn the world of microservices, there is a tendency to have very contained logic within every service,\nand hence reduced databases, that in many cases are simply no more than several tables with not that many fields.\nJava Records usually are a perfect fit for basic CRUD operations, and here is where Yorm shines.\n\n**Yorm** needs at least Java 17, although it works of newer versions as well.\n\n**Yorm** might be for you in case:\n\n- You are working with microservices with Java and like Java Records\n- Your relational databases are pretty simple and basic\n- Your tables contain auto increment ids\n- You don't need complex INNER JOIN queries, just basic CRUD\n\nAlthough the Java industry offers very well maintained ORM solutions like [Hibernate] or [Jooq], they tend not to work that well with Java Records. **Yorm** on the other side is specifically designed to leverage this Java capability.\n\nDue to the immutable nature of Java Records, **Yorm** cannot be understood as a persistent ORM, not even as an ORM, as there aren't really any relationships.\n## Features\n\n- No need to generate classes\n- No need to add annotations\n- No need to write SQL for basic operations\n- Seamless flow with API REST and CRUD operations\n\n**Yorm** doesn't need to generate classes or to annotate them, but it works on conventions. It will assume that your\ntable has a Primary Key called *id*, probably with an autoincrement. Also it will assume that foreign keys will follow the naming patter of *table_id*. The convention will assume as well that fields in the table and fields in the record will have the same name, or a very similar one.\n\nWhen a Java Record is operated with **Yorm**, a reflection inspection will came in, and all the methods of the Record will be matched with their counterparts from the database. This matching will be kept in memory as a map, to avoid using reflection again. However, if there is a change in the database structure, the microservice will probably need to be restarted to refresh this mapping.\n\n## Dependencies\n\n*Yorm* has been designed to need very few dependencies:\n\n- [HikariCP] - Hikari, to deal with the database\n- [MySql] - Database supported and tested\n- [PostgreSql] - Database supported and tested, courtesy of [PabloGrisafi]\n- [Junit 5] - For the unit tests\n- [TestContainers] - Also for the unit tests\n- [Slf4j] - Logging is usually useful\n\nAnd that's it, the **Yorm** lies heavily on Java Records and Reflections.\n\n**Yorm** has been tested with Snowflake, and it works as long as the table\nhas no primary keys, and the Jvm is run with the option *--add-opens java.base/java.nio=ALL-UNNAMED*\n\n## How to use it with examples\n\nImagine you have a database with a table called Person, defined like:\n\n```sql\nCREATE TABLE person\n(\n    id         INT(10) AUTO_INCREMENT PRIMARY KEY NOT NULL,\n    name       VARCHAR(20) NOT NULL,\n    email      VARCHAR(55) NOT NULL,\n    company_id int(10) NOT NULL\n);\n```\n\nHence, you a Java Record that can match that table:\n\n```java\npublic record Person(int id, String name, String email, int companyId) {}\n```\nPlease note how *companyId* follows the traditional camelcase in Java, and *company_id* the underscore which is\nvery popular in the database world. It can be like that, *Yorm* will take care and match both fields.\n\nSaving an object will be something as easy as:\n```java\nPerson person = new Person(0, \"John\", \"john.doe@um.com\", 1);\n    int idPerson = yorm.save(person);\n```\nThis will translate to SQL:\n```sql\nINSERT INTO person (name, email, company_id) VALUES (\"John\", \"john.doe@um.com\", 1)\n```\n\n**Yorm** will detect that the id has a 0, consider it an INSERT, and insert it in the database, in table Person. It will return the id of the object just inserted.\n\nWe might need to insert the object with its id, there is no problem:\n```java\nPerson person = new Person(2, \"Mark\", \"mark.doe@um.com\", 1);\n    int idPerson = yorm.insert(person);\n```\nThis operation will be automatically translated into\n```sql\nINSERT INTO person (id, name, email, company_id) VALUES (2, \"Mark\", \"mark.doe@um.com\", 1)\n```\n\nThe update operation follows the same pattern. Please bear in mind that Records are immutable, so we have to\ncreate a new one, and the id will be used to detect that it's an update operation:\n```java\nPerson person = new Person(2, \"Jacob\", \"jacob.doe@um.com\", 1);\n    yorm.save(person);\n```\nWhose equivalent SQL would be:\n```sql\nUPDATE person SET name = \"Jacob\", email=\"jacob.doe@um.com\" WHERE id=2 AND company_id=1\n```\nWhy is using company_id here? Well, it looks like a candidate to be a foreign key to a company table:\n```sql\nCREATE TABLE company\n(\n    id            INT(10) AUTO_INCREMENT PRIMARY KEY NOT NULL,\n    name          VARCHAR(20) NOT NULL,\n    country_code  VARCHAR(2)  NOT NULL,\n    creation_date DATE        NOT NULL,\n    is_active     TINYINT     NOT NULL\n);\n\nALTER TABLE `person`\n    ADD CONSTRAINT `person_id` FOREIGN KEY (`company_id`) REFERENCES `company` (`id`) ON DELETE CASCADE;\n```\n**Yorm** will detect that there is one Primary Key and a Multiple Key, and use them for the update. Nevertheless, **Yorm** also allows the update operation:\n```java\nPerson person = new Person(2, \"Jacob\", \"jacob.doe@um.com\", 1);\n    yorm.update(person);\n```\nInsertion can even be massive:\n```java\nPerson person1 = new Person(2, \"Hermione\", \"hermione.granger@hogwarts.com\", 1);\n    Person person2 = new Person(3, \"Harry\", \"harry.potter@hogwarts.com\", 1);\n    Person person3 = new Person(4, \"Sauron\", \"sauron@mordor.com\", 2);\n    List\u003cPerson\u003e list = List.of(person1, person2, person3);\n    yorm.insert(list);\n```\nWe've inserted and updated elements in the table. How can we retrieve them into Records? The first and easiest way would be retrieving all the elements. It's just one line:\n```java\nList\u003cPerson\u003e personList = yorm.find(Person.class);\n```\nWhich is the same as the SQL:\n```sql\nSELECT id, name, email, company_id FROM person\n```\nAnd automatically wrapping the result into a List.\nOf course, we could just get one element, if we know the id:\n```java\nPerson person = yorm.find(Person.class, 1);\n```\nWhich translates to SQL of:\n```sql\nSELECT id, name, email, company_id FROM person WHERE id=1\n```\nOr even retrieve elements with a foreign key:\n```java\nCompany company = new Company(1, null, null, null, false);\n    List\u003cPerson\u003e personList = yorm.find(Person.class, company);\n```\nPay attention how the Record company, with id 1, is used to retrieve a list of objects Person whose company_id is 1. **Yorm** will detect that Person has a foreign key with Company and use that to build the query:\n```sql\nSELECT id, name, email, company_id FROM person WHERE company_id=1\n```\nSome kinds of filtering is also allowed:\n```java\nPerson personFilter1 = new Person(0, \"harry\", \"john\", 0);\n    Person personFilter2 = new Person(0, null, null, 2);\n    List\u003cPerson\u003e list = List.of(personFilter1, personFilter2);\n    List\u003cPerson\u003e personList = yorm.find(list);\n```\nLet's review this one a bit. **Yorm** deals with Records. Here we have defined two Person records, the first one with some information on *name* and *email*, and the second one with a *company_id*. **Yorm** will ignore all the nulls and 0s on fields that are ids, but use the rest of the information to build a SELECT query and retrieve a List of Person Records:\n```sql\nSELECT id, name, email, company_id FROM person WHERE name like '%harry%' OR email like '%john%' OR company_id=2\n```\nBased on [Benjiql] idea, **Yorm** also has some sort of fluent API capabilities to find records. The previous example could also be written like:\n```java\nList\u003cPerson\u003e personList = yorm.from(Person.class).where(Person::name).like(\"harry\")\n    .or(Person::email).like(\"john\")\n    .or(Person::companyId).equalTo(2)\n    .find();\n```\n**Yorm** can chain operands to build SQL alike sentences just with code:\n```java\nList\u003cPerson\u003e thirdList = yorm.from(Person.class).where(Person::name).equalTo(\"John\")\n    .and(Person::lastLogin).greaterThan(LocalDateTime.of(2019, 01, 01, 0, 0, 0))\n    .find();\n```\nWhich translated to SQL would be:\n```sql\nSELECT id, name, email, last_login, company_id FROM person WHERE name = 'John' AND last_login \u003e= '2019-01-01 00:00' \n```\nAs a final note, Yorm works just by creating an instance of Yorm with a *javax.sql.DataSource*:\n```java\nDataSource ds = DbConnector.getDatasource(parameters);\n    Yorm yorm = new Yorm(ds);\n```\n**Yorm** is very young but nevertheless quite useful if you just want to perform basic CRUD operations, specially the ones that involve REST endpoints in the world of microservices. It's very to useful, and very transparent, since you just need a DataSource to put it to work.\n\n#### Use it with Maven\nYou can easily add Yorm to your project with\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003eorg.yorm\u003c/groupId\u003e\n  \u003cartifactId\u003eyorm\u003c/artifactId\u003e\n  \u003cversion\u003e0.8.1\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n\n#### Building from source\n##### Dependencies\n * Docker\n\n **Yorm** is a small library published in Maven Central. It can be very easily compiled like\n\n```sh\nmvn clean install\n```\n\n\n\n## License\n\nApache 2.0\n\n\n[//]: # (These are reference links used in the body of this note and get stripped out when the markdown processor does its job. There is no need to format nicely because it shouldn't be seen. Thanks SO - http://stackoverflow.com/questions/4823468/store-comments-in-markdown-syntax)\n\n[HikariCP]: \u003chttps://github.com/brettwooldridge/HikariCP\u003e\n[Mysql]: \u003chttps://www.mysql.com\u003e\n[PostgreSql]: \u003chttps://www.postgresql.org/\u003e\n[Junit 5]: \u003chttps://junit.org/junit5/\u003e\n[TestContainers]: \u003chttps://www.testcontainers.org/\u003e\n[Slf4j]: \u003chttps://www.slf4j.org/manual.html/\u003e\n[PabloGrisafi]: \u003chttps://github.com/pablogrisafi1975\u003e\n[Hibernate]: \u003chttps://hibernate.org/\u003e\n[Jooq]: \u003chttp://www.jooq.org/\u003e\n[Benjiql]: \u003chttps://github.com/benjiman/benjiql\u003e\n   \n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnaynecoder%2Fyorm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnaynecoder%2Fyorm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnaynecoder%2Fyorm/lists"}