{"id":13565307,"url":"https://github.com/brettwooldridge/SansOrm","last_synced_at":"2025-04-03T22:31:03.143Z","repository":{"id":4974605,"uuid":"6132431","full_name":"brettwooldridge/SansOrm","owner":"brettwooldridge","description":"A \"No-ORM\" sane SQL ←→ Java object mapping library","archived":false,"fork":false,"pushed_at":"2024-12-02T05:49:39.000Z","size":329,"stargazers_count":241,"open_issues_count":16,"forks_count":41,"subscribers_count":16,"default_branch":"master","last_synced_at":"2025-04-03T14:12:47.743Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"OpenGamma/OG-Platform","license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/brettwooldridge.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":"2012-10-08T22:48:32.000Z","updated_at":"2025-03-20T05:18:33.000Z","dependencies_parsed_at":"2024-01-14T03:51:37.694Z","dependency_job_id":"31e6224b-e101-45cf-8db7-cf58f15df110","html_url":"https://github.com/brettwooldridge/SansOrm","commit_stats":{"total_commits":179,"total_committers":11,"mean_commits":"16.272727272727273","dds":"0.26815642458100564","last_synced_commit":"ab22721db79c5f20c0e8483f09eda2844d596557"},"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brettwooldridge%2FSansOrm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brettwooldridge%2FSansOrm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brettwooldridge%2FSansOrm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brettwooldridge%2FSansOrm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brettwooldridge","download_url":"https://codeload.github.com/brettwooldridge/SansOrm/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247014524,"owners_count":20869376,"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":[],"created_at":"2024-08-01T13:01:44.234Z","updated_at":"2025-04-03T22:31:03.116Z","avatar_url":"https://github.com/brettwooldridge.png","language":"Java","funding_links":[],"categories":["others","Java"],"sub_categories":[],"readme":"# SansORM\n\n[![][Build Status img]][Build Status]\n[![][license img]][license]\n[![][Maven Central img]][Maven Central]\n[![][Javadocs img]][Javadocs]\n\n## Preface\n\nEven if you do \"pure JDBC\", you will find SansOrm's utility classes extremely useful.  SansOrm is a \"No-ORM\" sane\nJava-to-SQL/SQL-to-Java object mapping library.  It was created to rid my company's product of Hibernate.  After about\n10 years of using ORMs  in various projects, I came to the same conclusion as others: \n[ORM is an Anti-Pattern](https://github.com/brettwooldridge/SansOrm/wiki/ORM-is-an-anti-pattern).\n\nTL;DR:\n\n*  Standard ORMs do not scale.\n*  Don't fear the SQL.\n*  What are you, lazy?  Read the page.\n\n## SansOrm\n\nSansOrm is not an ORM.  SansOrm library will...\n\n* Massively decrease the boilerplate code you write even if you use pure SQL (and no Java objects)\n* Persist and retrieve simple annotated Java objects, and lists thereof, _without you writing SQL_\n* Persist and retrieve complex annotated Java objects, and lists thereof, _where you provide the SQL_\n\nSansOrm will _never_...\n\n* Perform a JOIN for you\n* Persist a graph of objects for you\n* Lazily retrieve anything for you\n* Page data for you\n\nThese things that SansOrm will _never_ do are better and more efficiently performed by _you_.  SansOrm will _help_ you\ndo them simply, but there isn't much magic under the covers.\n\nYou could consider the philosophy of SansOrm to be SQL-first.  That is, think about a correct SQL relational schema *first*, and then once that is correct, consider how to use SansOrm to make your life easier.  In order to scale, your SQL schema design and the queries that run against it need to be efficient.  There is no way to go from an \"object model\" to SQL with any kind of real efficiency, due to an inherent mis-match between the \"object world\" and the \"relational world\".  As others have noted, if you truly need to develop in the currency of pure objects, then what you need is not a relational database but instead an object database.\n\n**Note:** *SansOrm does not currently support MySQL because the MySQL JDBC driver does not return proper metadata\nwhich is required by SansOrm for mapping.  In the future, SansOrm may support a purely 100% annotation-based type\nmapping but this would merely be a concession to MySQL and in no way desirable.*\n\n----------------------------------------------------------------\n\n\u003cimg src=\"https://github.com/brettwooldridge/SansOrm/wiki/quote1.png\"/\u003e\n\n----------------------------------------------------------------\n\n### Initialization\n\nFirst of all we need a datasource. Once you get it, call one of ```SansOrm.initializeXXX``` methods:\n```Java\nDataSource ds = ...;\nSansOrm.initializeTxNone(ds);\n\n// or if you want to use embedded TransactionManager implementation\nSansOrm.initializeTxSimple(ds);\n\n// or if you have your own TransactionManager and UserTransaction\nTransactionManager tm = ...;\nUserTransaction ut = ...;\nSansOrm.initializeTxCustom(ds, tm, ut);\n```\nWe strongly recommend using the embedded ``TransactionManager`` via the the second initializer above.  If you have an existing external ``TransactionManager``, of course you can use that.\n\nThe embedded ``TransactionManager`` conserves database Connections when nested methods are called, alleviating the need to pass ``Connection`` instances around manually.  For example:\n```Java\nList\u003cUser\u003e getUsers(String lastNamePrefix) {\n   return SqlClosure.sqlExecute( connection -\u003e {       // \u003c-- Transaction started, Connection #1 acquired.\n      final List\u003cUsers\u003e users =\n         OrmElf.listFromClause(connection, User.class, \"last_name LIKE ?\", lastNamePrefix + \"%\");\n\n      return populateRoles(users);\n   }\n   // Transaction automatically committed at the end of the execute() call.\n}\n\nList\u003cUser\u003e populatePermissions(final List\u003cUser\u003e users) {\n   return SqlClosure.sqlExecute( connection -\u003e {       // \u003c-- Transaction in-progress, Connection #1 re-used.\n      for (User user : users) {\n         user.setPermissions(OrmElf.listFromClause(connection, Permission.class, \"user_id=?\", user.getId());\n      }\n      return users;\n   }\n   // Transaction will be committed at the end of the execute() call in getUsers() above.\n}\n```\nThe ``TransactionManager`` uses a ``ThreadLocal`` variable to \"flow\" the transaction across nested calls, allowing all work to be committed as a single unit of work.  Additionally, ``Connection`` resources are conserved.  Without a ``TransactionManager``, the above code would require two ``Connections`` to be borrowed from a pool.\n\n### SqlClosure\n\nWe'll work from simple to complex.  In the first examples, the savings in code will not seem that great, but as we go\nthrough the examples you'll notice the code using SansOrm vs. pure Java/JDBC gets more and more compact.\n\nSansOrm provides you with two important classes.  Let's look at the first, which has nothing to do with Java objects or \npersistence.  This class just makes your life easier when writing raw SQL (JDBC).  It is called ```SqlClosure```.\n\nTypical Java pure JDBC with [mostly] correct resource cleanup:\n```Java\npublic int getUserCount(String usernameWildcard) throws SQLException {\n   Connection connection = null;\n   try {\n      connection = dataSource.getConnection();\n      PreparedStatement stmt = connection.prepareStatement(\"SELECT COUNT(*) FROM users WHERE username LIKE ?\");\n      stmt.setString(1, usernameWildcard);\n\n      int count = 0;\n      ResultSet resultSet = stmt.executeQuery();\n      if (resultSet.next() {\n         count = resultSet.getInt(1);\n      }\n      resultSet.close();\n      stmt.close();\n      return count;\n   }\n   finally {\n      if (connection != null) {\n         try {\n            connection.close();\n         }\n         catch (SQLException e) {\n            // ignore\n         }\n      }\n   }\n}\n```\n\nNow the same code using SansOrm's ```SqlClosure``` (with _completely_ correct resource cleanup):\n```Java\npublic int getUserCount(final String usernameWildcard) {\n   return new SqlClosure\u003cInteger\u003e() {\n      public Integer execute(Connection conn) {\n          PreparedStatement stmt = conn.prepareStatement(\"SELECT COUNT(*) FROM users WHERE username LIKE ?\");\n          stmt.setString(1, usernameWildcard);\n          ResultSet resultSet = stmt.executeQuery();\n          return (resultSet.next() ? resultSet.getInt(1) : 0;\n      }\n   }.execute();\n}\n```\nImportant points:\n* The SqlClosure class is a generic (templated) class\n* The SqlClosure class will call your ```execute(Connection)``` method with a provided connection\n   * The provided connection will be closed quietly automatically (i.e. exceptions in ```connection.close()``` will be eaten)\n* SqlExceptions thrown from the body of the ```execute()``` method will be wrapped in a RuntimeException\n\n**Now with a Java 8 Lambda** \u003cbr\u003e\n```java\npublic int getUserCount(final String usernameWildcard) {\n   return SqlClosure.sqlExecute(connection -\u003e {\n      PreparedStatement stmt = connection.prepareStatement(\"SELECT COUNT(*) FROM users WHERE username LIKE ?\"));\n      stmt.setString(1, usernameWildcard);\n      ResultSet resultSet = stmt.executeQuery();\n      return (resultSet.next() ? resultSet.getInt(1) : 0;\n   });\n}\n```\nNote that the lambda automatically closes Statement and ResultSet resources.\n\nAs mentioned above, the ```SqlClosure``` class is generic, and the signature looks something like this:\n```Java\npublic class T SqlClosure\u003cT\u003e {\n   public abstract T execute(Connection);\n   public T execute();\n   public static \u003cV\u003e V sqlExecute(final SqlVarArgsFunction\u003cV\u003e functional, final Object... args);\n}\n```\n```SqlClosure``` is typically constructed as an anonymous class, and you must provide the implementation of \nthe ```execute(Connection connection)``` method.  Invoking the ```execute()``` method (no parameters) will create a\nConnection and invoke your overridden method, cleaning up resources in a finally, and returning the value\nreturned by the overridden method.  Of course you don't have to execute the closure right away; you could stick it into \na queue for later execution, pass it to another method, etc.  But typically you'll run execute it right away.\n\nMore common still is using **Java 8 Lambdas**.\n\nLet's look at an example of returning a complex type:\n```Java\npublic Set\u003cString\u003e getAllUsernames() {\n   return new SqlClosure\u003cSet\u003cString\u003e\u003e() {\n      public Set\u003cString\u003e execute(Connection connection) {\n         Set\u003cString\u003e usernames = new HashSet\u003c\u003e();\n         Statement statement = connection.createStatement();\n         ResultSet resultSet = statement.executeQuery(\"SELECT username FROM users\");\n         while (resultSet.next()) {\n            usernames.add(resultSet.getString(\"username\"));\n         }\n         return usernames;\n      }\n   }.execute();\n}\n```\n**And again with Java 8 Lambda** \u003cbr\u003e\n```Java\npublic Set\u003cString\u003e getAllUsernames() {\n   return SqlClosure.sqlExecute(connection -\u003e {\n      Set\u003cString\u003e usernames = new HashSet\u003c\u003e();\n      Statement statement = connection.createStatement();\n      ResultSet resultSet = statement.executeQuery(\"SELECT username FROM users\");\n      while (resultSet.next()) {\n         usernames.add(resultSet.getString(\"username\"));\n      }\n      return usernames;\n   });\n}\n```\nEven if you use no other features of SansOrm, the ```SqlClosure``` class alone can really help to cleanup and simplify\nyour code.\n\n### Object Mapping\nWhile the ```SqlClosure``` is extremly useful and helps reduce the boilerplate code that you write, we know why you're \nhere: _object mapping_.  Let's jump right in with some examples.\n\nTake this database table:\n```SQL\nCREATE TABLE customer (\n   customer_id INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY,\n   last_name VARCHAR(255),\n   first_name VARCHAR(255),\n   email VARCHAR(255)\n);\n```\nLet's imagine a Java class that reflects the table in a straight-forward way, and contains some JPA (javax.persistence) annotations:\n\nCustomer:\n```Java\n@Table(name = \"customer\")\npublic class Customer {\n   @Id\n   @GeneratedValue(strategy = GenerationType.IDENTITY)\n   @Column(name = \"customer_id\")\n   private int customer_id;\n\n   @Column(name = \"last_name\")\n   private String lastName;\n\n   @Column(name = \"first_name\")\n   private String firstName;\n\n   @Column(name = \"email\")\n   private String emailAddress;\n\n   public Customer() {\n      // no arg constuctor declaration is necessary only when other constructors are declared\n   }\n}\n```\nHere we introduce another SansOrm class, ```OrmElf```.  What is ```OrmElf```?  Well, an 'Elf' is a 'Helper'\nbut with fewer letters to type.  Besides, who doesn't like Elves?  Let's look at how the ```OrmElf``` can help us:\n```Java\npublic List\u003cCustomer\u003e getAllCustomers() {\n   return SqlClosure.sqlExecute( connection -\u003e {\n      PreparedStatement pstmt = connection.prepareStatement(\"SELECT * FROM customer\");\n      return OrmElf.statementToList(pstmt, Customer.class);\n   });\n}\n```\nThe OrmElf will execute the ```PreparedStatement``` and using the annotations in the ```Customer``` class will\nconstruct a ```List``` of ```Customer``` instances whose values come from the ```ResultSet```.  *Note that\n```OrmElf``` will set the properties directly on the object, it does not use getter/setters.  Note also that\n```autoClose()``` was not necessary, the OrmElf will close the statement automatically.*\n\nOf course, in addition to querying, the ```OrmElf``` can perform basic operations such these (where ```customer```\nis a ```Customer```):\n* ```OrmElf.insertObject(connection, customer)```\n* ```OrmElf.updateObject(connection, customer)```\n* ```OrmElf.deleteObject(connection, customer)```\n\nLet's make another example, somewhat silly, but showing how queries can be parameterized:\n```Java\npublic List\u003cCustomer\u003e getCustomersSillyQuery(final int minId, final int maxId, final String like) {\n   return SqlClosure.sqlExecute( conn -\u003e {\n      PreparedStatement pstmt = conn.prepareStatement(\n         \"SELECT * FROM customer WHERE (customer_id BETWEEN ? AND ?) AND last_name LIKE ?\"));\n      return OrmElf.statementToList(pstmt, Customer.class, minId, maxId, like+\"%\");\n   });\n}\n```\nWell, that's fairly handy. Note the use of varargs. Following the class parameter, zero or more parameters can be passed,\nand will be used to set query parameters (in order) on the ```PreparedStatement```.\n\nMaterializing object instances from rows is so common, there are some further things the 'Elf' can help with.  Let's do \nthe same thing as above, but using another helper method.\n```Java\npublic List\u003cCustomer\u003e getCustomersSillyQuery(final int minId, final int maxId, final String like) {\n   return SqlClosure.sqlExecute( connection -\u003e {\n      return OrmElf.listFromClause(connection, Customer.class,\n                                   \"(customer_id BETWEEN ? AND ?) AND last_name LIKE ?\",\n                                   minId, maxId, like+\"%\");\n   });\n}\n```\nNow we're cooking with gas!  The ```OrmElf``` will use the ```Connection``` that is passed, along with the annotations\non the ```Customer``` class to determine which table and columns to SELECT, and use the passed `clause` as the\nWHERE portion of the statement (passing 'WHERE' explicitly is also supported), and finally it will use the passed \nparameters to set the query parameters.\n\nWhile the ```SqlClosure``` is great, and you'll come to wonder how you did without it, for some simple cases like the\nprevious example, it adds a little bit of artiface around what could be even simpler.\n\nEnter ```SqlClosureElf```.  Yes, another elf.\n```Java\npublic List\u003cCustomer\u003e getCustomersSillyQuery(int minId, int maxId, String like) {\n   return SqlClosureElf.listFromClause(Customer.class, \n                                       \"(customer_id BETWEEN ? AND ?) AND last_name LIKE ?\",\n                                       minId, maxId, \"%\"+like+\"%\");\n}\n```\nHere the ```SqlClosureElf``` is creating the ```SqlClosure``` under the covers as well as using the ```OrmElf``` to retrieve\nthe list of ```Customer``` instances. Like the ```OrmElf``` the ```SqlClosureElf``` exposes lots of methods for\ncommon scenarios, a few are:\n* ```SqlClosureElf.insertObject(customer)```\n* ```SqlClosureElf.updateObject(customer)```\n* ```SqlClosureElf.deleteObject(customer)```\n\n### Supported Annotations\nExcept for the ``@Table`` and ``@MappedSuperclass`` annotations, which must annotate a *class*, all other annotations must appear on *member variables*.  Annotations on *getter/setter* methods are not supported.  SansOrm will get/set member variables directly through reflection during read/write operations.\n\nThe following annotations are supported:\n\n| Annotation            | Supported Attributes                                 |\n|:--------------------- |:---------------------------------------------------- |\n| ``@Column``           | ``name``, ``insertable``, ``updatable``, ``table``   |\n| ``@Convert``          | ``converter`` (``AttributeConverter`` _classes only_)|\n| ``@Enumerated``       | ``value`` (=``EnumType.ORDINAL``, ``EnumType.STRING``) |\n| ``@GeneratedValue``   | ``strategy`` (``GenerationType.IDENTITY`` _only_)    |\n| ``@Id``               | n/a                                                  |\n| ``@JoinColumn``       | ``name`` (supports **self-join** _only_)             |\n| ``@MappedSuperclass`` | n/a                                                  |\n| ``@Table``            | ``name``                                             |\n| ``@Transient``        | n/a                                                  |\n\nBy default, SansOrm will *lower-case* all ``name`` and ``table`` attribute values, which is fine for DML case-_insensitive_ databases such as PostgreSQL, Derby, Oracle, etc.  However, some databases are case-sensitive with respect to identifiers, such as H2. Therefore, SansOrm supports *case-sensitive* databases through the use of quoted identifiers.\n\nQuoted identifer example:\n```java\n@Table(name = \"\\\"Customer\\\"\")\nclass Customer {\n   @Column(name = \"\\\"Last_Name\\\"\")\n   String lastName;\n   ...\n}\n```\n\n### Automatic Data Type Conversions\n\n#### Writing\nWhen *writing* data to JDBC, SansOrm relies on the *driver* to perform most conversions.  SansOrm only calls ``Statement.setObject()`` internally, and expects that the driver will properly perform conversions.  For example, convert an ``int`` or ``java.lang.Integer`` into an ``INTEGER`` column type.\n\nIf the ``@Convert`` annotation is present on the field in question, the appropriate user-specified ``javax.persistence.AttributeConverter`` will be called. \n\nFor fields where the ``@Enumerated`` annotation is present, SansOrm will obtain the value to persist by calling ``ordinal()`` on the ``enum`` instance in the case of ``EnumType.ORDINAL``, and ``name()`` on the ``enum`` instance in the case of ``EnumType.STRING``.\n\n#### Reading\nWhen *reading* data from JDBC, SansOrm relies on the *driver* to perform most conversions.  SansOrm only calls ``ResultSet.getObject()`` internally, and expects that the driver will properly perform conversions to Java types.  For example , for an ``INTEGER`` column type, return a ``java.lang.Integer`` from ``ResultSet.getObject()``.\n\nHowever, if the Java object type returned by the driver *does not match* the type of the mapped member field, SansOrm permits the following automatic conversions:\n\n| Driver ``getObject()`` Java Type | Mapped Member Java type                 |\n|:-------------------------------- |:--------------------------------------- |\n| ``java.lang.Integer``            | ``boolean`` (0 == ``false``, everything else ``true``)|\n| ``java.math.BigDecimal``         | ``java.math.BigInteger``  |\n| ``java.math.BigDecimal``         | ``int`` or ``java.lang.Integer`` (via cast)  |\n| ``java.math.BigDecimal``         | ``long`` or ``java.lang.Long`` (via cast) |\n| ``java.math.BigDecimal``         | ``double`` or ``java.lang.Double`` (via cast) |\n| ``java.util.UUID``               | ``String``                                |\n| ``java.sql.Clob``                | ``String``                                |\n\nIf the ``@Convert`` annotation is present on the field in question, the appropriate user-specified ``javax.persistence.AttributeConverter`` will be called. \n\nFor fields where the ``@Enumerated`` annotation is present, SansOrm will map ``java.lang.Integer`` values from the driver to the correct ``Enum`` value in the case of ``EnumType.ORDINAL``, and will map ``java.lang.String`` values from the driver to the correct ``Enum`` value in the case of ``EnumType.STRING``.\n\nFinally, SansOrm has specific support for the PostgreSQL ``PGobject`` and ``CITEXT`` data types.  ``CITEXT`` column values are converted to ``java.lang.String``.  ``PGobject`` \"unknown type\" column values have their ``getValue()`` method called, and the result is attempted to be set via reflection onto the mapped member field.\n\n### More Advanced\n\nJust page as provided just a taste, so go on over to the [Advanced Usage](https://github.com/brettwooldridge/SansOrm/blob/master/doc/AdvancedUsage.md) page to go deep.\n\n\n[Build Status]:https://travis-ci.org/brettwooldridge/SansOrm\n[Build Status img]:https://travis-ci.org/brettwooldridge/SansOrm.svg?branch=master\n\n[license]:LICENSE\n[license img]:https://img.shields.io/badge/license-Apache%202-blue.svg\n   \n[Maven Central]:https://maven-badges.herokuapp.com/maven-central/com.zaxxer/sansorm\n[Maven Central img]:https://maven-badges.herokuapp.com/maven-central/com.zaxxer/sansorm/badge.svg\n   \n[Javadocs]:http://javadoc.io/doc/com.zaxxer/sansorm\n[Javadocs img]:http://javadoc.io/badge/com.zaxxer/sansorm.svg\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrettwooldridge%2FSansOrm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrettwooldridge%2FSansOrm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrettwooldridge%2FSansOrm/lists"}