├── .gitignore ├── COPYRIGHT ├── LICENSE ├── README.md ├── pom.xml ├── sql └── drop-create.sql └── src ├── main ├── java │ └── com │ │ └── github │ │ └── pires │ │ └── example │ │ ├── dao │ │ ├── AbstractDao.java │ │ └── JSONBEntityDao.java │ │ ├── hibernate │ │ ├── dialect │ │ │ └── JSONBPostgreSQLDialect.java │ │ └── user │ │ │ └── types │ │ │ ├── CollectionUserType.java │ │ │ ├── JSONBUserType.java │ │ │ └── MutableUserType.java │ │ └── model │ │ ├── JSONBDocument.java │ │ └── JSONBEntity.java └── resources │ └── hibernate_ddl_generation.properties └── test ├── java └── com │ └── github │ └── pires │ └── example │ ├── TestEnvironment.java │ └── jsonb │ └── JSONBEntityDaoTest.java └── resources ├── META-INF └── persistence.xml └── log4j.properties /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Package Files # 4 | *.jar 5 | *.war 6 | *.ear 7 | 8 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 9 | hs_err_pid* 10 | target 11 | gen 12 | build 13 | test-output 14 | 15 | # Idea 16 | *.iml 17 | .idea 18 | 19 | # Eclipse 20 | .settings 21 | .classpath 22 | .project 23 | 24 | #Temp Files 25 | *~ 26 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Licensed under the Apache License, Version 2.0 (the "License"); you may not 2 | use this file except in compliance with the License. You may obtain a copy of 3 | the License at 4 | http://www.apache.org/licenses/LICENSE-2.0 5 | 6 | Unless required by applicable law or agreed to in writing, software 7 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 8 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 9 | License for the specific language governing permissions and limitations under 10 | the License. 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | hibernate-postgres-jsonb 2 | ====================== 3 | 4 | Hibernate 4.3.x (JPA 2.1) + PostgreSQL 9.4 JSONB support example. 5 | 6 | What I've implemented: 7 | * Hibernate dialect with JSONB support 8 | * Hibernate user-type for JSONB support (with de/serialization from/to JSON, powered by Jackson ```ObjectMapper```) 9 | * Static-metamodel & SQL generation 10 | * Simple DAO with native JSONB query 11 | 12 | # Pre-requisites 13 | 14 | * JDK 7 15 | * Maven 3.1.0 or newer 16 | 17 | # Build and test 18 | 19 | ## PostgreSQL 9.4 20 | 21 | Install, configure and start PostgreSQL 9.4 on ```localhost```, with username ```postgres``` and password ```123qwe```. If you change this, you must update ```src/test/resources/META-INF/persistence.xml``` accordingly. 22 | 23 | ``` 24 | sudo su postgres 25 | createdb dbtest 26 | ``` 27 | 28 | ## Test 29 | 30 | ``` 31 | mvn clean test 32 | ``` 33 | 34 | You should see something like this: 35 | ``` 36 | ------------------------------------------------------- 37 | T E S T S 38 | ------------------------------------------------------- 39 | Running TestSuite 40 | Configuring TestNG with: TestNG652Configurator 41 | 15:06:14,354 INFO LogHelper:46 - HHH000204: Processing PersistenceUnitInfo [ 42 | name: test 43 | ...] 44 | 15:06:14,416 INFO Version:54 - HHH000412: Hibernate Core {4.3.6.Final} 45 | (...) 46 | 15:06:14,585 INFO Version:66 - HCANN000001: Hibernate Commons Annotations {4.0.5.Final} 47 | 15:06:14,607 WARN DriverManagerConnectionProviderImpl:93 - HHH000402: Using Hibernate built-in connection pool (not for production use!) 48 | 15:06:14,616 INFO DriverManagerConnectionProviderImpl:166 - HHH000401: using driver [org.postgresql.Driver] at URL [jdbc:postgresql://localhost:5432/dbtest] 49 | 15:06:14,616 INFO DriverManagerConnectionProviderImpl:175 - HHH000046: Connection properties: {user=postgres, password=****} 50 | 15:06:14,616 INFO DriverManagerConnectionProviderImpl:180 - HHH000006: Autocommit mode: false 51 | 15:06:14,617 INFO DriverManagerConnectionProviderImpl:102 - HHH000115: Hibernate connection pool size: 20 (min=1) 52 | 15:06:14,849 INFO Dialect:145 - HHH000400: Using dialect: com.github.pires.example.hibernate.dialect.JSONBPostgreSQLDialect 53 | 15:06:14,869 INFO LobCreatorBuilder:123 - HHH000424: Disabling contextual LOB creation as createClob() method threw error : java.lang.reflect.InvocationTargetException 54 | 15:06:15,055 INFO ASTQueryTranslatorFactory:47 - HHH000397: Using ASTQueryTranslatorFactory 55 | 15:06:15,073 TRACE TypeFactory:72 - Scoping types to session factory org.hibernate.internal.SessionFactoryImpl@73f808ef 56 | 15:06:15,231 INFO SchemaExport:343 - HHH000227: Running hbm2ddl schema export 57 | Hibernate: 58 | drop table if exists JSONB_ENTITIES cascade 59 | Hibernate: 60 | create table JSONB_ENTITIES ( 61 | id uuid not null, 62 | documents jsonb, 63 | version int8, 64 | primary key (id) 65 | ) 66 | 15:06:15,456 INFO SchemaExport:405 - HHH000230: Schema export complete 67 | Hibernate: 68 | insert 69 | into 70 | JSONB_ENTITIES 71 | (documents, version, id) 72 | values 73 | (?, ?, ?) 74 | 15:06:15,591 TRACE BasicBinder:81 - binding parameter [2] as [BIGINT] - [0] 75 | 15:06:15,592 TRACE BasicBinder:81 - binding parameter [3] as [OTHER] - [3bc2ac86-cd91-4795-b0b1-b9cdf3b5c2a2] 76 | Hibernate: 77 | insert 78 | into 79 | JSONB_ENTITIES 80 | (documents, version, id) 81 | values 82 | (?, ?, ?) 83 | 15:06:15,598 TRACE BasicBinder:81 - binding parameter [2] as [BIGINT] - [0] 84 | 15:06:15,599 TRACE BasicBinder:81 - binding parameter [3] as [OTHER] - [1d0d0fb0-e127-4812-90aa-4f27084a6580] 85 | Hibernate: 86 | select 87 | count(jsonbentit0_.id) as col_0_0_ 88 | from 89 | JSONB_ENTITIES jsonbentit0_ 90 | 15:06:15,734 TRACE BasicExtractor:78 - extracted value ([col_0_0_] : [BIGINT]) - [2] 91 | Hibernate: 92 | insert 93 | into 94 | JSONB_ENTITIES 95 | (documents, version, id) 96 | values 97 | (?, ?, ?) 98 | 15:06:15,736 TRACE BasicBinder:81 - binding parameter [2] as [BIGINT] - [0] 99 | 15:06:15,736 TRACE BasicBinder:81 - binding parameter [3] as [OTHER] - [c2841567-729a-47b1-badc-65d60650b78d] 100 | Hibernate: 101 | select 102 | count(jsonbentit0_.id) as col_0_0_ 103 | from 104 | JSONB_ENTITIES jsonbentit0_ 105 | 15:06:15,739 TRACE BasicExtractor:78 - extracted value ([col_0_0_] : [BIGINT]) - [3] 106 | Hibernate: 107 | select 108 | * 109 | from 110 | "public".jsonb_entities, 111 | lateral jsonb_array_elements(documents) document_entry 112 | where 113 | CAST(document_entry ->> 'created' AS bigint) BETWEEN ? AND ?; 114 | 15:06:15,755 TRACE BasicBinder:81 - binding parameter [1] as [BIGINT] - [1413209174141] 115 | 15:06:15,756 TRACE BasicBinder:81 - binding parameter [2] as [BIGINT] - [1413209184141] 116 | 15:06:15,758 TRACE BasicExtractor:78 - extracted value ([id] : [OTHER]) - [3bc2ac86-cd91-4795-b0b1-b9cdf3b5c2a2] 117 | 15:06:15,758 TRACE BasicExtractor:78 - extracted value ([id] : [OTHER]) - [1d0d0fb0-e127-4812-90aa-4f27084a6580] 118 | 15:06:15,759 TRACE BasicExtractor:78 - extracted value ([id] : [OTHER]) - [1d0d0fb0-e127-4812-90aa-4f27084a6580] 119 | 15:06:15,776 INFO DriverManagerConnectionProviderImpl:281 - HHH000030: Cleaning up connection pool [jdbc:postgresql://localhost:5432/dbtest] 120 | Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.829 sec - in TestSuite 121 | 122 | Results : 123 | 124 | Tests run: 5, Failures: 0, Errors: 0, Skipped: 0 125 | 126 | ``` 127 | 128 | # Generate drop-create SQL 129 | 130 | ``` 131 | mvn clean package -Pgenerate-sql 132 | ``` 133 | 134 | See ```sql/drop-create.sql``` 135 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 4.0.0 7 | 8 | com.github.pires.example 9 | hibernate-jsonb-example 10 | 0.1-SNAPSHOT 11 | Hibernate + PostgreSQL JSONB example 12 | jar 13 | 14 | 15 | UTF-8 16 | 1.7 17 | 18 | 6.8.8 19 | 1.7.7 20 | 2.4.3 21 | 4.3.6.Final 22 | 1.0.0.Final 23 | 1.3.0.Final 24 | 9.3-1102-jdbc4 25 | 1.0.3 26 | 27 | 2.6 28 | 3.1 29 | 2.6 30 | 1.8 31 | 2.9.1 32 | 2.6 33 | 2.17 34 | 35 | 36 | 37 | 38 | org.slf4j 39 | slf4j-api 40 | ${slf4j.version} 41 | 42 | 43 | org.hibernate 44 | hibernate-entitymanager 45 | ${hibernate.version} 46 | 47 | 48 | org.hibernate 49 | hibernate-jpamodelgen 50 | ${hibernate.jpamodelgen.version} 51 | 52 | 53 | org.hibernate.javax.persistence 54 | hibernate-jpa-2.1-api 55 | ${hibernate.jpa21.version} 56 | 57 | 58 | com.fasterxml.jackson.core 59 | jackson-databind 60 | ${jackson.version} 61 | 62 | 63 | org.postgresql 64 | postgresql 65 | ${postgres.driver.version} 66 | 67 | 68 | org.testng 69 | testng 70 | ${testng.version} 71 | test 72 | 73 | 74 | org.slf4j 75 | slf4j-log4j12 76 | ${slf4j.version} 77 | test 78 | 79 | 80 | 81 | 82 | 83 | 84 | org.apache.maven.plugins 85 | maven-clean-plugin 86 | ${maven-clean-plugin.version} 87 | 88 | 89 | 90 | ${basedir}/gen/main/java 91 | 92 | .gitkeep 93 | 94 | 95 | */** 96 | 97 | 98 | 99 | 100 | 101 | 102 | org.apache.maven.plugins 103 | maven-compiler-plugin 104 | ${maven-compiler-plugin.version} 105 | 106 | ${java.version} 107 | ${java.version} 108 | true 109 | true 110 | -proc:none 111 | 112 | 113 | 114 | org.apache.maven.plugins 115 | maven-resources-plugin 116 | ${maven-resources-plugin.version} 117 | 118 | ${project.build.sourceEncoding} 119 | 120 | 121 | 122 | org.bsc.maven 123 | maven-processor-plugin 124 | 2.2.4 125 | 126 | 127 | process 128 | 129 | process 130 | 131 | generate-sources 132 | 133 | ${project.build.directory}/generated-sources 134 | org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor 135 | 136 | 137 | 138 | 139 | 140 | org.codehaus.mojo 141 | build-helper-maven-plugin 142 | 1.8 143 | 144 | 145 | add-source 146 | generate-sources 147 | 148 | add-source 149 | 150 | 151 | 152 | ${project.build.directory}/generated-sources 153 | 154 | 155 | 156 | 157 | 158 | 159 | com.mycila 160 | license-maven-plugin 161 | ${license.plugin.version} 162 | false 163 | 164 | true 165 |
${basedir}/COPYRIGHT
166 | true 167 | 168 | **/src/main/**/*.java 169 | **/src/test/**/*.java 170 | **/src/main/**/*.xml 171 | **/src/test/**/*.xml 172 | 4.0.0 173 | 174 | true 175 | UTF-8 176 | 177 | ${project.inceptionYear} 178 | 179 |
180 | 181 | 182 | process-test-resources 183 | 184 | format 185 | 186 | 187 | 188 |
189 | 190 | org.apache.maven.plugins 191 | maven-surefire-plugin 192 | ${maven-surefire-plugin.version} 193 | 194 |
195 |
196 | 197 | 198 | 199 | generate-sql 200 | 201 | false 202 | 203 | 204 | 205 | 206 | de.juplo 207 | hibernate4-maven-plugin 208 | ${hibernate4-plugin.version} 209 | 210 | ${project.build.outputDirectory}/hibernate_ddl_generation.properties 211 | SCRIPT 212 | sql/drop-create.sql 213 | 214 | 215 | 216 | 217 | export 218 | 219 | 220 | 221 | 222 | 223 | org.hibernate 224 | hibernate-core 225 | ${hibernate.version} 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 |
235 | -------------------------------------------------------------------------------- /sql/drop-create.sql: -------------------------------------------------------------------------------- 1 | 2 | drop table if exists JSONB_ENTITIES cascade; 3 | 4 | create table JSONB_ENTITIES ( 5 | id uuid not null, 6 | documents jsonb, 7 | version int8, 8 | primary key (id) 9 | ); 10 | -------------------------------------------------------------------------------- /src/main/java/com/github/pires/example/dao/AbstractDao.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 3 | * use this file except in compliance with the License. You may obtain a copy of 4 | * the License at 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | * License for the specific language governing permissions and limitations under 11 | * the License. 12 | */ 13 | package com.github.pires.example.dao; 14 | 15 | import java.util.List; 16 | import javax.persistence.EntityManager; 17 | import javax.persistence.criteria.CriteriaBuilder; 18 | import javax.persistence.criteria.CriteriaQuery; 19 | import javax.persistence.criteria.Root; 20 | import javax.persistence.metamodel.EntityType; 21 | 22 | /** 23 | * Abstract data access object . 24 | * 25 | * @param 26 | * the entity type to be managed. 27 | */ 28 | public abstract class AbstractDao { 29 | 30 | protected final Class entityClass; 31 | private EntityManager em; 32 | 33 | public AbstractDao(Class entityClass) { 34 | this.entityClass = entityClass; 35 | } 36 | 37 | public EntityManager getEntityManager() { 38 | return this.em; 39 | } 40 | 41 | public void setEntityManager(EntityManager em) { 42 | this.em = em; 43 | } 44 | 45 | protected EntityType getMetaModel() { 46 | return getEntityManager(). 47 | getMetamodel(). 48 | entity(entityClass); 49 | } 50 | 51 | public void persist(T entity) { 52 | getEntityManager(). 53 | persist(entity); 54 | } 55 | 56 | public T merge(T entity) { 57 | return getEntityManager(). 58 | merge(entity); 59 | } 60 | 61 | public void remove(Object entityId) { 62 | T entity = find(entityId); 63 | if (entity != null) { 64 | getEntityManager(). 65 | remove(entity); 66 | } 67 | } 68 | 69 | public T find(Object id) { 70 | return getEntityManager(). 71 | find(entityClass, id); 72 | } 73 | 74 | public List findAll() { 75 | CriteriaQuery cq = getEntityManager(). 76 | getCriteriaBuilder(). 77 | createQuery( 78 | entityClass); 79 | cq.select(cq.from(entityClass)); 80 | return getEntityManager(). 81 | createQuery(cq). 82 | getResultList(); 83 | } 84 | 85 | public int count() { 86 | CriteriaBuilder cb = getEntityManager(). 87 | getCriteriaBuilder(); 88 | CriteriaQuery cq = cb.createQuery(Long.class); 89 | Root root = cq.from(entityClass); 90 | cq.select(cb.count(root)); 91 | return getEntityManager(). 92 | createQuery(cq). 93 | getSingleResult(). 94 | intValue(); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/com/github/pires/example/dao/JSONBEntityDao.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 3 | * use this file except in compliance with the License. You may obtain a copy of 4 | * the License at 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | * License for the specific language governing permissions and limitations under 11 | * the License. 12 | */ 13 | package com.github.pires.example.dao; 14 | 15 | import com.github.pires.example.model.JSONBEntity; 16 | import java.util.List; 17 | import javax.persistence.Query; 18 | 19 | /** 20 | * TODO add description 21 | */ 22 | public class JSONBEntityDao extends AbstractDao { 23 | 24 | public JSONBEntityDao() { 25 | super(JSONBEntity.class); 26 | } 27 | 28 | public List find_all_by_document_created_between( 29 | final long begin, final long end) { 30 | final String sql = "select * from \"public\".jsonb_entities, lateral jsonb_array_elements(documents) document_entry where CAST(document_entry ->> 'created' AS bigint) BETWEEN ? AND ?;"; 31 | Query query = getEntityManager(). 32 | createNativeQuery(sql, entityClass); 33 | query.setParameter(1, begin); 34 | query.setParameter(2, end); 35 | return query.getResultList(); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/github/pires/example/hibernate/dialect/JSONBPostgreSQLDialect.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 3 | * use this file except in compliance with the License. You may obtain a copy of 4 | * the License at 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | * License for the specific language governing permissions and limitations under 11 | * the License. 12 | */ 13 | package com.github.pires.example.hibernate.dialect; 14 | 15 | import java.sql.Types; 16 | import org.hibernate.dialect.PostgreSQL82Dialect; 17 | 18 | public class JSONBPostgreSQLDialect extends PostgreSQL82Dialect { 19 | 20 | public JSONBPostgreSQLDialect() { 21 | super(); 22 | registerColumnType(Types.JAVA_OBJECT, "jsonb"); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/github/pires/example/hibernate/user/types/CollectionUserType.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 3 | * use this file except in compliance with the License. You may obtain a copy of 4 | * the License at 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | * License for the specific language governing permissions and limitations under 11 | * the License. 12 | */ 13 | package com.github.pires.example.hibernate.user.types; 14 | 15 | import java.util.ArrayList; 16 | import java.util.Collection; 17 | import java.util.HashSet; 18 | import java.util.LinkedList; 19 | import java.util.List; 20 | import java.util.Set; 21 | import org.hibernate.HibernateException; 22 | import org.hibernate.usertype.UserType; 23 | 24 | /** 25 | * A abstract class for {@link UserType UserTypes} that may return 26 | * mutable collections 27 | * (but may also return any other object types). 28 | *

29 | * {@link #deepCopy(Object) Deep copies} collections to ensure any changes to 30 | * the 31 | * collection are tracked, so the collection is correctly updated. Recursively 32 | * clones collections 33 | * that contain collections, but not safe for collections that contain 34 | * themselves. 35 | */ 36 | public abstract class CollectionUserType extends MutableUserType { 37 | 38 | @SuppressWarnings("unchecked") 39 | @Override 40 | public Object deepCopy(Object value) throws HibernateException { 41 | /* 42 | * For non-collections, delegate; for collections, return a new collection 43 | * containing 44 | * the members of the initial collection. 45 | */ 46 | if (!(value instanceof Collection)) { 47 | return deepCopyValue(value); 48 | } 49 | 50 | /* 51 | * Have to use a non-generic form because otherwise there is no way to get 52 | * the two ? to match 53 | * in the add call below. 54 | */ 55 | Collection collection = (Collection) value; 56 | Collection collectionClone = CollectionFactory.newInstance(collection. 57 | getClass()); 58 | 59 | /* 60 | * Recurse on the members of the collection (which may themselves be 61 | * collections!) 62 | * XXX: will cause an endless loop for collections which contain themselves! 63 | */ 64 | for (Object member : collection) { 65 | collectionClone.add(deepCopy(member)); 66 | } 67 | 68 | return collectionClone; 69 | } 70 | 71 | /** 72 | * Creates a deep copy of the given value. 73 | *

74 | * See {@link UserType#deepCopy(Object)}. 75 | * 76 | * @param value the object to be copied 77 | * @return a deep copy of the given object 78 | */ 79 | protected abstract Object deepCopyValue(Object value); 80 | 81 | static final class CollectionFactory { 82 | 83 | /** 84 | * Creates a new collection instance. The runtime collection subtype of the 85 | * returned 86 | * instance matches that of the input collection, but the actual class may 87 | * differ. 88 | *

89 | * So for a {@link LinkedList} argument, the result is guaranteed to be a 90 | * {@link List}. It may, however, be an {@link ArrayList} rather than a 91 | * {@code LinkedList}. 92 | * 93 | * @param the type of the collection elements 94 | * @param the type of the collection 95 | * @param collectionClass the runtime class of the collection 96 | * @return an instance of a collection that is of the same collection 97 | * subtype as the 98 | * given class 99 | * @throws IllegalArgumentException if the collection type is not supported 100 | */ 101 | @SuppressWarnings("unchecked") 102 | static > T newInstance(Class collectionClass) { 103 | if (List.class.isAssignableFrom(collectionClass)) { 104 | return (T) new ArrayList(); 105 | } else if (Set.class.isAssignableFrom(collectionClass)) { 106 | return (T) new HashSet(); 107 | } else { 108 | throw new IllegalArgumentException("Unsupported collection type: " 109 | + collectionClass); 110 | } 111 | 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/com/github/pires/example/hibernate/user/types/JSONBUserType.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 3 | * use this file except in compliance with the License. You may obtain a copy of 4 | * the License at 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | * License for the specific language governing permissions and limitations under 11 | * the License. 12 | */ 13 | package com.github.pires.example.hibernate.user.types; 14 | 15 | import com.fasterxml.jackson.core.JsonProcessingException; 16 | import com.fasterxml.jackson.databind.ObjectMapper; 17 | import java.io.IOException; 18 | import java.io.Serializable; 19 | import java.sql.PreparedStatement; 20 | import java.sql.ResultSet; 21 | import java.sql.SQLException; 22 | import java.sql.Types; 23 | import java.util.Properties; 24 | import org.hibernate.HibernateException; 25 | import org.hibernate.engine.spi.SessionImplementor; 26 | import org.hibernate.internal.util.ReflectHelper; 27 | import org.hibernate.usertype.ParameterizedType; 28 | import org.hibernate.usertype.UserType; 29 | import org.postgresql.util.PGobject; 30 | 31 | /** 32 | * A {@link UserType} that persists objects as JSONB. 33 | *

34 | * Unlike the default JPA object mapping, {@code JSONBUserType} can also be used 35 | * for properties that do not implement {@link Serializable}. 36 | *

37 | * Users intending to use this type for mutable non-Collection 38 | * objects should override {@link #deepCopyValue(Object)} to correctly return a 39 | * copy of the object. 40 | */ 41 | public class JSONBUserType extends CollectionUserType implements 42 | ParameterizedType { 43 | 44 | private static final ObjectMapper MAPPER = new ObjectMapper(); 45 | private static final String JSONB_TYPE = "jsonb"; 46 | public static final String CLASS = "CLASS"; 47 | private Class returnedClass; 48 | 49 | @Override 50 | public Class returnedClass() { 51 | return Object.class; 52 | } 53 | 54 | @Override 55 | public int[] sqlTypes() { 56 | return new int[]{Types.JAVA_OBJECT}; 57 | } 58 | 59 | @Override 60 | public Object nullSafeGet(ResultSet resultSet, String[] names, 61 | SessionImplementor session, Object owner) 62 | throws HibernateException, SQLException { 63 | try { 64 | final String json = resultSet.getString(names[0]); 65 | return json == null 66 | ? null 67 | : MAPPER.readValue(json, returnedClass); 68 | } catch (IOException ex) { 69 | throw new HibernateException(ex); 70 | } 71 | } 72 | 73 | @Override 74 | public void nullSafeSet(PreparedStatement st, Object value, int index, 75 | SessionImplementor session) throws HibernateException, SQLException { 76 | try { 77 | final String json = value == null 78 | ? null 79 | : MAPPER.writeValueAsString(value); 80 | // otherwise PostgreSQL won't recognize the type 81 | PGobject pgo = new PGobject(); 82 | pgo.setType(JSONB_TYPE); 83 | pgo.setValue(json); 84 | st.setObject(index, pgo); 85 | } catch (JsonProcessingException ex) { 86 | throw new HibernateException(ex); 87 | } 88 | } 89 | 90 | @Override 91 | protected Object deepCopyValue(Object value) { 92 | return value; 93 | } 94 | 95 | @Override 96 | public void setParameterValues(Properties parameters) { 97 | final String clazz = (String) parameters.get(CLASS); 98 | try { 99 | returnedClass = ReflectHelper.classForName(clazz); 100 | } catch (ClassNotFoundException e) { 101 | throw new IllegalArgumentException("Class: " + clazz 102 | + " is not a known class type."); 103 | } 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/com/github/pires/example/hibernate/user/types/MutableUserType.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 3 | * use this file except in compliance with the License. You may obtain a copy of 4 | * the License at 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | * License for the specific language governing permissions and limitations under 11 | * the License. 12 | */ 13 | package com.github.pires.example.hibernate.user.types; 14 | 15 | import java.io.Serializable; 16 | import org.hibernate.HibernateException; 17 | import org.hibernate.type.SerializationException; 18 | import org.hibernate.usertype.UserType; 19 | 20 | /** 21 | * A skeleton Hibernate {@link UserType}. Assumes, by default, that the return 22 | * type is mutable. Subtypes whose {@code deepCopy} implementation returns a 23 | * non-serializable object must override 24 | * {@link #disassemble(Object)}. 25 | */ 26 | public abstract class MutableUserType implements UserType { 27 | 28 | @Override 29 | public boolean isMutable() { 30 | return true; 31 | } 32 | 33 | @Override 34 | public boolean equals(Object x, Object y) throws HibernateException { 35 | if (x == y) { 36 | return true; 37 | } 38 | if ((x == null) || (y == null)) { 39 | return false; 40 | } 41 | return x.equals(y); 42 | } 43 | 44 | @Override 45 | public int hashCode(Object x) throws HibernateException { 46 | assert (x != null); 47 | return x.hashCode(); 48 | } 49 | 50 | @Override 51 | public Object assemble(Serializable cached, Object owner) 52 | throws HibernateException { 53 | // also safe for mutable objects 54 | return deepCopy(cached); 55 | } 56 | 57 | /** 58 | * Disassembles the object in preparation for serialization. 59 | * See {@link org.hibernate.usertype.UserType#disassemble(java.lang.Object)}. 60 | *

61 | * Expects {@link #deepCopy(Object)} to return a {@code Serializable}. 62 | * Subtypes whose {@code deepCopy} implementation returns a 63 | * non-serializable object must override this method. 64 | */ 65 | @Override 66 | public Serializable disassemble(Object value) throws HibernateException { 67 | // also safe for mutable objects 68 | Object deepCopy = deepCopy(value); 69 | 70 | if (!(deepCopy instanceof Serializable)) { 71 | throw new SerializationException( 72 | String.format("deepCopy of %s is not serializable", value), null); 73 | } 74 | 75 | return (Serializable) deepCopy; 76 | } 77 | 78 | @Override 79 | public Object replace(Object original, Object target, Object owner) 80 | throws HibernateException { 81 | // also safe for mutable objects 82 | return deepCopy(original); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/com/github/pires/example/model/JSONBDocument.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 3 | * use this file except in compliance with the License. You may obtain a copy of 4 | * the License at 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | * License for the specific language governing permissions and limitations under 11 | * the License. 12 | */ 13 | package com.github.pires.example.model; 14 | 15 | /** 16 | * Simple object to test JSONB. 17 | */ 18 | public class JSONBDocument { 19 | 20 | private long created; 21 | private String description; 22 | 23 | public JSONBDocument() { 24 | } 25 | 26 | public long getCreated() { 27 | return created; 28 | } 29 | 30 | public JSONBDocument created(final long created) { 31 | this.created = created; 32 | return this; 33 | } 34 | 35 | public String getDescription() { 36 | return description; 37 | } 38 | 39 | public JSONBDocument description(final String description) { 40 | this.description = description; 41 | return this; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/github/pires/example/model/JSONBEntity.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 3 | * use this file except in compliance with the License. You may obtain a copy of 4 | * the License at 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | * License for the specific language governing permissions and limitations under 11 | * the License. 12 | */ 13 | package com.github.pires.example.model; 14 | 15 | import com.github.pires.example.hibernate.user.types.JSONBUserType; 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | import java.util.UUID; 19 | import static java.util.UUID.randomUUID; 20 | import javax.persistence.Entity; 21 | import javax.persistence.Id; 22 | import javax.persistence.Table; 23 | import javax.persistence.Version; 24 | import org.hibernate.annotations.Parameter; 25 | import org.hibernate.annotations.Type; 26 | import org.hibernate.annotations.TypeDef; 27 | 28 | @TypeDef(name = "jsonb", typeClass = JSONBUserType.class, parameters = { 29 | @Parameter(name = JSONBUserType.CLASS, 30 | value = "com.github.pires.example.model.JSONBEntity")}) 31 | @Entity 32 | @Table(name = "JSONB_ENTITIES") 33 | public class JSONBEntity { 34 | 35 | @Id 36 | @Type(type = "pg-uuid") 37 | private UUID id; 38 | 39 | @Version 40 | private Long version; 41 | 42 | @Type(type = "jsonb") 43 | private List documents; 44 | 45 | public JSONBEntity() { 46 | this.id = randomUUID(); 47 | documents = new ArrayList<>(); 48 | } 49 | 50 | public UUID getId() { 51 | return id; 52 | } 53 | 54 | public void setId(UUID id) { 55 | this.id = id; 56 | } 57 | 58 | public Long getVersion() { 59 | return version; 60 | } 61 | 62 | public void setVersion(Long version) { 63 | this.version = version; 64 | } 65 | 66 | public List getDocuments() { 67 | return documents; 68 | } 69 | 70 | public JSONBEntity documents(final List documents) { 71 | this.documents = documents; 72 | return this; 73 | } 74 | 75 | public JSONBEntity documents(final JSONBDocument... documents) { 76 | for (JSONBDocument document : documents) { 77 | this.documents.add(document); 78 | } 79 | return this; 80 | } 81 | 82 | public JSONBEntity document(final JSONBDocument document) { 83 | this.documents.add(document); 84 | return this; 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/main/resources/hibernate_ddl_generation.properties: -------------------------------------------------------------------------------- 1 | hibernate.dialect=com.github.pires.example.hibernate.dialect.JSONBPostgreSQLDialect 2 | hibernate.connection.charSet=UTF-8 3 | hibernate.export.schema.delimiter=; 4 | -------------------------------------------------------------------------------- /src/test/java/com/github/pires/example/TestEnvironment.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 3 | * use this file except in compliance with the License. You may obtain a copy of 4 | * the License at 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | * License for the specific language governing permissions and limitations under 11 | * the License. 12 | */ 13 | package com.github.pires.example; 14 | 15 | import javax.persistence.EntityManager; 16 | import javax.persistence.EntityManagerFactory; 17 | import javax.persistence.EntityTransaction; 18 | import javax.persistence.FlushModeType; 19 | import javax.persistence.Persistence; 20 | import org.testng.annotations.AfterSuite; 21 | import org.testng.annotations.BeforeSuite; 22 | 23 | /** 24 | * Initializes our test environment. 25 | */ 26 | public final class TestEnvironment { 27 | 28 | private static EntityManagerFactory entityManagerFactory; 29 | private static EntityManager entityManager; 30 | private static EntityTransaction tx; 31 | 32 | @BeforeSuite 33 | public void bootstrap() { 34 | // provision persistence manager 35 | entityManagerFactory = Persistence.createEntityManagerFactory("test"); 36 | entityManager = entityManagerFactory.createEntityManager(); 37 | entityManager.setFlushMode(FlushModeType.COMMIT); 38 | tx = entityManager.getTransaction(); 39 | } 40 | 41 | @AfterSuite 42 | public void cleanup() { 43 | tx = null; 44 | entityManager.close(); 45 | entityManagerFactory.close(); 46 | } 47 | 48 | public static EntityManagerFactory getEntityManagerFactory() { 49 | return entityManagerFactory; 50 | } 51 | 52 | public static EntityManager getEntityManager() { 53 | return entityManager; 54 | } 55 | 56 | public static EntityTransaction getTransaction() { 57 | return tx; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/test/java/com/github/pires/example/jsonb/JSONBEntityDaoTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 3 | * use this file except in compliance with the License. You may obtain a copy of 4 | * the License at 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | * License for the specific language governing permissions and limitations under 11 | * the License. 12 | */ 13 | package com.github.pires.example.jsonb; 14 | 15 | import com.github.pires.example.dao.JSONBEntityDao; 16 | import com.github.pires.example.model.JSONBDocument; 17 | import com.github.pires.example.model.JSONBEntity; 18 | import com.github.pires.example.TestEnvironment; 19 | import java.util.List; 20 | import javax.persistence.EntityTransaction; 21 | import static org.testng.Assert.assertEquals; 22 | import static org.testng.Assert.assertFalse; 23 | import static org.testng.Assert.assertNotNull; 24 | import org.testng.annotations.AfterClass; 25 | import org.testng.annotations.BeforeClass; 26 | import org.testng.annotations.Test; 27 | 28 | /** 29 | * Tests for {@link JSONBEntityDao}. 30 | */ 31 | public class JSONBEntityDaoTest { 32 | 33 | public static final String GROUP_CREATE = "JSONBEntityDaoTest_create"; 34 | public static final String GROUP_QUERY = "JSONBEntityDaoTest_query"; 35 | public static final String GROUP_DELETE = "JSONBEntityDaoTest_delete"; 36 | 37 | private static final Long NOW = System.currentTimeMillis(); 38 | private static final String DESCRIPTION = "awesome jsonb doc persisted"; 39 | 40 | private JSONBEntityDao dao; 41 | private EntityTransaction tx; 42 | 43 | private JSONBEntity entity; 44 | 45 | @BeforeClass 46 | public void setUp() { 47 | // provision entity transaction 48 | tx = TestEnvironment.getTransaction(); 49 | 50 | // provision daos 51 | dao = new JSONBEntityDao(); 52 | dao.setEntityManager(TestEnvironment.getEntityManager()); 53 | } 54 | 55 | @Test(groups = GROUP_CREATE) 56 | public void test_creation() { 57 | tx.begin(); 58 | JSONBDocument doc = new JSONBDocument().created(NOW). 59 | description(DESCRIPTION); 60 | entity = new JSONBEntity(); 61 | entity.document(doc); 62 | dao.persist(entity); 63 | tx.commit(); 64 | assertNotNull(entity.getId()); 65 | assertNotNull(entity.getVersion()); 66 | } 67 | 68 | @Test(groups = GROUP_CREATE) 69 | public void test_creation_with_null_document() { 70 | int count = dao.count(); 71 | tx.begin(); 72 | dao.persist(new JSONBEntity()); 73 | count++; 74 | tx.commit(); 75 | assertEquals(dao.count(), count); 76 | } 77 | 78 | @Test(groups = GROUP_CREATE) 79 | public void test_creation_with_document_tree() { 80 | final JSONBDocument doc1 = new JSONBDocument().created(NOW + 1000). 81 | description("doc1"); 82 | final JSONBDocument doc2 = new JSONBDocument().created(NOW + 7000). 83 | description("doc2"); 84 | tx.begin(); 85 | entity = new JSONBEntity(); 86 | entity.documents(doc1, doc2); 87 | dao.persist(entity); 88 | tx.commit(); 89 | assertNotNull(entity.getId()); 90 | assertNotNull(entity.getVersion()); 91 | } 92 | 93 | @Test(groups = GROUP_QUERY, dependsOnGroups = GROUP_CREATE) 94 | public void test_retrieve_created() { 95 | final JSONBEntity some = dao.find(entity.getId()); 96 | assertNotNull(some); 97 | assertNotNull(some.getDocuments()); 98 | assertEquals(some.getDocuments(). 99 | size(), 2); 100 | } 101 | 102 | @Test(groups = GROUP_QUERY, dependsOnGroups = GROUP_CREATE) 103 | public void test_find_all_by_document_created() { 104 | final List entities = dao. 105 | find_all_by_document_created_between( 106 | NOW, NOW + 10000); 107 | assertNotNull(entities); 108 | assertFalse(entities.isEmpty()); 109 | } 110 | 111 | @AfterClass 112 | public void cleanUp() { 113 | tx = null; 114 | dao = null; 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /src/test/resources/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 23 | 24 | org.hibernate.jpa.HibernatePersistenceProvider 25 | com.github.pires.example.model.JSONBEntity 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # Direct log messages to stdout 2 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 3 | log4j.appender.stdout.Target=System.out 4 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 5 | log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n 6 | 7 | # Root logger option 8 | log4j.rootLogger=INFO, stdout 9 | 10 | # Hibernate logging options (INFO only shows startup messages) 11 | log4j.logger.org.hibernate=INFO 12 | 13 | # Log JDBC bind parameter runtime arguments 14 | log4j.logger.org.hibernate.type=TRACE --------------------------------------------------------------------------------