Compare commits

...

112 Commits

Author SHA1 Message Date
Edouard DUPIN
ec762cfb96 [RELEASE] Release v0.29.0 2025-04-22 14:14:54 +02:00
Edouard DUPIN
31d353de40 [FEAT] update personal email 2025-04-22 12:29:26 +02:00
21d7eace4c [FIX] cover upload API 2025-04-22 12:27:24 +02:00
b530cb629b [FEAT] add capability to use @ManyToManyLocal that is an implementation compatible with NoSql MAny-ToMany link
This feature manage to update the 2 side of the local stored of the data
2025-04-22 11:30:58 +02:00
0cf718d415 [FIX] throw in @ManyToMany when not a list instead of log a simple error 2025-04-22 11:29:04 +02:00
963f53c2ad [FEAT] add capability to load previous data befor call updaete od soem fields 2025-04-22 11:27:42 +02:00
7f5987338a [FEAT] update naming of the TableCoversGeneric to be generic 2025-04-22 11:24:19 +02:00
70a9be86b2 [FIX] timestamp check for Github test 2025-04-22 11:23:04 +02:00
11b49ea95d [FIX] the code generation does not manage corectly the field name 2025-04-19 18:55:16 +02:00
18329659c8 [FEAT] update path structure of the project 2025-04-19 17:35:42 +02:00
74278041d6 [VERSION] update dev tag version 2025-04-18 08:31:53 +02:00
d45e07450f [RELEASE] Release v0.28.4 2025-04-18 08:31:50 +02:00
8c46d9b441 [FIX] Error 403 instead of 401 when no role found in token. 2025-04-18 08:31:17 +02:00
106e64a90c [FIX] @ManyToMany auto link generation
Not functionnal di to the bijectivity model ==> need fix it
2025-04-18 08:14:36 +02:00
8a9567d730 [VERSION] update dev tag version 2025-04-15 20:37:23 +02:00
edfbc5da21 [RELEASE] Release v0.28.2 2025-04-15 20:37:20 +02:00
95a37fc7de [FIX] order the inputs in typescript generation 2025-04-15 20:36:10 +02:00
2ba7d47b10 [VERSION] update dev tag version 2025-04-14 13:43:46 +02:00
2a2599f35c [RELEASE] Release v0.28.0 2025-04-14 13:43:42 +02:00
3fba44a041 [FIX] build 2025-04-14 13:33:34 +02:00
dependabot[bot]
6d10f564eb [DEV-OPS] (dependabot) Bump org.apache.maven.plugins:maven-gpg-plugin
Bumps [org.apache.maven.plugins:maven-gpg-plugin](https://github.com/apache/maven-gpg-plugin) from 1.6 to 3.2.7.
- [Release notes](https://github.com/apache/maven-gpg-plugin/releases)
- [Commits](https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-1.6...maven-gpg-plugin-3.2.7)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-gpg-plugin
  dependency-version: 3.2.7
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-14 12:51:32 +02:00
dependabot[bot]
d608e534c3 [DEV-OPS] (dependabot) Bump dev.morphia.morphia:morphia-core
Bumps [dev.morphia.morphia:morphia-core](https://github.com/MorphiaOrg/morphia) from 2.4.15 to 2.4.16.
- [Release notes](https://github.com/MorphiaOrg/morphia/releases)
- [Changelog](https://github.com/MorphiaOrg/morphia/blob/master/CHANGELOG.md)
- [Commits](https://github.com/MorphiaOrg/morphia/compare/v2.4.15...v2.4.16)

---
updated-dependencies:
- dependency-name: dev.morphia.morphia:morphia-core
  dependency-version: 2.4.16
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-14 12:51:27 +02:00
dependabot[bot]
5a03f338c4 [DEV-OPS] (dependabot) Bump net.revelc.code.formatter:formatter-maven-plugin
Bumps net.revelc.code.formatter:formatter-maven-plugin from 2.24.1 to 2.26.0.

---
updated-dependencies:
- dependency-name: net.revelc.code.formatter:formatter-maven-plugin
  dependency-version: 2.26.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-14 12:50:39 +02:00
dependabot[bot]
bf5a0e5ed0
[DEV-OPS] (dependabot) Bump com.nimbusds:nimbus-jose-jwt
Bumps [com.nimbusds:nimbus-jose-jwt](https://bitbucket.org/connect2id/nimbus-jose-jwt) from 10.0.2 to 10.2.
- [Changelog](https://bitbucket.org/connect2id/nimbus-jose-jwt/src/master/CHANGELOG.txt)
- [Commits](https://bitbucket.org/connect2id/nimbus-jose-jwt/branches/compare/10.2..10.0.2)

---
updated-dependencies:
- dependency-name: com.nimbusds:nimbus-jose-jwt
  dependency-version: '10.2'
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-14 10:32:00 +00:00
32d7be23e9 [FEAT] update @ManyToMany Tests 2025-04-13 15:50:25 +02:00
7a7ca29081 [FIX] update of the @ManyToMany values 2025-04-13 15:49:58 +02:00
464f844eed [FEAT,API] review the manyToMany link table name to support better automatic models
- change link table name
  - nupport reverse add link, support table name with max size of 64 with hash reduce
2025-04-11 01:42:11 +02:00
401bd8318a [FEAT] add capability to select the frmat of thumbnail generate 2025-04-09 12:52:19 +02:00
05a51ad87a [FEAT] move in the correct domain org.kar -> org.atriasoft 2025-04-08 21:09:38 +02:00
6298d09828 [VERSION] update dev tag version 2025-04-08 20:41:48 +02:00
aa8330fa1a [RELEASE] Release v0.27.0 2025-04-08 20:41:45 +02:00
37629b4cb2 [FIX] doc generation 2025-04-08 20:41:00 +02:00
69f69a8113 [FIX] Double and double inclution generate a double inclusion in Typescript generation 2025-04-08 10:27:30 +02:00
dependabot[bot]
7185a34fda
[DEV-OPS] (dependabot) Bump advanced-security/maven-dependency-submission-action from 4.1.1 to 4.1.2 (#42)
Bumps
[advanced-security/maven-dependency-submission-action](https://github.com/advanced-security/maven-dependency-submission-action)
from 4.1.1 to 4.1.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/advanced-security/maven-dependency-submission-action/releases">advanced-security/maven-dependency-submission-action's
releases</a>.</em></p>
<blockquote>
<h2>v4.1.2</h2>
<h2>What's Changed</h2>
<ul>
<li>Add cycle safety for transitive dependencies by <a
href="https://github.com/juxtin"><code>@​juxtin</code></a> in <a
href="https://redirect.github.com/advanced-security/maven-dependency-submission-action/pull/103">advanced-security/maven-dependency-submission-action#103</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/juxtin"><code>@​juxtin</code></a> made
their first contribution in <a
href="https://redirect.github.com/advanced-security/maven-dependency-submission-action/pull/103">advanced-security/maven-dependency-submission-action#103</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/advanced-security/maven-dependency-submission-action/compare/v4...v4.1.2">https://github.com/advanced-security/maven-dependency-submission-action/compare/v4...v4.1.2</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="aeab9f8852"><code>aeab9f8</code></a>
Merge pull request <a
href="https://redirect.github.com/advanced-security/maven-dependency-submission-action/issues/106">#106</a>
from advanced-security/juxtin/prep-412</li>
<li><a
href="bc43a53a41"><code>bc43a53</code></a>
Update step numbers</li>
<li><a
href="8ec6a0b12e"><code>8ec6a0b</code></a>
Add note about running npm build</li>
<li><a
href="8c8c37cf51"><code>8c8c37c</code></a>
Merge branch 'main' into juxtin/prep-412</li>
<li><a
href="4ccf7bf0a3"><code>4ccf7bf</code></a>
Update version to 4.1.2 and update release instructions</li>
<li><a
href="973a8cf442"><code>973a8cf</code></a>
Merge pull request <a
href="https://redirect.github.com/advanced-security/maven-dependency-submission-action/issues/104">#104</a>
from advanced-security/juxtin/prep-412</li>
<li><a
href="48f232b0d1"><code>48f232b</code></a>
Update dist files</li>
<li><a
href="769e1e8558"><code>769e1e8</code></a>
Prepare for 4.1.2 release</li>
<li><a
href="298a804769"><code>298a804</code></a>
Upgrade version of Maven plugin</li>
<li><a
href="29fd39885e"><code>29fd398</code></a>
Merge pull request <a
href="https://redirect.github.com/advanced-security/maven-dependency-submission-action/issues/103">#103</a>
from advanced-security/juxtin/handle-cycles</li>
<li>Additional commits viewable in <a
href="4f64ddab9d...aeab9f8852">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=advanced-security/maven-dependency-submission-action&package-manager=github_actions&previous-version=4.1.1&new-version=4.1.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-08 09:06:16 +02:00
b12108ec00 [FIX] test on DateTime is ended and solution in 90% operational 2025-04-07 23:08:23 +02:00
ecc8829e8c [FEAT] develop a new RESTApi interface to be easiest and flexible 2025-04-06 23:26:46 +02:00
e7e8c48c5c [FEAT] add OffsetDateTime in the BDD interface with UTC force 2025-04-06 23:26:19 +02:00
c09c29bf8d [FEAT] add jakarta some methode to parse the date more flexible 2025-04-06 23:25:48 +02:00
16bb30b260 [FEAT] add some test for time system 2025-04-06 23:24:58 +02:00
f044473a67 [FEAT] Jackson update date serializer to be more permissive 2025-04-06 23:24:12 +02:00
1d375f8580 [VERSION] update dev tag version 2025-04-03 10:30:43 +02:00
e2d35c5060 [RELEASE] Release v0.26.4 2025-04-03 10:30:40 +02:00
9ebd37788d [FIX] ready to deploy on maven central 2025-04-03 10:29:56 +02:00
8dde49adff [FIX] ApiNotNull inplementation 2025-04-03 10:25:33 +02:00
5960bbc666 [VERSION] update dev tag version 2025-04-02 23:50:03 +02:00
afcbb4711c [RELEASE] Release v0.26.2 2025-04-02 23:50:00 +02:00
a0b22e00bd [FEAT] update for maven central 2025-04-02 23:49:28 +02:00
a49e374bd1 [DEV] remove unneeded libraries 2025-04-02 22:28:23 +02:00
2b43d348a8 [VERSION] update dev tag version 2025-04-02 21:46:56 +02:00
242e6b9261 [RELEASE] Release v0.26.0 2025-04-02 21:46:53 +02:00
c627484b49 [FIX] annotation in the common model 2025-04-02 21:42:17 +02:00
44df939873 [FEAT] add ApiNotNull to overwrite all fields possibility 2025-04-02 21:34:27 +02:00
b283f8545c [FEAT] add readable optionnal for field 2025-04-02 21:33:52 +02:00
dependabot[bot]
6e4c10724a [DEV-OPS] (dependabot) Bump org.apache.maven.plugins:maven-surefire-plugin
Bumps [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.5.2 to 3.5.3.
- [Release notes](https://github.com/apache/maven-surefire/releases)
- [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.5.2...surefire-3.5.3)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-surefire-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-02 19:39:12 +02:00
41aa78667f [FIX] some basic types generation constraint 2025-04-01 22:18:58 +02:00
f7ba570913 [FIX] foreinKeyValidator 2025-04-01 22:18:50 +02:00
55957278a0 [VERSION] update dev tag version 2025-03-30 23:28:36 +02:00
a15be78e10 [RELEASE] Release v0.25.6 2025-03-30 23:27:32 +02:00
abff1ada45 [FIX] (RestApi tool) null object are serialize as null (like browser) 2025-03-30 23:24:17 +02:00
1c769827cf [FIX] (RestApi tool) multipart file is not merged with nest parameter 2025-03-30 23:23:51 +02:00
e09de7cc7a [FEAT] remove dataJson plugin that never exist in NoSQL 2025-03-30 17:00:19 +02:00
a5b9b60294 [FEAT] rename parameter to be clear 2025-03-30 16:59:34 +02:00
288e1f8293 [FEAT] add capabilituy of rest API to manage multipart 2025-03-30 16:58:34 +02:00
d9a5f1ece2 [FEAT] configure deleted element to be missing or false in morphia 2025-03-29 20:23:42 +01:00
f77c6ce13e [FEAT] remove doucle of code 2025-03-29 20:23:04 +01:00
85754f20f8 [FIX] configuration of mongo interface 2025-03-29 20:22:44 +01:00
969bf78576 [FEAT] configure back compatibility with mongo 2025-03-29 20:21:07 +01:00
f0cf1acf8a [FIX] configure morphia that is compatible with mongo 2025-03-29 20:20:21 +01:00
7208db5bdf [FEAT] normalize Mongo and SQL 2025-03-29 20:19:55 +01:00
5b88401d48 [FIX] name of the primary key of the cover is not managed 2025-03-28 21:13:15 +01:00
d53a0719b5 [FEAT] fix 400 error in input error 2025-03-21 08:57:24 +01:00
3e7a1a5473 [VERSION] update dev tag version 2025-03-19 23:36:56 +01:00
c7338c7877 [RELEASE] Release v0.25.4 2025-03-19 23:35:39 +01:00
1007628713 [FEAT] update test 2025-03-19 23:34:24 +01:00
84a968a426 [Fix] correct @OneToMany on ObjectId 2025-03-19 19:15:24 +01:00
581c936bec [VERSION] update dev tag version 2025-03-18 23:07:41 +01:00
a6204032b5 [RELEASE] Release v0.25.2 2025-03-18 23:04:15 +01:00
e824feb8a2 [FIX] fix json serialize null in error (remove) 2025-03-18 15:27:42 +01:00
26ea70f80c [FIX] correct some generic code generation 2025-03-18 15:27:39 +01:00
6291466df0 [VERSION] update dev tag version 2025-03-17 21:59:02 +01:00
2bc7a2c5e3 [RELEASE] Release v0.25.0 2025-03-17 21:55:44 +01:00
dependabot[bot]
6c69bc63c6 [DEV-OPS] (dependabot) Bump all dependency 2025-03-17 21:13:14 +01:00
85ac72648f [FIX] not ready dependency 2025-03-16 11:48:33 +01:00
1281415c48 [DEV] integrate hybernate validator 2025-03-16 11:24:16 +01:00
dependabot[bot]
a4521853c3 [DEV-OPS] (dependabot) Bump com.twelvemonkeys.imageio:imageio-webp
Bumps com.twelvemonkeys.imageio:imageio-webp from 3.11.0 to 3.12.0.

---
updated-dependencies:
- dependency-name: com.twelvemonkeys.imageio:imageio-webp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-15 08:51:19 +01:00
dependabot[bot]
abe2dd9480 [DEV-OPS] (dependabot) Bump com.nimbusds:nimbus-jose-jwt
Bumps [com.nimbusds:nimbus-jose-jwt](https://bitbucket.org/connect2id/nimbus-jose-jwt) from 9.41.1 to 10.0.2.
- [Changelog](https://bitbucket.org/connect2id/nimbus-jose-jwt/src/master/CHANGELOG.txt)
- [Commits](https://bitbucket.org/connect2id/nimbus-jose-jwt/branches/compare/10.0.2..9.41.1)

---
updated-dependencies:
- dependency-name: com.nimbusds:nimbus-jose-jwt
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-15 08:36:46 +01:00
dependabot[bot]
b7af0b4575 [DEV-OPS] (dependabot) Bump org.hibernate.validator:hibernate-validator
Bumps [org.hibernate.validator:hibernate-validator](https://github.com/hibernate/hibernate-validator) from 8.0.1.Final to 8.0.2.Final.
- [Changelog](https://github.com/hibernate/hibernate-validator/blob/8.0.2.Final/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-validator/compare/8.0.1.Final...8.0.2.Final)

---
updated-dependencies:
- dependency-name: org.hibernate.validator:hibernate-validator
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-15 08:36:43 +01:00
dependabot[bot]
5412eadf2e [DEV-OPS] (dependabot) Bump org.apache.maven.plugins:maven-checkstyle-plugin
Bumps [org.apache.maven.plugins:maven-checkstyle-plugin](https://github.com/apache/maven-checkstyle-plugin) from 3.5.0 to 3.6.0.
- [Commits](https://github.com/apache/maven-checkstyle-plugin/compare/maven-checkstyle-plugin-3.5.0...maven-checkstyle-plugin-3.6.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-checkstyle-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-15 08:36:41 +01:00
dependabot[bot]
d5e2e0f5b3 [DEV-OPS] (dependabot) Bump org.glassfish.jersey:jersey-bom
Bumps org.glassfish.jersey:jersey-bom from 3.1.5 to 3.1.10.

---
updated-dependencies:
- dependency-name: org.glassfish.jersey:jersey-bom
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-15 08:36:38 +01:00
28ab6c49d4 [FIX] update PR title 2025-03-15 08:30:14 +01:00
260a3abd13 [FEAT] update dependabot 2025-03-15 08:25:09 +01:00
895e8c2b37 [FEAT] update versions 2025-03-15 08:18:14 +01:00
1673f1680b [FEAT] add a spotbug nullable 2025-03-15 08:06:59 +01:00
04a82250d8 [FEAT] add capability to catch ConstraintViolationException 2025-03-15 08:06:50 +01:00
0326bde209 [FEAT] understable template typing 2025-03-10 07:27:51 +01:00
c1ccaf20ec
[FEAT] rework the generation of code for typescript (#27)
This PR is not compatible with previous code...
Rename some Annotation to have a better experience

Update the generation of the Typescript object 
===================================

By default the generation of object are only the object requested

if you annotate the object with: `@ApiGenerationMode(create = true,
update = true)`

the generation will add 2 object in typescript:
  - `MyClassUpdate` that contain the object specific for `PUT` request
  - `MyClassCreate` that contain the object specific for `POST` request
  - the PATCH request are generate with `Partial<MyClassUpdate>`

If you do not generate the create or update the system will wrap the
bass by default: `MyClass`

Add support of hidding the element in the generated object
=============================================

When generate the object some field are not needed to transmit to the
client, then we add `@ApiAccessLimitation(creatable = false, updatable =
false)` that permit to remove the field when Update or Create object are
generated

It will be used to check the Input validity instead of
`@schema(readonly)` (error of implementation)


TODO:
=====

  - Some issue when request Update generation and parent does not exist
- The checker is not updated with the use of the `@ApiAccessLimitation`
decorator.


dependencies:
===========
  - Closes: https://github.com/kangaroo-and-rabbit/archidata/issues/22
2025-03-08 14:32:16 +01:00
2174d7689f [FEAT] add callback to detect and collect errors 2025-03-05 23:48:15 +01:00
15113807b3 [DOC] add some documentation 2025-03-05 23:45:08 +01:00
ffdc6c1249 [VERSION] update dev tag version 2025-02-24 12:34:45 +01:00
1b4e6ca239 [RELEASE] Release v0.24.0 2025-02-24 12:34:41 +01:00
be8a5c713a [FIX] ObjectId manyToOne and OneToMany 2025-02-17 00:16:11 +01:00
c3f03bc1e8 [VERSION] update dev tag version 2025-02-09 22:06:56 +01:00
c7eadc607d [RELEASE] Release v0.23.6 2025-02-09 22:06:53 +01:00
5f89ff7944 [DEV] add variable to permit not check if the DB exist 2025-02-09 22:05:35 +01:00
20d2d004cb [VERSION] update dev tag version 2025-02-06 07:47:20 +01:00
da3c467569 [RELEASE] Release v0.23.4 2025-02-06 07:47:17 +01:00
0c932d4e92 [FEAT] set the object enpty as identical as their parent.
this prevent react-hook-form resolver erreur, it does not support empty object
2025-02-04 21:15:10 +01:00
a400bb99b8 [FEAT] add Jwt token description to be serialize in front 2025-02-02 19:34:23 +01:00
bdc9a4ac4d [VERSION] update dev tag version 2025-01-30 21:45:29 +01:00
b0bf103195 [RELEASE] Release v0.23.2 2025-01-30 21:45:26 +01:00
d36c366ab6 [FIX] zod number gte/lte 2025-01-30 21:25:15 +01:00
cddb4dd7fe [DEV] update dev tag version 2025-01-30 10:01:58 +01:00
344 changed files with 9587 additions and 3194 deletions

View File

@ -1,48 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<classpath> <classpath>
<classpathentry including="**/*.java" kind="src" output="target/classes" path="src">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="test/src">
<attributes>
<attribute name="test" value="true"/>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry exported="true" kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"> <classpathentry exported="true" kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes> <attributes>
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry excluding="**" kind="src" output="target/test-classes" path="test/resources">
<attributes>
<attribute name="test" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="optional" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-21"> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-21">
<attributes> <attributes>
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
<attributes>
<attribute name="test" value="true"/>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="ignore_optional_problems" value="true"/>
<attribute name="m2e-apt" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" path="target/generated-sources/annotations"> <classpathentry kind="src" path="target/generated-sources/annotations">
<attributes> <attributes>
<attribute name="optional" value="true"/> <attribute name="optional" value="true"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="optional" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/testResources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
<attribute name="optional" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/classes" path="src/main">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/> <classpathentry kind="output" path="target/classes"/>
</classpath> </classpath>

View File

@ -1,12 +1,24 @@
# To get started with Dependabot version updates, you'll need to specify which ---
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2 version: 2
updates: updates:
- package-ecosystem: "" # See documentation for possible values - package-ecosystem: "github-actions"
directory: "/" # Location of package manifests directory: "/"
schedule: schedule:
interval: "weekly" interval: "daily"
time: "05:00"
timezone: "Europe/Paris"
commit-message:
prefix: "[DEV-OPS] (dependabot) "
assignees:
- HeeroYui
- package-ecosystem: "maven"
directory: "/"
schedule:
interval: "daily"
time: "06:00"
timezone: "Europe/Paris"
commit-message:
prefix: "[DEV-OPS] (dependabot) "
assignees:
- HeeroYui

View File

@ -9,6 +9,8 @@ on:
- synchronize - synchronize
- ready_for_review - ready_for_review
- reopened - reopened
- labeled
- unlabeled
jobs: jobs:
check-title: check-title:
@ -17,7 +19,7 @@ jobs:
- name: "Check title" - name: "Check title"
uses: Slashgear/action-check-pr-title@v4.3.0 uses: Slashgear/action-check-pr-title@v4.3.0
with: with:
regexp: "\\[(API,)?(API|DEV-OPS|DOC|FEAT|FIX|FIX\\-CI|STYLE)\\]( \\([A-Za-z0-9.\\-]+\\))? [A-Za-z0-9 ,.'\\-!]+$" regexp: "\\[(API,)?(API|DEV-OPS|DOC|FEAT|FIX|FIX\\-CI|STYLE)\\]( \\([A-Za-z0-9.\\-/_]+\\))? [A-Za-z0-9: ,.'\\-!/_]+$"
helpMessage: | helpMessage: |
Title of the PR MUST respect format: "[{TYPE}] clear description without typos in english" with {TYPE}: Title of the PR MUST respect format: "[{TYPE}] clear description without typos in english" with {TYPE}:
* [API] Change API that permit to access on the application (un-compatibility only). This one can specifically added with [API,{TYPE}] * [API] Change API that permit to access on the application (un-compatibility only). This one can specifically added with [API,{TYPE}]

View File

@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Set up JDK 17 - name: Set up JDK 21
uses: actions/setup-java@v4 uses: actions/setup-java@v4
with: with:
java-version: '21' java-version: '21'
@ -32,4 +32,4 @@ jobs:
# Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive # Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive
- name: Update dependency graph - name: Update dependency graph
uses: advanced-security/maven-dependency-submission-action@4f64ddab9d742a4806eeb588d238e4c311a8397d uses: advanced-security/maven-dependency-submission-action@aeab9f885293af501bae8bdfe88c589528ea5e25

View File

@ -28,4 +28,4 @@ def getEnvName(branchName) {
return "dev"; return "dev";
} }
return "other"; return "other";
} }

185
pom.xml
View File

@ -1,41 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>kangaroo-and-rabbit</groupId> <groupId>org.atria-soft</groupId>
<artifactId>archidata</artifactId> <artifactId>archidata</artifactId>
<version>0.23.0</version> <version>0.29.0</version>
<properties> <description>Wrapper to manage a simple interface for REST project to bind MySQL, SQLite or mongoDB.</description>
<java.version>21</java.version> <url>https://github.com/kangaroo-and-rabbit/archidata</url>
<maven.compiler.version>3.1</maven.compiler.version> <licenses>
<maven.compiler.source>21</maven.compiler.source> <license>
<maven.compiler.target>21</maven.compiler.target> <name>Mozilla Public License 2.0</name>
<maven.dependency.version>3.1.1</maven.dependency.version> <url>https://opensource.org/licenses/MPL-2.0</url>
<jersey.version>3.1.5</jersey.version> <distribution>repo</distribution>
<jaxb.version>2.3.1</jaxb.version> </license>
<istack.version>4.1.1</istack.version> </licenses>
</properties> <developers>
<repositories> <developer>
<repository> <id>dev1</id>
<id>gitea</id> <name>Edouard DUPIN</name>
<url>https://gitea.atria-soft.org/api/packages/kangaroo-and-rabbit/maven</url> <email>edouard.dupin@proton.me</email>
</repository> <roles>
</repositories> <role>Lead Developer</role>
<distributionManagement> </roles>
<repository> </developer>
<id>gitea</id> </developers>
<url>https://gitea.atria-soft.org/api/packages/kangaroo-and-rabbit/maven</url> <scm>
</repository> <url>https://github.com/kangaroo-and-rabbit/archidata</url>
<snapshotRepository> <connection>scm:git:git://github.com/kangaroo-and-rabbit/archidata.git</connection>
<id>gitea</id> <developerConnection>scm:git:ssh:github.com/kangaroo-and-rabbit/archidata.git</developerConnection>
<url>https://gitea.atria-soft.org/api/packages/kangaroo-and-rabbit/maven</url> </scm>
</snapshotRepository>
</distributionManagement>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.glassfish.jersey</groupId> <groupId>org.glassfish.jersey</groupId>
<artifactId>jersey-bom</artifactId> <artifactId>jersey-bom</artifactId>
<version>${jersey.version}</version> <version>4.0.0-M2</version>
<type>pom</type> <type>pom</type>
<scope>import</scope> <scope>import</scope>
</dependency> </dependency>
@ -58,13 +56,13 @@
<dependency> <dependency>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-webp</artifactId> <artifactId>imageio-webp</artifactId>
<version>3.11.0</version> <version>3.12.0</version>
</dependency> </dependency>
<!-- Decode JPEG image --> <!-- Decode JPEG image -->
<dependency> <dependency>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-jpeg</artifactId> <artifactId>imageio-jpeg</artifactId>
<version>3.11.0</version> <version>3.12.0</version>
</dependency> </dependency>
<!-- Encode file in webp --> <!-- Encode file in webp -->
<dependency> <dependency>
@ -76,7 +74,7 @@
<dependency> <dependency>
<groupId>org.apache.tika</groupId> <groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId> <artifactId>tika-core</artifactId>
<version>3.0.0-BETA2</version> <version>3.1.0</version>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.media/jersey-media-multipart --> <!-- https://mvnrepository.com/artifact/org.glassfish.jersey.media/jersey-media-multipart -->
<dependency> <dependency>
@ -96,9 +94,8 @@
<artifactId>jersey-container-grizzly2-http</artifactId> <artifactId>jersey-container-grizzly2-http</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>javax.xml.bind</groupId> <groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jaxb-api</artifactId> <artifactId>jersey-bean-validation</artifactId>
<version>${jaxb.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.glassfish.jaxb</groupId> <groupId>org.glassfish.jaxb</groupId>
@ -110,22 +107,6 @@
<artifactId>jakarta.ws.rs-api</artifactId> <artifactId>jakarta.ws.rs-api</artifactId>
<version>4.0.0</version> <version>4.0.0</version>
</dependency> </dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>${jaxb.version}</version>
</dependency>
<dependency>
<groupId>com.sun.istack</groupId>
<artifactId>istack-commons-runtime</artifactId>
<version>${istack.version}</version>
</dependency>
<!-- continu to be needed ??? -->
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<scope>test</scope>
</dependency>
<!-- Serialize and un-serialize request in JSON--> <!-- Serialize and un-serialize request in JSON-->
<dependency> <dependency>
<groupId>org.glassfish.jersey.media</groupId> <groupId>org.glassfish.jersey.media</groupId>
@ -134,18 +115,18 @@
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId> <artifactId>jackson-databind</artifactId>
<version>2.18.1</version> <version>2.18.3</version>
</dependency> </dependency>
<!-- encode output in CSV --> <!-- encode output in CSV -->
<dependency> <dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId> <groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-csv</artifactId> <artifactId>jackson-dataformat-csv</artifactId>
<version>2.18.1</version> <version>2.18.3</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.datatype</groupId> <groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId> <artifactId>jackson-datatype-jsr310</artifactId>
<version>2.18.1</version> <version>2.18.3</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>jakarta.servlet</groupId> <groupId>jakarta.servlet</groupId>
@ -157,18 +138,18 @@
<dependency> <dependency>
<groupId>com.mysql</groupId> <groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId> <artifactId>mysql-connector-j</artifactId>
<version>9.0.0</version> <version>9.2.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.xerial</groupId> <groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId> <artifactId>sqlite-jdbc</artifactId>
<version>3.46.1.0</version> <version>3.49.1.0</version>
</dependency> </dependency>
<!-- Interface for JWT token --> <!-- Interface for JWT token -->
<dependency> <dependency>
<groupId>com.nimbusds</groupId> <groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId> <artifactId>nimbus-jose-jwt</artifactId>
<version>9.41.1</version> <version>10.2</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>jakarta.persistence</groupId> <groupId>jakarta.persistence</groupId>
@ -179,37 +160,34 @@
<dependency> <dependency>
<groupId>io.swagger.core.v3</groupId> <groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-jaxrs2-jakarta</artifactId> <artifactId>swagger-jaxrs2-jakarta</artifactId>
<version>2.2.23</version> <version>2.2.29</version>
</dependency> </dependency>
<!-- spotbug tooling --> <!-- spotbug tooling -->
<dependency> <dependency>
<groupId>com.github.spotbugs</groupId> <groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-annotations</artifactId> <artifactId>spotbugs-annotations</artifactId>
<version>4.8.6</version> <version>4.9.3</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<!-- Morphia --> <!-- Morphia -->
<dependency> <dependency>
<groupId>dev.morphia.morphia</groupId> <groupId>dev.morphia.morphia</groupId>
<artifactId>morphia-core</artifactId> <artifactId>morphia-core</artifactId>
<version>2.3.0</version> <version>2.4.16</version>
</dependency> </dependency>
<!-- MongoDB Java Driver --> <!-- MongoDB Java Driver -->
<dependency> <dependency>
<groupId>org.mongodb</groupId> <groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId> <artifactId>mongodb-driver-sync</artifactId>
<version>4.3.0</version> <!--<version>5.3.1</version>-->
<!--Morphia 2.4.x does not support version upper than 4.x-->
<version>4.11.5</version>
</dependency> </dependency>
<!-- Bean Validation (JSR 303 / 380) --> <!-- Bean Validation (JSR 303 / 380) -->
<dependency> <dependency>
<groupId>org.hibernate.validator</groupId> <groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId> <artifactId>hibernate-validator</artifactId>
<version>7.0.0.Final</version> <version>9.0.0.CR1</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency> </dependency>
<!-- <!--
************************************************************ ************************************************************
@ -219,60 +197,59 @@
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId> <artifactId>junit-jupiter-api</artifactId>
<version>5.11.0</version> <version>5.12.1</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId> <artifactId>junit-jupiter-engine</artifactId>
<version>5.11.0</version> <version>5.12.1</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>net.revelc.code.formatter</groupId> <groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId> <artifactId>formatter-maven-plugin</artifactId>
<version>2.24.1</version> <version>2.26.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId> <artifactId>maven-checkstyle-plugin</artifactId>
<version>3.5.0</version> <version>3.6.0</version>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>
<sourceDirectory>src</sourceDirectory> <sourceDirectory>src/main</sourceDirectory>
<resources> <resources>
<resource> <resource>
<directory>${basedir}/src/resources</directory> <directory>${basedir}/src/resources</directory>
</resource> </resource>
</resources> </resources>
<testSourceDirectory>test/src</testSourceDirectory> <testSourceDirectory>src/test</testSourceDirectory>
<testResources> <testResources>
<testResource> <testResource>
<directory>${basedir}/test/resources</directory> <directory>${basedir}/src/testResources</directory>
</testResource> </testResource>
</testResources> </testResources>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.version}</version> <version>3.14.0</version>
<configuration> <configuration>
<source>${maven.compiler.source}</source> <source>21</source>
<target>${maven.compiler.target}</target> <target>21</target>
<!--<encoding>${project.build.sourceEncoding}</encoding>-->
</configuration> </configuration>
</plugin> </plugin>
<!-- Create the source bundle --> <!-- Create the source bundle -->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId> <artifactId>maven-source-plugin</artifactId>
<version>3.3.0</version> <version>3.3.1</version>
<executions> <executions>
<execution> <execution>
<id>attach-sources</id> <id>attach-sources</id>
<goals> <goals>
<goal>jar</goal> <goal>jar-no-fork</goal>
</goals> </goals>
</execution> </execution>
</executions> </executions>
@ -295,7 +272,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version> <version>3.5.3</version>
</plugin> </plugin>
<plugin> <plugin>
<artifactId>maven-assembly-plugin</artifactId> <artifactId>maven-assembly-plugin</artifactId>
@ -314,17 +291,21 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version> <version>3.3.0</version>
<configuration> <executions>
<show>private</show> <execution>
<nohelp>true</nohelp> <id>attach-javadocs</id>
</configuration> <goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin> </plugin>
<!-- Check the style of the code --> <!-- Check the style of the code -->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId> <artifactId>maven-checkstyle-plugin</artifactId>
<version>3.3.1</version> <version>3.6.0</version>
<configuration> <configuration>
<configLocation>CheckStyle.xml</configLocation> <configLocation>CheckStyle.xml</configLocation>
<consoleOutput>true</consoleOutput> <consoleOutput>true</consoleOutput>
@ -336,7 +317,7 @@
<plugin> <plugin>
<groupId>net.revelc.code.formatter</groupId> <groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId> <artifactId>formatter-maven-plugin</artifactId>
<version>2.23.0</version> <version>2.26.0</version>
<configuration> <configuration>
<encoding>UTF-8</encoding> <encoding>UTF-8</encoding>
<lineEnding>LF</lineEnding> <lineEnding>LF</lineEnding>
@ -377,19 +358,29 @@
--> -->
</configuration> </configuration>
</plugin> </plugin>
</plugins>
</build>
<!-- Generate Java-docs As Part Of Project Reports -->
<reporting>
<plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-gpg-plugin</artifactId>
<version>3.2.0</version> <version>3.2.7</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId>
<version>0.7.0</version>
<extensions>true</extensions>
<configuration> <configuration>
<show>public</show> <publishingServerId>central</publishingServerId>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>
</reporting> </build>
</project> </project>

311
pom.xml.versionsBackup Normal file
View File

@ -0,0 +1,311 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>kangaroo-and-rabbit</groupId>
<artifactId>archidata</artifactId>
<version>0.6.1</version>
<properties>
<maven.compiler.version>3.1</maven.compiler.version>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<maven.dependency.version>3.1.1</maven.dependency.version>
<jersey.version>3.1.5</jersey.version>
<jaxb.version>2.3.1</jaxb.version>
<istack.version>4.1.1</istack.version>
</properties>
<repositories>
<repository>
<id>gitea</id>
<url>https://gitea.atria-soft.org/api/packages/kangaroo-and-rabbit/maven</url>
</repository>
</repositories>
<distributionManagement>
<repository>
<id>gitea</id>
<url>https://gitea.atria-soft.org/api/packages/kangaroo-and-rabbit/maven</url>
</repository>
<snapshotRepository>
<id>gitea</id>
<url>https://gitea.atria-soft.org/api/packages/kangaroo-and-rabbit/maven</url>
</snapshotRepository>
</distributionManagement>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey</groupId>
<artifactId>jersey-bom</artifactId>
<version>${jersey.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.9</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.media/jersey-media-multipart -->
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-http</artifactId>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>${jaxb.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>4.0.4</version>
</dependency>
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>${jaxb.version}</version>
</dependency>
<dependency>
<groupId>com.sun.istack</groupId>
<artifactId>istack-commons-runtime</artifactId>
<version>${istack.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-csv</artifactId>
<version>2.16.1</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.1.0-M1</version>
<scope>provided</scope>
</dependency>
<!-- Interface for My-sql & sqlite DB -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.2.0</version>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.40.0.0</version>
</dependency>
<!-- Interface for JWT token -->
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>9.37.1</version>
</dependency>
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<version>3.2.0-M1</version>
</dependency>
<!-- Swagger dependencies -->
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-jaxrs2-jakarta</artifactId>
<version>2.1.10</version>
</dependency>
<!--
************************************************************
** TEST dependency **
************************************************************
-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.10.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>2.23.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.3.1</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<testSourceDirectory>test/src</testSourceDirectory>
<testResources>
<testResource>
<directory>${basedir}/test/resources</directory>
</testResource>
</testResources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.version}</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<!--<encoding>${project.build.sourceEncoding}</encoding>-->
</configuration>
</plugin>
<!-- Create the source bundle -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- For dependabot plugin -->
<plugin>
<groupId>org.apache.servicemix.tooling</groupId>
<artifactId>depends-maven-plugin</artifactId>
<version>1.5.0</version>
<executions>
<execution>
<id>generate-depends-file</id>
<goals>
<goal>generate-depends-file</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- junit results -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>fully.qualified.MainClass</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
<!-- Java-doc generation for stand-alone site -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<show>private</show>
<nohelp>true</nohelp>
</configuration>
</plugin>
<!-- Check the style of the code -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.3.1</version>
<configuration>
<configLocation>CheckStyle.xml</configLocation>
<consoleOutput>true</consoleOutput>
<failOnViolation>true</failOnViolation>
<failsOnError>true</failsOnError>
<includeTestSourceDirectory>true</includeTestSourceDirectory>
</configuration>
</plugin>
<plugin>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>2.23.0</version>
<configuration>
<encoding>UTF-8</encoding>
<lineEnding>LF</lineEnding>
<configFile>Formatter.xml</configFile>
<directories>
<directory>src/</directory>
<directory>test/src</directory>
</directories>
<includes>
<include>**/*.java</include>
</includes>
<excludes>
<exclude>module-info.java</exclude>
</excludes>
</configuration>
<executions>
<execution>
<goals>
<goal>validate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<!-- Generate Java-docs As Part Of Project Reports -->
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<show>public</show>
</configuration>
</plugin>
</plugins>
</reporting>
</project>

View File

@ -7,18 +7,18 @@ import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.jackson.JacksonFeature; import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.media.multipart.MultiPartFeature; import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.server.ResourceConfig;
import org.kar.archidata.GlobalConfiguration; import org.atriasoft.archidata.GlobalConfiguration;
import org.kar.archidata.UpdateJwtPublicKey; import org.atriasoft.archidata.UpdateJwtPublicKey;
import org.kar.archidata.api.DataResource; import org.atriasoft.archidata.api.DataResource;
import org.kar.archidata.catcher.ExceptionCatcher; import org.atriasoft.archidata.catcher.ExceptionCatcher;
import org.kar.archidata.catcher.FailExceptionCatcher; import org.atriasoft.archidata.catcher.FailExceptionCatcher;
import org.kar.archidata.catcher.InputExceptionCatcher; import org.atriasoft.archidata.catcher.InputExceptionCatcher;
import org.kar.archidata.catcher.SystemExceptionCatcher; import org.atriasoft.archidata.catcher.SystemExceptionCatcher;
import org.kar.archidata.db.DBConfig; import org.atriasoft.archidata.db.DBConfig;
import org.kar.archidata.filter.CORSFilter; import org.atriasoft.archidata.filter.CORSFilter;
import org.kar.archidata.filter.OptionFilter; import org.atriasoft.archidata.filter.OptionFilter;
import org.kar.archidata.migration.MigrationEngine; import org.atriasoft.archidata.migration.MigrationEngine;
import org.kar.archidata.tools.ConfigBaseVariable; import org.atriasoft.archidata.tools.ConfigBaseVariable;
import sample.archidata.basic.api.Front; import sample.archidata.basic.api.Front;
import sample.archidata.basic.api.HealthCheck; import sample.archidata.basic.api.HealthCheck;
import sample.archidata.basic.api.MediaResource; import sample.archidata.basic.api.MediaResource;

View File

@ -3,9 +3,9 @@ package sample.archidata.basic;
import java.util.List; import java.util.List;
import org.kar.archidata.api.DataResource; import org.atriasoft.archidata.api.DataResource;
import org.kar.archidata.dataAccess.DataFactoryTsApi; import org.atriasoft.archidata.dataAccess.DataFactoryTsApi;
import org.kar.archidata.tools.ConfigBaseVariable; import org.atriasoft.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@ -8,15 +8,15 @@ import java.util.UUID;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam; import org.glassfish.jersey.media.multipart.FormDataParam;
import org.kar.archidata.annotation.AsyncType; import org.atriasoft.archidata.annotation.AsyncType;
import org.kar.archidata.annotation.TypeScriptProgress; import org.atriasoft.archidata.annotation.TypeScriptProgress;
import org.kar.archidata.api.DataResource; import org.atriasoft.archidata.api.DataResource;
import org.kar.archidata.dataAccess.DataAccess; import org.atriasoft.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.addOn.AddOnDataJson; import org.atriasoft.archidata.dataAccess.addOn.AddOnDataJson;
import org.kar.archidata.exception.FailException; import org.atriasoft.archidata.exception.FailException;
import org.kar.archidata.exception.InputException; import org.atriasoft.archidata.exception.InputException;
import org.kar.archidata.model.Data; import org.atriasoft.archidata.model.Data;
import org.kar.archidata.tools.DataTools; import org.atriasoft.archidata.tools.DataTools;
import sample.archidata.basic.model.MyModel; import sample.archidata.basic.model.MyModel;
import sample.archidata.basic.model.Season; import sample.archidata.basic.model.Season;
import sample.archidata.basic.model.Series; import sample.archidata.basic.model.Series;

View File

@ -2,7 +2,7 @@ package sample.archidata.basic.migration;
import java.util.List; import java.util.List;
import org.kar.archidata.migration.MigrationSqlStep; import org.atriasoft.archidata.migration.MigrationSqlStep;
import sample.archidata.basic.model.MyModel; import sample.archidata.basic.model.MyModel;
public class Initialization extends MigrationSqlStep { public class Initialization extends MigrationSqlStep {

View File

@ -3,9 +3,9 @@ package sample.archidata.basic.model;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import org.kar.archidata.annotation.DataJson; import org.atriasoft.archidata.annotation.DataJson;
import org.kar.archidata.model.Data; import org.atriasoft.archidata.model.Data;
import org.kar.archidata.model.GenericDataSoftDelete; import org.atriasoft.archidata.model.GenericDataSoftDelete;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;

View File

@ -1,7 +1,7 @@
package org.kar.archidata; package org.atriasoft.archidata;
import org.kar.archidata.tools.ConfigBaseVariable; import org.atriasoft.archidata.tools.ConfigBaseVariable;
import org.kar.archidata.tools.JWTWrapper; import org.atriasoft.archidata.tools.JWTWrapper;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@ -1,18 +1,19 @@
package org.kar.archidata.annotation; package org.atriasoft.archidata.annotation;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Parameter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.kar.archidata.annotation.checker.Checker; import org.atriasoft.archidata.annotation.checker.Checker;
import org.kar.archidata.annotation.checker.CollectionItemNotNull; import org.atriasoft.archidata.annotation.checker.CollectionItemNotNull;
import org.kar.archidata.annotation.checker.CollectionItemUnique; import org.atriasoft.archidata.annotation.checker.CollectionItemUnique;
import org.kar.archidata.annotation.checker.CollectionNotEmpty; import org.atriasoft.archidata.annotation.checker.CollectionNotEmpty;
import org.kar.archidata.dataAccess.QueryOptions; import org.atriasoft.archidata.dataAccess.QueryOptions;
import org.kar.archidata.dataAccess.options.OptionRenameColumn; import org.atriasoft.archidata.dataAccess.options.OptionRenameColumn;
import org.kar.archidata.dataAccess.options.OverrideTableName; import org.atriasoft.archidata.dataAccess.options.OverrideTableName;
import org.kar.archidata.exception.DataAccessException; import org.atriasoft.archidata.exception.DataAccessException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -41,6 +42,24 @@ import jakarta.ws.rs.DefaultValue;
public class AnnotationTools { public class AnnotationTools {
static final Logger LOGGER = LoggerFactory.getLogger(AnnotationTools.class); static final Logger LOGGER = LoggerFactory.getLogger(AnnotationTools.class);
public static <TYPE extends Annotation> TYPE get(final Parameter param, final Class<TYPE> clazz) {
final TYPE[] annotations = param.getDeclaredAnnotationsByType(clazz);
if (annotations.length == 0) {
return null;
}
return annotations[0];
}
public static <TYPE extends Annotation> TYPE[] gets(final Parameter param, final Class<TYPE> clazz) {
final TYPE[] annotations = param.getDeclaredAnnotationsByType(clazz);
if (annotations.length == 0) {
return null;
}
return annotations;
}
public static <TYPE extends Annotation> TYPE get(final Field element, final Class<TYPE> clazz) { public static <TYPE extends Annotation> TYPE get(final Field element, final Class<TYPE> clazz) {
final TYPE[] annotations = element.getDeclaredAnnotationsByType(clazz); final TYPE[] annotations = element.getDeclaredAnnotationsByType(clazz);
@ -59,6 +78,24 @@ public class AnnotationTools {
return annotations; return annotations;
} }
public static <TYPE extends Annotation> TYPE get(final Class<?> classObject, final Class<TYPE> clazz) {
final TYPE[] annotations = classObject.getDeclaredAnnotationsByType(clazz);
if (annotations.length == 0) {
return null;
}
return annotations[0];
}
public static <TYPE extends Annotation> TYPE[] gets(final Class<?> classObject, final Class<TYPE> clazz) {
final TYPE[] annotations = classObject.getDeclaredAnnotationsByType(clazz);
if (annotations.length == 0) {
return null;
}
return annotations;
}
// For SQL declaration table Name // For SQL declaration table Name
public static String getTableName(final Class<?> clazz, final QueryOptions options) throws DataAccessException { public static String getTableName(final Class<?> clazz, final QueryOptions options) throws DataAccessException {
if (options != null) { if (options != null) {
@ -121,14 +158,6 @@ public class AnnotationTools {
return get(element, CollectionNotEmpty.class); return get(element, CollectionNotEmpty.class);
} }
public static boolean getSchemaReadOnly(final Field element) {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Schema.class);
if (annotation.length == 0) {
return false;
}
return ((Schema) annotation[0]).readOnly();
}
public static String getSchemaExample(final Class<?> element) { public static String getSchemaExample(final Class<?> element) {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Schema.class); final Annotation[] annotation = element.getDeclaredAnnotationsByType(Schema.class);
if (annotation.length == 0) { if (annotation.length == 0) {
@ -137,14 +166,6 @@ public class AnnotationTools {
return ((Schema) annotation[0]).example(); return ((Schema) annotation[0]).example();
} }
public static boolean getNoWriteSpecificMode(final Class<?> element) {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(NoWriteSpecificMode.class);
if (annotation.length == 0) {
return false;
}
return true;
}
public static String getSchemaDescription(final Class<?> element) { public static String getSchemaDescription(final Class<?> element) {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Schema.class); final Annotation[] annotation = element.getDeclaredAnnotationsByType(Schema.class);
if (annotation.length == 0) { if (annotation.length == 0) {
@ -428,7 +449,7 @@ public class AnnotationTools {
if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) { if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
continue; continue;
} }
if (AnnotationTools.getFieldNameRaw(field).equals(name)) { if (field.getName().equals(name)) {
return true; return true;
} }
} }

View File

@ -0,0 +1,45 @@
package org.atriasoft.archidata.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The CreationTimestamp annotation is used to automatically set the creation
* date of an object in the database. This annotation ensures that the field
* marked with @CreationTimestamp is populated with the current timestamp
* when the object is first created.
*
* <p>Usage:
* - Target: This annotation can be applied to fields within a class.
* - Retention: The annotation is retained at runtime, allowing it to be
* processed by frameworks or libraries that handle data persistence logic.
*
* <p>Behavior:
* - When a field is annotated with @CreationTimestamp, it will automatically
* be set to the current date and time when the object is inserted into the
* database.
* - This annotation is typically used in conjunction with other annotations
* such as @Column to define database column properties.
*
* <p>Example:
* <pre>{@code
* public class MyEntity {
* @DataNotRead
* @CreationTimestamp
* @Column(nullable = false, insertable = false, updatable = false)
* @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX") // optional depend on the configuration
* @Nullable
* public Date createdAt = null;
* }
* }</pre>
*
* In this example, the createdAt field will be automatically set to the
* current timestamp when a new User object is created in the database.
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CreationTimestamp {
}

View File

@ -0,0 +1,46 @@
package org.atriasoft.archidata.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The DataDeleted annotation is used to manage a boolean variable that marks
* an object as 'deleted' in the database. This annotation helps in soft deletion
* by excluding marked objects from being automatically retrieved unless
* explicitly specified.
*
* <p>Usage:
* - Target: This annotation can be applied to fields within a class.
* - Retention: The annotation is retained at runtime, allowing it to be
* processed by frameworks or libraries that handle data retrieval logic.
*
* <p>Behavior:
* - When a field is annotated with @DataDeleted, it will not be included in the
* default data retrieval process from the database if its value is false.
* - To override this behavior and access deleted items, the query must include
* the option AccessDeletedItems.
*
* <p>Example:
* <pre>{@code
* public class MyEntity {
* public String username;
*
* @DataDeleted
* @DataNotRead
* @Column(nullable = false)
* @DefaultValue("'0'")
* public Boolean deleted = null;
* }
* }</pre>
*
* In this example, objects with `deleted` set to true will not be retrieved
* by default. To include them in the query results, the AccessDeletedItems
* option must be specified in the query.
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataDeleted {
}

View File

@ -1,4 +1,4 @@
package org.kar.archidata.annotation; package org.atriasoft.archidata.annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;

View File

@ -0,0 +1,47 @@
package org.atriasoft.archidata.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The DataJson annotation is used to convert fields or classes to JSON format
* for storage in a database. This annotation allows storing complex data types
* such as lists, maps, and other objects in SQL databases as JSON or STRING
* (for SQLite).
*
* <p>Usage:
* - Target: This annotation can be applied to both fields and classes.
* - Retention: The annotation is retained at runtime, allowing it to be
* processed by frameworks or libraries that handle data persistence logic.
*
* <p>Behavior:
* - When applied to a field or class, the DataJson annotation enables the
* conversion of the annotated element to JSON format before storing it in
* the database.
* - This is particularly useful in SQL databases where only basic data types
* (char, short, int, long, float, string, timestamp) can be stored directly.
* The DataJson annotation makes it possible to store complex data structures
* by converting them to JSON.
*
* <p>Attributes:
* - targetEntity: Specifies the target entity class to which the JSON data
* should be mapped. Defaults auto-detect if not specified.
*
* <p>Example:
* <pre>{@code
* public class User {
* @DataJson
* public Map<String, Object> additionalData;
* }
* }</pre>
*
* In this example, the additionalData field can store complex data structures
* as JSON in the database.
*/
@Target({ ElementType.TYPE, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface DataJson {
Class<?> targetEntity() default Void.class;
}

View File

@ -0,0 +1,43 @@
package org.atriasoft.archidata.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The DataNotRead annotation is used to mark fields in a class that should not
* be automatically read from the database. This annotation helps in optimizing
* data retrieval by excluding certain fields from being fetched unless
* explicitly specified.
*
* <p>Usage:
* - Target: This annotation can be applied to fields within a class.
* - Retention: The annotation is retained at runtime, allowing it to be
* processed by frameworks or libraries that handle data retrieval logic.
*
* <p>Behavior:
* - When a field is annotated with @DataNotRead, it will not be included in the
* default data retrieval process from the database.
* - To override this behavior and read all columns, including those marked with
* `@DataNotRead`, the query must include the option ReadAllColumn.
*
* <p>Example:
* <pre>{@code
* public class MyEntity {
* public String username;
*
* @DataNotRead
* private String sensitiveData;
* }
* }</pre>
*
* In this example, the sensitiveData field will not be read from the database
* by default. To include it in the query results, the ReadAllColumn option must
* be specified in the query.
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataNotRead {
}

View File

@ -0,0 +1,27 @@
package org.atriasoft.archidata.annotation;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* In NoSql entity the relation is stored in the 2 part of the entity,
* then it is needed to define the field that store the relation data value in the remote elements.
*/
@Retention(RUNTIME)
@Target({ FIELD, METHOD })
public @interface ManyToManyLocal {
/**
* The entity class that is the target of the
* association.
*/
Class<?> targetEntity();
/**
* The field that owns the revert value. empty if the relationship is unidirectional.
*/
String remoteField() default "";
}

View File

@ -0,0 +1,44 @@
package org.atriasoft.archidata.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The UpdateTimestamp annotation is used to automatically update the timestamp
* of an object in the database whenever it is modified. This annotation ensures
* that the field marked with @UpdateTimestamp is set to the current timestamp
* each time the object is updated.
*
* <p>Usage:
* - Target: This annotation can be applied to fields within a class.
* - Retention: The annotation is retained at runtime, allowing it to be
* processed by frameworks or libraries that handle data persistence logic.
*
* <p>Behavior:
* - When a field is annotated with @UpdateTimestamp, it will automatically
* be updated to the current date and time whenever the object is modified
* in the database.
* - This annotation is typically used in conjunction with other annotations
* such as @Column to define database column properties.
*
* <p>Example:
* <pre>{@code
* public class MyEntity {
* @UpdateTimestamp
* @Column(nullable = false, insertable = false, updatable = false)
* @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
* @Nullable
* public Date updatedAt = null;
* }
* }</pre>
*
* In this example, the updatedAt field will be automatically set to the
* current timestamp whenever the User object is modified in the database.
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UpdateTimestamp {
}

View File

@ -1,4 +1,4 @@
package org.kar.archidata.annotation.addOn; package org.atriasoft.archidata.annotation.addOn;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;

View File

@ -0,0 +1,26 @@
package org.atriasoft.archidata.annotation.apiGenerator;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Retention(RUNTIME)
@Target(FIELD)
public @interface ApiAccessLimitation {
/**
* (Optional) The field is accessible in read (GET)
*/
boolean readable() default true;
/**
* (Optional) The field is accessible in creation (POST)
*/
boolean creatable() default true;
/**
* (Optional) The field is accessible in update mode (PUT, PATCH)
*/
boolean updatable() default true;
}

View File

@ -1,4 +1,4 @@
package org.kar.archidata.annotation; package org.atriasoft.archidata.annotation.apiGenerator;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
@ -8,7 +8,7 @@ import java.lang.annotation.Target;
/** In case of the update parameter with String input to detect null element. */ /** In case of the update parameter with String input to detect null element. */
@Target({ ElementType.PARAMETER, ElementType.METHOD }) @Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface AsyncType { public @interface ApiAsyncType {
// Possible class values. // Possible class values.
Class<?>[] value(); Class<?>[] value();

View File

@ -0,0 +1,57 @@
package org.atriasoft.archidata.annotation.apiGenerator;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The ApiGenerationMode annotation is used to indicate the generation mode for
* API operations when producing code for other languages. This annotation is
* particularly useful in code generators for client libraries where specific
* data structures for read, create, and update operations may or may not be needed.
*
* <p>Usage:
* - Target: This annotation can be applied to class types.
* - Retention: The annotation is retained at runtime, allowing it to be
* processed by frameworks or libraries that handle code generation logic.
*
* <p>Behavior:
* - When applied to a class, the ApiGenerationMode annotation specifies
* which API operations (read, create, update) should generate specific
* data structures. This can simplify the generated code by avoiding the
* creation of unnecessary structures.
*
* <p>Example:
* <pre>{@code
* @ApiGenerationMode(creatable=false, updatable=false)
* public class User {
* public String username;
* public String email;
* }
* }</pre>
*
* In this example, the User class will not generate separate data structures
* for create and update operations in the client code, only for read operations.
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiGenerationMode {
/**
* (Optional) Enable the generation of specific code for read access
* (generate object: MyClass).
*/
boolean read() default true;
/**
* (Optional) Enable the generation of specific code for create access
* (generate object: MyClassCreate).
*/
boolean create() default false;
/**
* (Optional) Enable the generation of specific code for update access
* (generate object: MyClassUpdate).
*/
boolean update() default false;
}

View File

@ -0,0 +1,80 @@
package org.atriasoft.archidata.annotation.apiGenerator;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The FormDataOptional annotation is used to indicate that a form data parameter
* is optional when generating client code. By default, form data parameters are
* required, but this annotation allows them to be optional, enabling the creation
* of polymorphic APIs.
*
* <p>Usage:
* - Target: This annotation can be applied to method parameters.
* - Retention: The annotation is retained at runtime, allowing it to be
* processed by frameworks or libraries that handle code generation logic.
*
* <p>Behavior:
* - When applied to a parameter, the FormDataOptional annotation specifies that
* the parameter is optional in the generated client code. This allows for
* more flexible API designs where certain inputs can be omitted.
*
* <p>Example:
*
* {@code
* public class AlbumService {
*
* @POST
* @Path("{id}/cover")
* @RolesAllowed("ADMIN")
* @Consumes({ MediaType.MULTIPART_FORM_DATA })
* @Operation(description = "Add a cover on a specific album")
* @TypeScriptProgress
* public Album uploadCover(@PathParam("id") final Long id,
* @ApiInputOptional @FormDataParam("uri") final String uri,
* @ApiInputOptional @FormDataParam("file") final InputStream fileInputStream,
* @ApiInputOptional @FormDataParam("file") final FormDataContentDisposition fileMetaData)
* throws Exception {
* // some code
* }
* }
* }
*
*
* Note: @FormDataParam must be allway at the last position.
*
* In this example, the uri, fileInputStream, and fileMetaData parameters are
* marked as optional, allowing the client to omit them when calling the API.
*
* <p>Generated TypeScript code example:
*
* {@code
* //Add a cover on a specific album
* export function uploadCover({
* restConfig,
* params,
* data,
* callbacks,
* }: {
* restConfig: RESTConfig,
* params: {
* id: Long,
* },
* data: {
* file?: File, // element is optional
* uri?: string, // element is optional
* },
* callbacks?: RESTCallbacks,
* }): Promise<Album> { ... }
* }
*
*
* The generated TypeScript function reflects the optional nature of the form data parameters
*/
@Target({ ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiInputOptional {
}

View File

@ -0,0 +1,45 @@
package org.atriasoft.archidata.annotation.apiGenerator;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation to explicitly define the nullability of a parameter in an API.
*
* This annotation allows marking a parameter as required (non-null) or optional (nullable),
* overriding any other nullability considerations. It is useful in API generation frameworks
* to ensure precise validation and documentation of method parameters.
*
* <p>Usage:
* - Target: This annotation can be applied to field.
* - Retention: The annotation is retained at runtime, allowing it to be
* processed by frameworks or libraries that handle code generation logic.
*
* <p>Behavior:
* - When applied to a parameter, it explicitly marks it as optional or required in the API.
* This annotation overrides all other considerations regarding nullability.
*
* <p>Example:
* <pre>{@code
* public class User {
* @ReadOnlyField
* @ApiNotNull
* public String username;
* public String email;
* }
* }</pre>
*
* In this example, the `username` field in the `User` class is explicitly marked as non-null in the generated API.
*/
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiNotNull {
/**
* (Optional) Specifies whether the API element can be null.
* If set to `true`, the element is required (non-null).
* If set to `false`, the element is optional (nullable).
*/
boolean value() default true;
}

View File

@ -0,0 +1,72 @@
package org.atriasoft.archidata.annotation.apiGenerator;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The TypeScriptProgress annotation is used to specify that an API method
* will take a significant amount of time to complete, and thus requires a
* callback API to provide more precise progress tracking, particularly for
* upload operations.
*
* <p>Usage:
* - Target: This annotation can be applied to method parameters and methods.
* - Retention: The annotation is retained at runtime, allowing it to be
* processed by frameworks or libraries that handle code generation logic.
*
* <p>Behavior:
* - When applied to a method or parameter, the TypeScriptProgress annotation
* indicates that the client code generator should provide a callback API
* for tracking the progress of the operation.
* - Note: The use of this annotation implies that the standard browser fetch
* API is not used, as the callback API is not yet operational. Instead,
* the older XMLHttpRequest interface is utilized.
*
* <p>Example:
* {@code
* public class SeasonService {
*
* @POST
* @Path("{id}/cover")
* @RolesAllowed("ADMIN")
* @Consumes(MediaType.MULTIPART_FORM_DATA)
* @Operation(description = "Upload a new season cover season", tags = "GLOBAL")
* @TypeScriptProgress
* public Season uploadCover(@PathParam("id") final Long id,
* @FormDataParam("file") final InputStream fileInputStream,
* @FormDataParam("file") final FormDataContentDisposition fileMetaData)
* throws Exception {
* // Upload logic
* }
* }
* }
*
* In this example, the uploadCover method will generate a client-side API
* with progress tracking capabilities using XMLHttpRequest.
*
* <p>Generated TypeScript code example:
* {@code
* export function uploadCover({
* restConfig,
* params,
* data,
* callbacks, // add this callback handle
* }: {
* restConfig: RESTConfig,
* params: {
* id: Long,
* },
* data: {
* file: File,
* },
* callbacks?: RESTCallbacks,
* }): Promise<Season> {...}
* }
*
*
*/
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiTypeScriptProgress {}

View File

@ -0,0 +1,30 @@
package org.atriasoft.archidata.annotation.checker;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
@Constraint(validatedBy = CheckForeignKeyValidator.class)
@Target({ ElementType.TYPE, //
ElementType.METHOD, //
ElementType.FIELD, //
ElementType.ANNOTATION_TYPE, //
ElementType.CONSTRUCTOR, //
ElementType.PARAMETER, //
ElementType.TYPE_USE, //
})
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckForeignKey {
Class<?> target();
String message() default "Foreign-key does not exist in the DB";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1,36 @@
package org.atriasoft.archidata.annotation.checker;
import org.atriasoft.archidata.dataAccess.DataAccess;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
public class CheckForeignKeyValidator implements ConstraintValidator<CheckForeignKey, Object> {
Class<?> target = null;
private final static Logger LOGGER = LoggerFactory.getLogger(CheckForeignKeyValidator.class);
@Override
public void initialize(final CheckForeignKey annotation) {
this.target = annotation.target();
}
@Override
public boolean isValid(final Object value, final ConstraintValidatorContext context) {
if (value == null) {
return true;
}
try {
final long count = DataAccess.count(this.target, value);
if (count != 1) {
return false;
}
} catch (final Exception e) {
LOGGER.error("Fail to access to the DB");
context.buildConstraintViolationWithTemplate("fail to access on the DB").addConstraintViolation();
return false;
}
return true;
}
}

View File

@ -0,0 +1,52 @@
package org.atriasoft.archidata.annotation.checker;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.atriasoft.archidata.dataAccess.options.CheckFunctionInterface;
/**
* The Checker annotation is used to specify a checker class that automatically
* validates data for a parent class. This annotation can be applied to both
* classes and fields to enforce validation rules defined in the checker class.
*
* <p>Usage:
* - Target: This annotation can be applied to types (classes) and fields.
* - Retention: The annotation is retained at runtime, allowing it to be
* processed by frameworks or libraries that handle data validation logic.
*
* <p>Behavior:
* - When applied to a class or field, the Checker annotation specifies a
* checker class that implements the CheckFunctionInterface. This checker
* class is responsible for validating the data associated with the annotated
* element.
* - The validation is automatically triggered when the data of the parent class
* is validated, ensuring that the data adheres to the specified rules.
*
* <p>Attributes:
* - value: Specifies the checker class that implements the validation logic.
* This class must extend the CheckFunctionInterface.
*
* <p>Example:
* <pre>{@code
* public class User {
*
* @Checker(UserDataChecker.class)
* public String email;
* }
*
* public class UserDataChecker implements CheckFunctionInterface {
* ...
* }
* }</pre>
*
* In this example, the email field in the User class is validated using the
* UserDataChecker class whenever the User class data is validated.
*/
@Target({ ElementType.TYPE, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Checker {
Class<? extends CheckFunctionInterface> value();
}

View File

@ -0,0 +1,20 @@
package org.atriasoft.archidata.annotation.checker;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
@Constraint(validatedBy = CollectionItemNotNullValidator.class)
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface CollectionItemNotNull {
String message() default "Collection can not contain NULL item";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1,31 @@
package org.atriasoft.archidata.annotation.checker;
import java.util.Collection;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
public class CollectionItemNotNullValidator implements ConstraintValidator<CollectionItemNotNull, Object> {
@Override
public void initialize(final CollectionItemNotNull annotation) {
// nothing to do...
}
@Override
public boolean isValid(final Object value, final ConstraintValidatorContext context) {
if (value == null) {
return true;
}
if (value instanceof final Collection<?> tmpCollection) {
final Object[] elements = tmpCollection.toArray();
for (final Object element : elements) {
if (element == null) {
return false;
//throw new InputException(baseName + fieldName + '[' + iii + ']', "Collection can not contain NULL item");
}
}
}
return true;
}
}

View File

@ -0,0 +1,21 @@
package org.atriasoft.archidata.annotation.checker;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
@Constraint(validatedBy = CollectionItemUniqueValidator.class)
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface CollectionItemUnique {
String message() default "Cannot insert multiple times the same elements";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1,30 @@
package org.atriasoft.archidata.annotation.checker;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
public class CollectionItemUniqueValidator implements ConstraintValidator<CollectionItemUnique, Object> {
@Override
public void initialize(final CollectionItemUnique annotation) {
// nothing to do...
}
@Override
public boolean isValid(final Object value, final ConstraintValidatorContext context) {
if (value == null) {
return true;
}
if (value instanceof final Collection<?> tmpCollection) {
final Set<Object> uniqueValues = new HashSet<>(tmpCollection);
if (uniqueValues.size() != tmpCollection.size()) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,21 @@
package org.atriasoft.archidata.annotation.checker;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
@Constraint(validatedBy = CollectionNotEmptyValidator.class)
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface CollectionNotEmpty {
String message() default "Collection can not be empty";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1,27 @@
package org.atriasoft.archidata.annotation.checker;
import java.util.Collection;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
public class CollectionNotEmptyValidator implements ConstraintValidator<CollectionNotEmpty, Object> {
@Override
public void initialize(final CollectionNotEmpty annotation) {
// nothing to do...
}
@Override
public boolean isValid(final Object value, final ConstraintValidatorContext context) {
if (value == null) {
return true;
}
if (value instanceof final Collection<?> tmpCollection) {
if (tmpCollection.isEmpty()) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,20 @@
package org.atriasoft.archidata.annotation.checker;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
@Constraint(validatedBy = ReadOnlyFieldValidator.class)
@Target({ ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ReadOnlyField {
String message() default "Field can not be set, it is a read-only field.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1,20 @@
package org.atriasoft.archidata.annotation.checker;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
public class ReadOnlyFieldValidator implements ConstraintValidator<ReadOnlyField, Object> {
@Override
public void initialize(final ReadOnlyField annotation) {
// nothing to do...
}
@Override
public boolean isValid(final Object value, final ConstraintValidatorContext context) {
if (value != null) {
return false;
}
return true;
}
}

View File

@ -1,4 +1,4 @@
package org.kar.archidata.annotation.method; package org.atriasoft.archidata.annotation.method;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.annotation.method; package org.atriasoft.archidata.annotation.method;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.annotation.security; package org.atriasoft.archidata.annotation.security;
import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.RetentionPolicy.RUNTIME;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.api; package org.atriasoft.archidata.api;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
@ -21,17 +21,18 @@ import java.util.UUID;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import org.atriasoft.archidata.annotation.apiGenerator.ApiInputOptional;
import org.atriasoft.archidata.annotation.security.PermitTokenInURI;
import org.atriasoft.archidata.dataAccess.DataAccess;
import org.atriasoft.archidata.dataAccess.QueryCondition;
import org.atriasoft.archidata.dataAccess.options.Condition;
import org.atriasoft.archidata.exception.FailException;
import org.atriasoft.archidata.filter.GenericContext;
import org.atriasoft.archidata.model.Data;
import org.atriasoft.archidata.tools.ConfigBaseVariable;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam; import org.glassfish.jersey.media.multipart.FormDataParam;
import org.kar.archidata.annotation.security.PermitTokenInURI;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.exception.FailException;
import org.kar.archidata.filter.GenericContext;
import org.kar.archidata.model.Data;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -156,13 +157,8 @@ public class DataResource {
return null; return null;
} }
public Data createNewData(final long tmpUID, final String originalFileName, final String sha512) protected String getMimeType(final String extension) throws IOException {
throws IOException { return switch (extension.toLowerCase()) {
// determine mime type:
Data injectedData = new Data();
String mimeType = "";
final String extension = originalFileName.substring(originalFileName.lastIndexOf('.') + 1);
mimeType = switch (extension.toLowerCase()) {
case "jpg", "jpeg" -> "image/jpeg"; case "jpg", "jpeg" -> "image/jpeg";
case "png" -> "image/png"; case "png" -> "image/png";
case "webp" -> "image/webp"; case "webp" -> "image/webp";
@ -171,6 +167,15 @@ public class DataResource {
case "webm" -> "video/webm"; case "webm" -> "video/webm";
default -> throw new IOException("Can not find the mime type of data input: '" + extension + "'"); default -> throw new IOException("Can not find the mime type of data input: '" + extension + "'");
}; };
}
public Data createNewData(final long tmpUID, final String originalFileName, final String sha512)
throws IOException {
// determine mime type:
Data injectedData = new Data();
String mimeType = "";
final String extension = originalFileName.substring(originalFileName.lastIndexOf('.') + 1);
mimeType = getMimeType(extension);
injectedData.mimeType = mimeType; injectedData.mimeType = mimeType;
injectedData.sha512 = sha512; injectedData.sha512 = sha512;
final String tmpPath = getTmpFileInData(tmpUID); final String tmpPath = getTmpFileInData(tmpUID);
@ -368,10 +373,9 @@ public class DataResource {
} }
LOGGER.info("input size image: {}x{} type={}", inputImage.getWidth(), inputImage.getHeight(), LOGGER.info("input size image: {}x{} type={}", inputImage.getWidth(), inputImage.getHeight(),
inputImage.getType()); inputImage.getType());
final int scaledWidth = 250; final int scaledWidth = ConfigBaseVariable.getThumbnailWidth();
final int scaledHeight = (int) ((float) inputImage.getHeight() / (float) inputImage.getWidth() final int scaledHeight = (int) ((float) inputImage.getHeight() / (float) inputImage.getWidth()
* scaledWidth); * scaledWidth);
// creates output image // creates output image
final BufferedImage outputImage = new BufferedImage(scaledWidth, scaledHeight, inputImage.getType()); final BufferedImage outputImage = new BufferedImage(scaledWidth, scaledHeight, inputImage.getType());
@ -380,16 +384,10 @@ public class DataResource {
LOGGER.info("output size image: {}x{}", scaledWidth, scaledHeight); LOGGER.info("output size image: {}x{}", scaledWidth, scaledHeight);
g2d.drawImage(inputImage, 0, 0, scaledWidth, scaledHeight, null); g2d.drawImage(inputImage, 0, 0, scaledWidth, scaledHeight, null);
g2d.dispose(); g2d.dispose();
for (final String data : ImageIO.getWriterFormatNames()) {
LOGGER.info("availlable format: {}", data);
}
// create the output stream: // create the output stream:
final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final ByteArrayOutputStream baos = new ByteArrayOutputStream();
try { try {
// TODO: check how to remove buffer file !!! here, it is not needed at all... ImageIO.write(outputImage, ConfigBaseVariable.getThumbnailFormat(), baos);
//ImageIO.write(outputImage, "JPEG", baos);
//ImageIO.write(outputImage, "png", baos);
ImageIO.write(outputImage, "WebP", baos);
} catch (final IOException e) { } catch (final IOException e) {
e.printStackTrace(); e.printStackTrace();
return Response.status(500).entity("Internal Error: resize fail: " + e.getMessage()).type("text/plain") return Response.status(500).entity("Internal Error: resize fail: " + e.getMessage()).type("text/plain")
@ -397,12 +395,20 @@ public class DataResource {
} }
final byte[] imageData = baos.toByteArray(); final byte[] imageData = baos.toByteArray();
LOGGER.info("output length {}", imageData.length); LOGGER.info("output length {}", imageData.length);
// Response.ok(new ByteArrayInputStream(imageData)).build(); if (imageData.length == 0) {
LOGGER.error("Fail to convert image... Availlable format:");
for (final String data : ImageIO.getWriterFormatNames()) {
LOGGER.error(" - {}", data);
}
}
final Response.ResponseBuilder out = Response.ok(imageData).header(HttpHeaders.CONTENT_LENGTH, final Response.ResponseBuilder out = Response.ok(imageData).header(HttpHeaders.CONTENT_LENGTH,
imageData.length); imageData.length);
//out.type("image/jpeg"); try {
out.type("image/webp"); out.type(getMimeType(ConfigBaseVariable.getThumbnailFormat()));
//out.type("image/png"); } catch (final IOException ex) {
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR,
"Fail to convert mime type of " + ConfigBaseVariable.getThumbnailFormat(), ex);
}
// TODO: move this in a decorator !!! // TODO: move this in a decorator !!!
final CacheControl cc = new CacheControl(); final CacheControl cc = new CacheControl();
cc.setMaxAge(3600); cc.setMaxAge(3600);
@ -426,7 +432,7 @@ public class DataResource {
public Response retrieveDataFull( public Response retrieveDataFull(
@Context final SecurityContext sc, @Context final SecurityContext sc,
@QueryParam(HttpHeaders.AUTHORIZATION) final String token, @QueryParam(HttpHeaders.AUTHORIZATION) final String token,
@HeaderParam("Range") final String range, @ApiInputOptional @HeaderParam("Range") final String range,
@PathParam("oid") final ObjectId oid, @PathParam("oid") final ObjectId oid,
@PathParam("name") final String name) throws Exception { @PathParam("name") final String name) throws Exception {
final GenericContext gc = (GenericContext) sc.getUserPrincipal(); final GenericContext gc = (GenericContext) sc.getUserPrincipal();

View File

@ -1,4 +1,4 @@
package org.kar.archidata.api; package org.atriasoft.archidata.api;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.api; package org.atriasoft.archidata.api;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.api; package org.atriasoft.archidata.api;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.api; package org.atriasoft.archidata.api;
import io.swagger.v3.jaxrs2.integration.resources.BaseOpenApiResource; import io.swagger.v3.jaxrs2.integration.resources.BaseOpenApiResource;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.backup; package org.atriasoft.archidata.backup;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;

View File

@ -0,0 +1,47 @@
package org.atriasoft.archidata.catcher;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.validation.ConstraintViolationException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
public class ConstraintViolationExceptionCatcher implements ExceptionMapper<ConstraintViolationException> {
private static final Logger LOGGER = LoggerFactory.getLogger(ConstraintViolationExceptionCatcher.class);
@Override
public Response toResponse(final ConstraintViolationException exception) {
LOGGER.warn("Catch ConstraintViolationException: {}", exception.getLocalizedMessage());
final RestErrorResponse ret = build(exception);
LOGGER.error("Error OID={}", ret.oid);
return Response.status(Response.Status.BAD_REQUEST).entity(ret).type(MediaType.APPLICATION_JSON).build();
}
private RestErrorResponse build(final ConstraintViolationException exception) {
final List<RestInputError> inputError = new ArrayList<>();
for (final var cv : exception.getConstraintViolations()) {
if (cv == null) {
continue;
}
inputError.add(new RestInputError(cv.getPropertyPath(), cv.getMessage()));
}
Collections.sort(inputError, Comparator.comparing(RestInputError::getFullPath));
String errorType = "Multiple error on input";
if (inputError.size() == 0) {
errorType = "Constraint Violation";
} else if (inputError.size() == 1) {
errorType = "Error on input='" + inputError.get(0).path + "'";
}
return new RestErrorResponse(Response.Status.BAD_REQUEST, Instant.now().toString(), errorType,
exception.getMessage(), inputError);
}
}

View File

@ -1,4 +1,4 @@
package org.kar.archidata.catcher; package org.atriasoft.archidata.catcher;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@ -1,6 +1,6 @@
package org.kar.archidata.catcher; package org.atriasoft.archidata.catcher;
import org.kar.archidata.exception.FailException; import org.atriasoft.archidata.exception.FailException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.catcher; package org.atriasoft.archidata.catcher;
import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.server.ResourceConfig;
@ -11,14 +11,16 @@ public class GenericCatcher {
public static void addAll(final ResourceConfig rc) { public static void addAll(final ResourceConfig rc) {
// Generic Json parsing error // Generic Json parsing error
rc.register(JacksonExceptionCatcher.class); rc.register(JacksonExceptionCatcher.class);
// Catch jakarta generic errors
rc.register(WebApplicationExceptionCatcher.class);
// Archidata exceptions // Archidata exceptions
rc.register(InputExceptionCatcher.class); rc.register(InputExceptionCatcher.class);
rc.register(SystemExceptionCatcher.class); rc.register(SystemExceptionCatcher.class);
rc.register(FailExceptionCatcher.class); rc.register(FailExceptionCatcher.class);
// generic Exception catcher // generic Exception catcher
rc.register(ConstraintViolationExceptionCatcher.class);
rc.register(QueryParamExceptionCatcher.class);
rc.register(ExceptionCatcher.class); rc.register(ExceptionCatcher.class);
// Catch jakarta generic errors
rc.register(WebApplicationExceptionCatcher.class);
} }
} }

View File

@ -1,6 +1,6 @@
package org.kar.archidata.catcher; package org.atriasoft.archidata.catcher;
import org.kar.archidata.exception.InputException; import org.atriasoft.archidata.exception.InputException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.catcher; package org.atriasoft.archidata.catcher;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@ -0,0 +1,34 @@
package org.atriasoft.archidata.catcher;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import org.glassfish.jersey.server.ParamException.QueryParamException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
public class QueryParamExceptionCatcher implements ExceptionMapper<QueryParamException> {
private static final Logger LOGGER = LoggerFactory.getLogger(QueryParamExceptionCatcher.class);
@Override
public Response toResponse(final QueryParamException exception) {
LOGGER.trace("Catch IllegalArgumentException: {}", exception.getLocalizedMessage());
final RestErrorResponse ret = build(exception);
LOGGER.error("Error OID={}", ret.oid);
return Response.status(Response.Status.BAD_REQUEST).entity(ret).type(MediaType.APPLICATION_JSON).build();
}
private RestErrorResponse build(final QueryParamException exception) {
final List<RestInputError> inputError = new ArrayList<>();
inputError.add(new RestInputError("query", exception.getParameterName(), exception.getCause().getMessage()));
final String errorType = "Error on query input='" + exception.getParameterName() + "'";
return new RestErrorResponse(Response.Status.BAD_REQUEST, Instant.now().toString(), errorType,
"Input parsing fail", inputError);
}
}

View File

@ -1,15 +1,20 @@
package org.kar.archidata.catcher; package org.atriasoft.archidata.catcher;
import java.time.Instant; import java.time.Instant;
import java.util.List;
import org.atriasoft.archidata.annotation.apiGenerator.ApiGenerationMode;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import org.kar.archidata.annotation.NoWriteSpecificMode;
import com.fasterxml.jackson.annotation.JsonInclude;
import jakarta.annotation.Nullable;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
@NoWriteSpecificMode @ApiGenerationMode
@JsonInclude(JsonInclude.Include.NON_NULL)
public class RestErrorResponse { public class RestErrorResponse {
public ObjectId oid = new ObjectId(); public ObjectId oid = new ObjectId();
@NotNull @NotNull
@ -27,6 +32,19 @@ public class RestErrorResponse {
@Column(length = 0) @Column(length = 0)
final public String statusMessage; final public String statusMessage;
@Nullable
final public List<RestInputError> inputError;
public RestErrorResponse(final Response.Status status, final String time, final String error, final String message,
final List<RestInputError> inputError) {
this.time = time;
this.name = error;
this.message = message;
this.status = status.getStatusCode();
this.statusMessage = status.getReasonPhrase();
this.inputError = inputError;
}
public RestErrorResponse(final Response.Status status, final String time, final String error, public RestErrorResponse(final Response.Status status, final String time, final String error,
final String message) { final String message) {
this.time = time; this.time = time;
@ -34,6 +52,7 @@ public class RestErrorResponse {
this.message = message; this.message = message;
this.status = status.getStatusCode(); this.status = status.getStatusCode();
this.statusMessage = status.getReasonPhrase(); this.statusMessage = status.getReasonPhrase();
this.inputError = null;
} }
public RestErrorResponse(final Response.Status status, final String error, final String message) { public RestErrorResponse(final Response.Status status, final String error, final String message) {
@ -42,6 +61,7 @@ public class RestErrorResponse {
this.message = message; this.message = message;
this.status = status.getStatusCode(); this.status = status.getStatusCode();
this.statusMessage = status.getReasonPhrase(); this.statusMessage = status.getReasonPhrase();
this.inputError = null;
} }
public RestErrorResponse(final Response.Status status) { public RestErrorResponse(final Response.Status status) {
@ -50,6 +70,7 @@ public class RestErrorResponse {
this.time = Instant.now().toString(); this.time = Instant.now().toString();
this.status = status.getStatusCode(); this.status = status.getStatusCode();
this.statusMessage = status.getReasonPhrase(); this.statusMessage = status.getReasonPhrase();
this.inputError = null;
} }
} }

View File

@ -0,0 +1,59 @@
package org.atriasoft.archidata.catcher;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.fasterxml.jackson.annotation.JsonInclude;
import jakarta.persistence.Column;
import jakarta.validation.Path;
import jakarta.validation.constraints.NotNull;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class RestInputError {
private static Pattern PATTERN = Pattern.compile("^([^.]+)\\.([^.]+)(\\.(.*))?");
@Column(length = 0)
public String argument;
@Column(length = 0)
public String path;
@NotNull
@Column(length = 0)
public String message;
@Override
public String toString() {
return "RestInputError [argument=" + this.argument + ", path=" + this.path + ", message=" + this.message + "]";
}
public RestInputError() {}
public RestInputError(final String argument, final String path, final String message) {
this.path = argument;
this.path = path;
this.message = message;
}
public RestInputError(final String path, final String message) {
this.path = path;
this.message = message;
}
public RestInputError(final Path path, final String message) {
final Matcher matcher = PATTERN.matcher(path.toString());
if (matcher.find()) {
//String firstPart = matcher.group(1); this is the request base element ==> not needed
this.argument = matcher.group(2);
this.path = matcher.group(4);
} else {
this.path = path.toString();
}
this.message = message;
}
String getFullPath() {
if (this.path == null) {
return this.argument;
}
return this.argument + "." + this.path;
}
}

View File

@ -1,6 +1,6 @@
package org.kar.archidata.catcher; package org.atriasoft.archidata.catcher;
import org.kar.archidata.exception.SystemException; import org.atriasoft.archidata.exception.SystemException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.catcher; package org.atriasoft.archidata.catcher;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.checker; package org.atriasoft.archidata.checker;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.sql.Timestamp; import java.sql.Timestamp;
@ -14,21 +14,21 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.kar.archidata.annotation.AnnotationTools; import org.atriasoft.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.checker.CheckForeignKey; import org.atriasoft.archidata.annotation.checker.CheckForeignKey;
import org.kar.archidata.annotation.checker.Checker; import org.atriasoft.archidata.annotation.checker.Checker;
import org.kar.archidata.annotation.checker.CollectionItemNotNull; import org.atriasoft.archidata.annotation.checker.CollectionItemNotNull;
import org.kar.archidata.annotation.checker.CollectionItemUnique; import org.atriasoft.archidata.annotation.checker.CollectionItemUnique;
import org.kar.archidata.annotation.checker.CollectionNotEmpty; import org.atriasoft.archidata.annotation.checker.CollectionNotEmpty;
import org.kar.archidata.dataAccess.DBAccess; import org.atriasoft.archidata.dataAccess.DBAccess;
import org.kar.archidata.dataAccess.QueryCondition; import org.atriasoft.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.QueryOptions; import org.atriasoft.archidata.dataAccess.QueryOptions;
import org.kar.archidata.dataAccess.options.CheckFunctionInterface; import org.atriasoft.archidata.dataAccess.options.CheckFunctionInterface;
import org.kar.archidata.dataAccess.options.CheckFunctionVoid; import org.atriasoft.archidata.dataAccess.options.CheckFunctionVoid;
import org.kar.archidata.dataAccess.options.Condition; import org.atriasoft.archidata.dataAccess.options.Condition;
import org.kar.archidata.dataAccess.options.ConditionChecker; import org.atriasoft.archidata.dataAccess.options.ConditionChecker;
import org.kar.archidata.exception.DataAccessException; import org.atriasoft.archidata.exception.DataAccessException;
import org.kar.archidata.exception.InputException; import org.atriasoft.archidata.exception.InputException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@ -0,0 +1,53 @@
package org.atriasoft.archidata.converter.Jakarta;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Date;
import org.atriasoft.archidata.tools.DateTools;
import jakarta.annotation.Priority;
import jakarta.ws.rs.ext.ParamConverter;
import jakarta.ws.rs.ext.ParamConverterProvider;
import jakarta.ws.rs.ext.Provider;
@Provider
@Priority(1)
public class DateParamConverterProvider implements ParamConverterProvider {
@SuppressWarnings("unchecked")
@Override
public <T> ParamConverter<T> getConverter(
final Class<T> rawType,
final Type genericType,
final Annotation[] annotations) {
if (rawType != Date.class) {
return null;
}
return (ParamConverter<T>) new DateParamConverter();
}
public class DateParamConverter implements ParamConverter<Date> {
@Override
public Date fromString(final String value) {
if (value == null || value.isEmpty()) {
return null;
}
try {
return DateTools.parseDate(value);
} catch (final IOException e) {
throw new IllegalArgumentException("Invalid date format. Please use ISO8601", e);
}
}
@Override
public String toString(final Date value) {
if (value == null) {
return null;
}
return DateTools.serializeMilliWithUTCTimeZone(value);
}
}
}

View File

@ -0,0 +1,52 @@
package org.atriasoft.archidata.converter.Jakarta;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.time.OffsetDateTime;
import org.atriasoft.archidata.tools.DateTools;
import jakarta.ws.rs.ext.ParamConverter;
import jakarta.ws.rs.ext.ParamConverterProvider;
import jakarta.ws.rs.ext.Provider;
@Provider
public class OffsetDateTimeParamConverterProvider implements ParamConverterProvider {
@SuppressWarnings("unchecked")
@Override
public <T> ParamConverter<T> getConverter(
final Class<T> rawType,
final Type genericType,
final Annotation[] annotations) {
if (rawType != OffsetDateTime.class) {
return null;
}
return (ParamConverter<T>) new OffsetDateTimeParamConverter();
}
public class OffsetDateTimeParamConverter implements ParamConverter<OffsetDateTime> {
@Override
public OffsetDateTime fromString(final String value) {
if (value == null || value.isEmpty()) {
return null;
}
try {
return DateTools.parseOffsetDateTime(value);
} catch (final IOException e) {
throw new IllegalArgumentException("Invalid date format. Please use ISO8601", e);
}
}
@Override
public String toString(final OffsetDateTime value) {
if (value == null) {
return null;
}
return DateTools.serializeMilliWithUTCTimeZone(value);
}
};
}

View File

@ -0,0 +1,19 @@
package org.atriasoft.archidata.converter.jackson;
import java.io.IOException;
import java.util.Date;
import org.atriasoft.archidata.tools.DateTools;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
public class DateDeserializer extends JsonDeserializer<Date> {
@Override
public Date deserialize(final JsonParser p, final DeserializationContext ctxt) throws IOException {
final String value = p.getText();
final Date ret = DateTools.parseDate(value);
return ret;
}
}

View File

@ -0,0 +1,18 @@
package org.atriasoft.archidata.converter.jackson;
import java.io.IOException;
import java.util.Date;
import org.atriasoft.archidata.tools.DateTools;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
public class DateSerializer extends JsonSerializer<Date> {
@Override
public void serialize(final Date value, final JsonGenerator gen, final SerializerProvider serializers)
throws IOException {
gen.writeString(DateTools.serializeMilliWithUTCTimeZone(value));
}
}

View File

@ -0,0 +1,21 @@
package org.atriasoft.archidata.converter.jackson;
import java.time.OffsetDateTime;
import java.util.Date;
import org.bson.types.ObjectId;
import com.fasterxml.jackson.databind.module.SimpleModule;
public class JacksonModules {
public static SimpleModule getAllModules() {
final SimpleModule module = new SimpleModule();
module.addSerializer(ObjectId.class, new ObjectIdSerializer());
module.addDeserializer(ObjectId.class, new ObjectIdDeserializer());
module.addSerializer(Date.class, new DateSerializer());
module.addDeserializer(Date.class, new DateDeserializer());
module.addSerializer(OffsetDateTime.class, new OffsetDateTimeSerializer());
module.addDeserializer(OffsetDateTime.class, new OffsetDateTimeDeserializer());
return module;
}
}

View File

@ -1,4 +1,4 @@
package org.kar.archidata.converter.jackson; package org.atriasoft.archidata.converter.jackson;
import java.io.IOException; import java.io.IOException;
@ -13,4 +13,4 @@ public class ObjectIdDeserializer extends JsonDeserializer<ObjectId> {
public ObjectId deserialize(final JsonParser p, final DeserializationContext ctxt) throws IOException { public ObjectId deserialize(final JsonParser p, final DeserializationContext ctxt) throws IOException {
return new ObjectId(p.getValueAsString()); return new ObjectId(p.getValueAsString());
} }
} }

View File

@ -1,4 +1,4 @@
package org.kar.archidata.converter.jackson; package org.atriasoft.archidata.converter.jackson;
import java.io.IOException; import java.io.IOException;
@ -14,4 +14,4 @@ public class ObjectIdSerializer extends JsonSerializer<ObjectId> {
throws IOException { throws IOException {
gen.writeString(value.toHexString()); gen.writeString(value.toHexString());
} }
} }

View File

@ -0,0 +1,19 @@
package org.atriasoft.archidata.converter.jackson;
import java.io.IOException;
import java.time.OffsetDateTime;
import org.atriasoft.archidata.tools.DateTools;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
public class OffsetDateTimeDeserializer extends JsonDeserializer<OffsetDateTime> {
@Override
public OffsetDateTime deserialize(final JsonParser p, final DeserializationContext ctxt) throws IOException {
final String value = p.getText();
final OffsetDateTime ret = DateTools.parseOffsetDateTime(value);
return ret;
}
}

View File

@ -0,0 +1,18 @@
package org.atriasoft.archidata.converter.jackson;
import java.io.IOException;
import java.time.OffsetDateTime;
import org.atriasoft.archidata.tools.DateTools;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
public class OffsetDateTimeSerializer extends JsonSerializer<OffsetDateTime> {
@Override
public void serialize(final OffsetDateTime value, final JsonGenerator gen, final SerializerProvider serializers)
throws IOException {
gen.writeString(DateTools.serializeMilliWithUTCTimeZone(value));
}
}

View File

@ -1,4 +1,4 @@
package org.kar.archidata.converter.morphia; package org.atriasoft.archidata.converter.morphia;
import java.sql.Timestamp; import java.sql.Timestamp;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess; package org.atriasoft.archidata.dataAccess;
/** Java does not permit to set return data (eg: integer) in the function parameter. This class permit to update a value as in/out function parameters. */ /** Java does not permit to set return data (eg: integer) in the function parameter. This class permit to update a value as in/out function parameters. */
public class CountInOut { public class CountInOut {

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess; package org.atriasoft.archidata.dataAccess;
import java.io.Closeable; import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
@ -6,21 +6,21 @@ import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.kar.archidata.annotation.AnnotationTools; import org.atriasoft.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.AnnotationTools.FieldName; import org.atriasoft.archidata.annotation.AnnotationTools.FieldName;
import org.kar.archidata.dataAccess.options.Condition; import org.atriasoft.archidata.dataAccess.options.Condition;
import org.kar.archidata.dataAccess.options.FilterValue; import org.atriasoft.archidata.dataAccess.options.FilterValue;
import org.kar.archidata.dataAccess.options.Limit; import org.atriasoft.archidata.dataAccess.options.Limit;
import org.kar.archidata.dataAccess.options.OptionSpecifyType; import org.atriasoft.archidata.dataAccess.options.OptionSpecifyType;
import org.kar.archidata.dataAccess.options.QueryOption; import org.atriasoft.archidata.dataAccess.options.QueryOption;
import org.kar.archidata.dataAccess.options.TransmitKey; import org.atriasoft.archidata.dataAccess.options.TransmitKey;
import org.kar.archidata.db.DbConfig; import org.atriasoft.archidata.db.DbConfig;
import org.kar.archidata.db.DbIo; import org.atriasoft.archidata.db.DbIo;
import org.kar.archidata.db.DbIoFactory; import org.atriasoft.archidata.db.DbIoFactory;
import org.kar.archidata.db.DbIoMorphia; import org.atriasoft.archidata.db.DbIoMorphia;
import org.kar.archidata.db.DbIoSql; import org.atriasoft.archidata.db.DbIoSql;
import org.kar.archidata.exception.DataAccessException; import org.atriasoft.archidata.exception.DataAccessException;
import org.kar.archidata.tools.ContextGenericTools; import org.atriasoft.archidata.tools.ContextGenericTools;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -185,7 +185,8 @@ public abstract class DBAccess implements Closeable {
/** @param <T> /** @param <T>
* @param data * @param data
* @param id * @param id
* @param filterValue * @param updateColomn
* @param option
* @return the affected rows. * @return the affected rows.
* @throws Exception */ * @throws Exception */
public <T, ID_TYPE> long update( public <T, ID_TYPE> long update(
@ -300,8 +301,7 @@ public abstract class DBAccess implements Closeable {
/** Delete items with the specific condition and some options. If the Entity is manage as a softDeleted model, then it is flag as removed (if not already done before). /** Delete items with the specific condition and some options. If the Entity is manage as a softDeleted model, then it is flag as removed (if not already done before).
* @param clazz Data model that might remove element. * @param clazz Data model that might remove element.
* @param condition Condition to remove elements. * @param option (Optional) Options of the request.
* @param options (Optional) Options of the request.
* @return Number of element that is removed. */ * @return Number of element that is removed. */
public long deleteWhere(final Class<?> clazz, final QueryOption... option) throws Exception { public long deleteWhere(final Class<?> clazz, final QueryOption... option) throws Exception {
final String hasDeletedFieldName = AnnotationTools.getDeletedFieldName(clazz); final String hasDeletedFieldName = AnnotationTools.getDeletedFieldName(clazz);

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess; package org.atriasoft.archidata.dataAccess;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
@ -14,24 +14,26 @@ import java.util.Date;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import org.atriasoft.archidata.annotation.AnnotationTools;
import org.atriasoft.archidata.annotation.AnnotationTools.FieldName;
import org.atriasoft.archidata.annotation.CreationTimestamp;
import org.atriasoft.archidata.annotation.UpdateTimestamp;
import org.atriasoft.archidata.dataAccess.addOnMongo.AddOnManyToOne;
import org.atriasoft.archidata.dataAccess.addOnMongo.AddOnOneToMany;
import org.atriasoft.archidata.dataAccess.addOnMongo.DataAccessAddOn;
import org.atriasoft.archidata.dataAccess.options.CheckFunction;
import org.atriasoft.archidata.dataAccess.options.Condition;
import org.atriasoft.archidata.dataAccess.options.FilterValue;
import org.atriasoft.archidata.dataAccess.options.Limit;
import org.atriasoft.archidata.dataAccess.options.OptionSpecifyType;
import org.atriasoft.archidata.dataAccess.options.OrderBy;
import org.atriasoft.archidata.dataAccess.options.QueryOption;
import org.atriasoft.archidata.db.DbIoMorphia;
import org.atriasoft.archidata.exception.DataAccessException;
import org.atriasoft.archidata.tools.UuidUtils;
import org.bson.Document; import org.bson.Document;
import org.bson.conversions.Bson; import org.bson.conversions.Bson;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.CreationTimestamp;
import org.kar.archidata.annotation.UpdateTimestamp;
import org.kar.archidata.dataAccess.addOnMongo.AddOnManyToOne;
import org.kar.archidata.dataAccess.addOnMongo.AddOnOneToMany;
import org.kar.archidata.dataAccess.addOnMongo.DataAccessAddOn;
import org.kar.archidata.dataAccess.options.CheckFunction;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.dataAccess.options.FilterValue;
import org.kar.archidata.dataAccess.options.Limit;
import org.kar.archidata.dataAccess.options.OrderBy;
import org.kar.archidata.dataAccess.options.QueryOption;
import org.kar.archidata.db.DbIoMorphia;
import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.tools.UuidUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -128,7 +130,7 @@ public class DBAccessMorphia extends DBAccess {
return groups; return groups;
} }
protected <T> void setValuedb( protected <T> void setValueToDb(
final Class<?> type, final Class<?> type,
final T data, final T data,
final Field field, final Field field,
@ -136,7 +138,9 @@ public class DBAccessMorphia extends DBAccess {
final Document docSet, final Document docSet,
final Document docUnSet) throws Exception { final Document docUnSet) throws Exception {
if (field.get(data) == null) { if (field.get(data) == null) {
docUnSet.append(fieldName, ""); if (docUnSet != null) {
docUnSet.append(fieldName, "");
}
return; return;
} }
if (type == long.class) { if (type == long.class) {
@ -151,7 +155,7 @@ public class DBAccessMorphia extends DBAccess {
docSet.append(fieldName, field.getFloat(data)); docSet.append(fieldName, field.getFloat(data));
return; return;
} }
if (type == Double.class) { if (type == double.class) {
docSet.append(fieldName, field.getDouble(data)); docSet.append(fieldName, field.getDouble(data));
return; return;
} }
@ -196,56 +200,40 @@ public class DBAccessMorphia extends DBAccess {
docSet.append(fieldName, tmp); docSet.append(fieldName, tmp);
return; return;
} }
if (type == ObjectId.class) {
docSet.append(fieldName, tmp);
return;
}
if (type == UUID.class) { if (type == UUID.class) {
docSet.append(fieldName, tmp); docSet.append(fieldName, tmp);
return; return;
} }
if (type == Date.class) { if (type == Date.class) {
// TODO ...
/* /*
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
final Timestamp sqlDate = java.sql.Timestamp.from(((Date) tmp).toInstant()); final Timestamp sqlDate = java.sql.Timestamp.from(((Date) tmp).toInstant());
ps.setTimestamp(iii.value, sqlDate); ps.setTimestamp(iii.value, sqlDate);
}*/ */
} }
if (type == Instant.class) { if (type == Instant.class) {
/* /*
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
final String sqlDate = ((Instant) tmp).toString(); final String sqlDate = ((Instant) tmp).toString();
ps.setString(iii.value, sqlDate); ps.setString(iii.value, sqlDate);
}
*/ */
} }
if (type == LocalDate.class) { if (type == LocalDate.class) {
/* /*
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
final java.sql.Date sqlDate = java.sql.Date.valueOf((LocalDate) tmp); final java.sql.Date sqlDate = java.sql.Date.valueOf((LocalDate) tmp);
ps.setDate(iii.value, sqlDate); ps.setDate(iii.value, sqlDate);
}
*/ */
} }
if (type == LocalTime.class) { if (type == LocalTime.class) {
/* /*
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
final java.sql.Time sqlDate = java.sql.Time.valueOf((LocalTime) tmp); final java.sql.Time sqlDate = java.sql.Time.valueOf((LocalTime) tmp);
ps.setTime(iii.value, sqlDate); ps.setTime(iii.value, sqlDate);
}
*/ */
} }
throw new DataAccessException("Unknown Field Type"); docSet.append(fieldName, tmp);
//throw new DataAccessException("Unknown Field Type");
} }
public <T> void setValueFromDoc( public <T> void setValueFromDoc(
@ -265,6 +253,11 @@ public class DBAccessMorphia extends DBAccess {
field.set(data, value); field.set(data, value);
return; return;
} }
if (type == ObjectId.class) {
final ObjectId value = doc.get(fieldName, ObjectId.class);
field.set(data, value);
return;
}
if (type == Long.class || type == long.class) { if (type == Long.class || type == long.class) {
final Long value = doc.getLong(fieldName); final Long value = doc.getLong(fieldName);
field.set(data, value); field.set(data, value);
@ -349,8 +342,8 @@ public class DBAccessMorphia extends DBAccess {
final Object value = doc.get(fieldName, field.getType()); final Object value = doc.get(fieldName, field.getType());
field.set(data, value); field.set(data, value);
} else { } else {
final Object value = createObjectFromDocument(doc.get(fieldName, Document.class), field.getType(), null, final Object value = createObjectFromDocument(doc.get(fieldName, Document.class), field.getType(),
lazyCall); new QueryOptions(), lazyCall);
field.set(data, value); field.set(data, value);
} }
@ -459,32 +452,45 @@ public class DBAccessMorphia extends DBAccess {
Object uniqueId = null; Object uniqueId = null;
// real add in the BDD: // real add in the BDD:
ObjectId insertedId = null; ObjectId insertedId = null;
final List<OptionSpecifyType> specificTypes = options.get(OptionSpecifyType.class);
try { try {
final MongoCollection<Document> collection = this.db.getDatastore().getDatabase() final MongoCollection<Document> collection = this.db.getDatastore().getDatabase()
.getCollection(collectionName); .getCollection(collectionName);
final Document doc = new Document(); final Document docSet = new Document();
final Document docUnSet = new Document();
for (final Field field : clazz.getFields()) { for (final Field field : clazz.getFields()) {
// static field is only for internal global declaration ==> remove it .. // static field is only for internal global declaration ==> remove it ..
if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) { if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
continue; continue;
} }
final String tableFieldName = AnnotationTools.getFieldName(field, options).inTable(); final FieldName tableFieldName = AnnotationTools.getFieldName(field, options);
Object currentInsertValue = field.get(data); Object currentInsertValue = field.get(data);
if (AnnotationTools.isPrimaryKey(field)) { if (AnnotationTools.isPrimaryKey(field)) {
primaryKeyField = field; primaryKeyField = field;
if (primaryKeyField.getType() == UUID.class) { if (primaryKeyField.getType() == UUID.class) {
final UUID uuid = UuidUtils.nextUUID(); final UUID uuid = UuidUtils.nextUUID();
uniqueId = uuid; uniqueId = uuid;
doc.append(tableFieldName, uuid); docSet.append(tableFieldName.inTable(), uuid);
continue; continue;
} else if (primaryKeyField.getType() == Long.class || primaryKeyField.getType() == long.class) { } else if (primaryKeyField.getType() == Long.class || primaryKeyField.getType() == long.class) {
// By default the MongoDB does not manage the // By default the MongoDB does not manage the
final long id = getNextSequenceLongValue(collectionName, tableFieldName); final long id = getNextSequenceLongValue(collectionName, tableFieldName.inTable());
uniqueId = id; uniqueId = id;
doc.append(tableFieldName, id); docSet.append(tableFieldName.inTable(), id);
continue; continue;
} }
LOGGER.error("TODO: Manage the ID primary key for type: "); LOGGER.error("TODO: Manage the ID primary key for type: {}=>{}", clazz.getCanonicalName(),
primaryKeyField.getType());
continue;
}
final boolean createTime = field.getDeclaredAnnotationsByType(CreationTimestamp.class).length != 0;
if (createTime) {
docSet.append(tableFieldName.inTable(), Date.from(Instant.now()));
continue;
}
final boolean updateTime = field.getDeclaredAnnotationsByType(UpdateTimestamp.class).length != 0;
if (updateTime) {
docSet.append(tableFieldName.inTable(), Date.from(Instant.now()));
continue; continue;
} }
final DataAccessAddOn addOn = findAddOnforField(field); final DataAccessAddOn addOn = findAddOnforField(field);
@ -495,19 +501,8 @@ public class DBAccessMorphia extends DBAccess {
} }
continue; continue;
} }
final boolean createTime = field.getDeclaredAnnotationsByType(CreationTimestamp.class).length != 0;
if (createTime) {
doc.append(tableFieldName, Date.from(Instant.now()));
continue;
}
final boolean updateTime = field.getDeclaredAnnotationsByType(UpdateTimestamp.class).length != 0;
if (updateTime) {
doc.append(tableFieldName, Date.from(Instant.now()));
continue;
}
if (currentInsertValue == null && !field.getClass().isPrimitive()) { if (currentInsertValue == null && !field.getClass().isPrimitive()) {
final DefaultValue[] defaultValue = field.getDeclaredAnnotationsByType(DefaultValue.class); final DefaultValue[] defaultValue = field.getDeclaredAnnotationsByType(DefaultValue.class);
LOGGER.error("TODO: convert default value in the correct value for the DB...");
if (defaultValue.length == 0) { if (defaultValue.length == 0) {
continue; continue;
} else { } else {
@ -518,9 +513,24 @@ public class DBAccessMorphia extends DBAccess {
currentInsertValue = convertDefaultField(value, field); currentInsertValue = convertDefaultField(value, field);
} }
} }
doc.append(tableFieldName, currentInsertValue); // conversion table ...
//doc.append(tableFieldName, currentInsertValue);
if (addOn != null) {
addOn.insertData(this, field, data, options, docSet, docUnSet);
} else {
final Class<?> type = field.getType();
if (!type.isPrimitive()) {
if (field.get(data) == null) {
if (currentInsertValue != null) {
docSet.append(tableFieldName.inTable(), currentInsertValue);
}
continue;
}
}
setValueToDb(type, data, field, tableFieldName.inTable(), docSet, null);
}
} }
final InsertOneResult result = collection.insertOne(doc); final InsertOneResult result = collection.insertOne(docSet);
// Get the Object of inserted object: // Get the Object of inserted object:
insertedId = result.getInsertedId().asObjectId().getValue(); insertedId = result.getInsertedId().asObjectId().getValue();
LOGGER.info("Document inserted with ID: " + insertedId); LOGGER.info("Document inserted with ID: " + insertedId);
@ -587,14 +597,14 @@ public class DBAccessMorphia extends DBAccess {
if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) { if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
continue; continue;
} }
final String fieldName = AnnotationTools.getFieldName(field, options).inTable(); final FieldName fieldName = AnnotationTools.getFieldName(field, options);
// update field is not conditioned by filter: // update field is not conditioned by filter:
final boolean updateTime = field.getDeclaredAnnotationsByType(UpdateTimestamp.class).length != 0; final boolean updateTime = field.getDeclaredAnnotationsByType(UpdateTimestamp.class).length != 0;
if (updateTime) { if (updateTime) {
docSet.append(fieldName, Date.from(Instant.now())); docSet.append(fieldName.inTable(), Date.from(Instant.now()));
continue; continue;
} }
if (!filterKey.getValues().contains(fieldName)) { if (!filterKey.getValues().contains(fieldName.inStruct())) {
continue; continue;
} else if (AnnotationTools.isGenericField(field)) { } else if (AnnotationTools.isGenericField(field)) {
continue; continue;
@ -624,12 +634,10 @@ public class DBAccessMorphia extends DBAccess {
continue; continue;
} }
} }
setValuedb(type, data, field, fieldName, docSet, docUnSet); setValueToDb(type, data, field, fieldName.inTable(), docSet, docUnSet);
} }
} }
// Do the query ... // Do the query ...
final MongoCollection<Document> collection = this.db.getDatastore().getDatabase() final MongoCollection<Document> collection = this.db.getDatastore().getDatabase()
.getCollection(collectionName); .getCollection(collectionName);
final Document actions = new Document(); final Document actions = new Document();
@ -652,7 +660,6 @@ public class DBAccessMorphia extends DBAccess {
} }
public List<String> generateSelectField(final Class<?> clazz, final QueryOptions options) throws Exception { public List<String> generateSelectField(final Class<?> clazz, final QueryOptions options) throws Exception {
// TODO: list of user select fields.
final boolean readAllfields = QueryOptions.readAllColomn(options); final boolean readAllfields = QueryOptions.readAllColomn(options);
final List<String> fieldsName = new ArrayList<>(); final List<String> fieldsName = new ArrayList<>();
@ -776,6 +783,8 @@ public class DBAccessMorphia extends DBAccess {
final Class<?> clazz, final Class<?> clazz,
final QueryOptions options, final QueryOptions options,
final List<LazyGetter> lazyCall) throws Exception { final List<LazyGetter> lazyCall) throws Exception {
final List<OptionSpecifyType> specificTypes = options.get(OptionSpecifyType.class);
LOGGER.info("createObjectFromDocument: {}", clazz.getCanonicalName());
final boolean readAllfields = QueryOptions.readAllColomn(options); final boolean readAllfields = QueryOptions.readAllColomn(options);
// TODO: manage class that is defined inside a class ==> Not manage for now... // TODO: manage class that is defined inside a class ==> Not manage for now...
Object data = null; Object data = null;
@ -790,23 +799,37 @@ public class DBAccessMorphia extends DBAccess {
"Can not find the default constructor for the class: " + clazz.getCanonicalName()); "Can not find the default constructor for the class: " + clazz.getCanonicalName());
} }
for (final Field elem : clazz.getFields()) { for (final Field elem : clazz.getFields()) {
LOGGER.info(" Inspect field: name='{}' type='{}'", elem.getName(), elem.getType().getCanonicalName());
// static field is only for internal global declaration ==> remove it .. // static field is only for internal global declaration ==> remove it ..
if (java.lang.reflect.Modifier.isStatic(elem.getModifiers())) { if (java.lang.reflect.Modifier.isStatic(elem.getModifiers())) {
LOGGER.info(" ==> static");
continue; continue;
} }
final DataAccessAddOn addOn = findAddOnforField(elem); final DataAccessAddOn addOn = findAddOnforField(elem);
if (addOn != null && !addOn.canRetrieve(elem)) { if (addOn != null && !addOn.canRetrieve(elem)) {
LOGGER.info(" ==> Can not retreive this field");
continue; continue;
} }
final boolean notRead = AnnotationTools.isDefaultNotRead(elem); final boolean notRead = AnnotationTools.isDefaultNotRead(elem);
if (!readAllfields && notRead) { if (!readAllfields && notRead) {
LOGGER.info(" ==> Not read this element");
continue; continue;
} }
if (addOn != null) { if (addOn != null) {
LOGGER.error("TODO: Add on not managed .6. ");
addOn.fillFromDoc(this, doc, elem, data, options, lazyCall); addOn.fillFromDoc(this, doc, elem, data, options, lazyCall);
} else { } else {
setValueFromDoc(elem.getType(), data, elem, doc, lazyCall, options); Class<?> type = elem.getType();
if (type == Object.class) {
for (final OptionSpecifyType specify : specificTypes) {
if (specify.name.equals(elem.getName())) {
type = specify.clazz;
LOGGER.info("Detect overwrite of typing var={} ... '{}' => '{}'", elem.getName(),
elem.getType().getCanonicalName(), specify.clazz.getCanonicalName());
break;
}
}
}
setValueFromDoc(type, data, elem, doc, lazyCall, options);
} }
} }
return data; return data;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess; package org.atriasoft.archidata.dataAccess;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
@ -14,37 +14,43 @@ import java.sql.Types;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalTime; import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import org.atriasoft.archidata.annotation.AnnotationTools;
import org.atriasoft.archidata.annotation.AnnotationTools.FieldName;
import org.atriasoft.archidata.annotation.CreationTimestamp;
import org.atriasoft.archidata.annotation.UpdateTimestamp;
import org.atriasoft.archidata.dataAccess.addOnSQL.AddOnDataJson;
import org.atriasoft.archidata.dataAccess.addOnSQL.AddOnManyToMany;
import org.atriasoft.archidata.dataAccess.addOnSQL.AddOnManyToManyLocal;
import org.atriasoft.archidata.dataAccess.addOnSQL.AddOnManyToOne;
import org.atriasoft.archidata.dataAccess.addOnSQL.AddOnOneToMany;
import org.atriasoft.archidata.dataAccess.addOnSQL.DataAccessAddOn;
import org.atriasoft.archidata.dataAccess.options.AccessDeletedItems;
import org.atriasoft.archidata.dataAccess.options.CheckFunction;
import org.atriasoft.archidata.dataAccess.options.Condition;
import org.atriasoft.archidata.dataAccess.options.DBInterfaceRoot;
import org.atriasoft.archidata.dataAccess.options.FilterValue;
import org.atriasoft.archidata.dataAccess.options.GroupBy;
import org.atriasoft.archidata.dataAccess.options.Limit;
import org.atriasoft.archidata.dataAccess.options.OptionSpecifyType;
import org.atriasoft.archidata.dataAccess.options.OrderBy;
import org.atriasoft.archidata.dataAccess.options.QueryOption;
import org.atriasoft.archidata.dataAccess.options.ReadAllColumn;
import org.atriasoft.archidata.dataAccess.options.TransmitKey;
import org.atriasoft.archidata.db.DbIoSql;
import org.atriasoft.archidata.exception.DataAccessException;
import org.atriasoft.archidata.tools.ConfigBaseVariable;
import org.atriasoft.archidata.tools.DateTools;
import org.atriasoft.archidata.tools.UuidUtils;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.AnnotationTools.FieldName;
import org.kar.archidata.annotation.CreationTimestamp;
import org.kar.archidata.annotation.UpdateTimestamp;
import org.kar.archidata.dataAccess.addOnSQL.AddOnDataJson;
import org.kar.archidata.dataAccess.addOnSQL.AddOnManyToMany;
import org.kar.archidata.dataAccess.addOnSQL.AddOnManyToOne;
import org.kar.archidata.dataAccess.addOnSQL.AddOnOneToMany;
import org.kar.archidata.dataAccess.addOnSQL.DataAccessAddOn;
import org.kar.archidata.dataAccess.options.CheckFunction;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.dataAccess.options.DBInterfaceRoot;
import org.kar.archidata.dataAccess.options.FilterValue;
import org.kar.archidata.dataAccess.options.GroupBy;
import org.kar.archidata.dataAccess.options.Limit;
import org.kar.archidata.dataAccess.options.OptionSpecifyType;
import org.kar.archidata.dataAccess.options.OrderBy;
import org.kar.archidata.dataAccess.options.QueryOption;
import org.kar.archidata.dataAccess.options.TransmitKey;
import org.kar.archidata.db.DbIoSql;
import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.kar.archidata.tools.DateTools;
import org.kar.archidata.tools.UuidUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -61,8 +67,12 @@ import jakarta.ws.rs.InternalServerErrorException;
public class DBAccessSQL extends DBAccess { public class DBAccessSQL extends DBAccess {
final static Logger LOGGER = LoggerFactory.getLogger(DBAccessSQL.class); final static Logger LOGGER = LoggerFactory.getLogger(DBAccessSQL.class);
// by default we manage some add-on that permit to manage non-native model (like json serialization, List of external key as String list...) // by default we manage some add-on that permit to manage non-native model (like json serialization, List of external key as String list...)
final static List<DataAccessAddOn> addOn = List.of(new AddOnManyToMany(), new AddOnManyToOne(), final static List<DataAccessAddOn> addOn = List.of(//
new AddOnOneToMany(), new AddOnDataJson()); new AddOnManyToMany(), //
new AddOnManyToOne(), //
new AddOnOneToMany(), //
new AddOnDataJson(), //
new AddOnManyToManyLocal());
private final DbIoSql db; private final DbIoSql db;
@ -223,16 +233,35 @@ public class DBAccessSQL extends DBAccess {
return out; return out;
} }
public byte[][] splitIntoGroupsOf12Bytes(final byte[] input) {
final int inputLength = input.length;
final int numOfGroups = (inputLength + 11) / 12; // Calculate the number of groups needed
final byte[][] groups = new byte[numOfGroups][12];
for (int i = 0; i < numOfGroups; i++) {
final int startIndex = i * 12;
final int endIndex = Math.min(startIndex + 12, inputLength);
groups[i] = Arrays.copyOfRange(input, startIndex, endIndex);
}
return groups;
}
public List<ObjectId> getListOfRawOIDs(final ResultSet rs, final int iii) throws SQLException, DataAccessException { public List<ObjectId> getListOfRawOIDs(final ResultSet rs, final int iii) throws SQLException, DataAccessException {
final byte[] trackString = rs.getBytes(iii); final byte[] trackString = rs.getBytes(iii);
if (rs.wasNull()) { if (rs.wasNull()) {
return null; return null;
} }
final byte[][] elements = splitIntoGroupsOf16Bytes(trackString); final byte[][] elements = splitIntoGroupsOf12Bytes(trackString);
final List<ObjectId> out = new ArrayList<>(); final List<ObjectId> out = new ArrayList<>();
for (final byte[] elem : elements) { for (final byte[] elem : elements) {
final ObjectId tmp = new ObjectId(elem); try {
out.add(tmp); final ObjectId tmp = new ObjectId(elem);
out.add(tmp);
} catch (final IllegalArgumentException ex) {
ex.printStackTrace();
LOGGER.error("Fail to parse the OID element: {}", elem);
}
} }
return out; return out;
} }
@ -259,8 +288,81 @@ public class DBAccessSQL extends DBAccess {
final CountInOut iii, final CountInOut iii,
final Field field, final Field field,
final PreparedStatement ps) throws Exception { final PreparedStatement ps) throws Exception {
if (type == ObjectId.class) { if (type == long.class) {
final Object tmp = field.get(data); ps.setLong(iii.value, field.getLong(data));
iii.inc();
return;
}
if (type == int.class) {
ps.setInt(iii.value, field.getInt(data));
iii.inc();
return;
}
if (type == float.class) {
ps.setFloat(iii.value, field.getFloat(data));
iii.inc();
return;
}
if (type == double.class) {
ps.setDouble(iii.value, field.getDouble(data));
iii.inc();
return;
}
if (type == boolean.class) {
ps.setBoolean(iii.value, field.getBoolean(data));
iii.inc();
return;
}
final Object tmp = field.get(data);
if (type.isEnum()) {
if (tmp == null) {
ps.setNull(iii.value, Types.VARCHAR);
} else {
ps.setString(iii.value, tmp.toString());
}
} else if (type == Long.class) {
if (tmp == null) {
ps.setNull(iii.value, Types.BIGINT);
} else {
ps.setLong(iii.value, (Long) tmp);
}
} else if (type == Integer.class) {
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
ps.setInt(iii.value, (Integer) tmp);
}
} else if (type == Float.class) {
if (tmp == null) {
ps.setNull(iii.value, Types.FLOAT);
} else {
ps.setFloat(iii.value, (Float) tmp);
}
} else if (type == Double.class) {
if (tmp == null) {
ps.setNull(iii.value, Types.DOUBLE);
} else {
ps.setDouble(iii.value, (Double) tmp);
}
} else if (type == Boolean.class) {
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
ps.setBoolean(iii.value, (Boolean) tmp);
}
} else if (type == String.class) {
if (tmp == null) {
ps.setNull(iii.value, Types.VARCHAR);
} else {
ps.setString(iii.value, (String) tmp);
}
} else if (type == Timestamp.class) {
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
ps.setTimestamp(iii.value, (Timestamp) tmp);
}
} else if (type == ObjectId.class) {
if (tmp == null) { if (tmp == null) {
ps.setNull(iii.value, Types.BINARY); ps.setNull(iii.value, Types.BINARY);
} else { } else {
@ -268,75 +370,31 @@ public class DBAccessSQL extends DBAccess {
ps.setBytes(iii.value, dataByte); ps.setBytes(iii.value, dataByte);
} }
} else if (type == UUID.class) { } else if (type == UUID.class) {
final Object tmp = field.get(data);
if (tmp == null) { if (tmp == null) {
ps.setNull(iii.value, Types.BINARY); ps.setNull(iii.value, Types.BINARY);
} else { } else {
final byte[] dataByte = UuidUtils.asBytes((UUID) tmp); final byte[] dataByte = UuidUtils.asBytes((UUID) tmp);
ps.setBytes(iii.value, dataByte); ps.setBytes(iii.value, dataByte);
} }
} else if (type == Long.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.BIGINT);
} else {
ps.setLong(iii.value, (Long) tmp);
}
} else if (type == long.class) {
ps.setLong(iii.value, field.getLong(data));
} else if (type == Integer.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
ps.setInt(iii.value, (Integer) tmp);
}
} else if (type == int.class) {
ps.setInt(iii.value, field.getInt(data));
} else if (type == Float.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.FLOAT);
} else {
ps.setFloat(iii.value, (Float) tmp);
}
} else if (type == float.class) {
ps.setFloat(iii.value, field.getFloat(data));
} else if (type == Double.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.DOUBLE);
} else {
ps.setDouble(iii.value, (Double) tmp);
}
} else if (type == Double.class) {
ps.setDouble(iii.value, field.getDouble(data));
} else if (type == Boolean.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
ps.setBoolean(iii.value, (Boolean) tmp);
}
} else if (type == boolean.class) {
ps.setBoolean(iii.value, field.getBoolean(data));
} else if (type == Timestamp.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
ps.setTimestamp(iii.value, (Timestamp) tmp);
}
} else if (type == Date.class) { } else if (type == Date.class) {
final Object tmp = field.get(data);
if (tmp == null) { if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER); ps.setNull(iii.value, Types.INTEGER);
} else { } else {
final Timestamp sqlDate = java.sql.Timestamp.from(((Date) tmp).toInstant()); final OffsetDateTime offsetDateTime = ((Date) tmp).toInstant().atZone(ZoneId.systemDefault())
.toOffsetDateTime();
final Timestamp sqlDate = java.sql.Timestamp
.from(offsetDateTime.withOffsetSameInstant(ZoneOffset.UTC).toInstant());
ps.setTimestamp(iii.value, sqlDate);
}
} else if (type == OffsetDateTime.class) {
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
final Timestamp sqlDate = java.sql.Timestamp
.from(((OffsetDateTime) tmp).withOffsetSameInstant(ZoneOffset.UTC).toInstant());
ps.setTimestamp(iii.value, sqlDate); ps.setTimestamp(iii.value, sqlDate);
} }
} else if (type == Instant.class) { } else if (type == Instant.class) {
final Object tmp = field.get(data);
if (tmp == null) { if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER); ps.setNull(iii.value, Types.INTEGER);
} else { } else {
@ -344,7 +402,6 @@ public class DBAccessSQL extends DBAccess {
ps.setString(iii.value, sqlDate); ps.setString(iii.value, sqlDate);
} }
} else if (type == LocalDate.class) { } else if (type == LocalDate.class) {
final Object tmp = field.get(data);
if (tmp == null) { if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER); ps.setNull(iii.value, Types.INTEGER);
} else { } else {
@ -352,27 +409,12 @@ public class DBAccessSQL extends DBAccess {
ps.setDate(iii.value, sqlDate); ps.setDate(iii.value, sqlDate);
} }
} else if (type == LocalTime.class) { } else if (type == LocalTime.class) {
final Object tmp = field.get(data);
if (tmp == null) { if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER); ps.setNull(iii.value, Types.INTEGER);
} else { } else {
final java.sql.Time sqlDate = java.sql.Time.valueOf((LocalTime) tmp); final java.sql.Time sqlDate = java.sql.Time.valueOf((LocalTime) tmp);
ps.setTime(iii.value, sqlDate); ps.setTime(iii.value, sqlDate);
} }
} else if (type == String.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.VARCHAR);
} else {
ps.setString(iii.value, (String) tmp);
}
} else if (type.isEnum()) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.VARCHAR);
} else {
ps.setString(iii.value, tmp.toString());
}
} else { } else {
throw new DataAccessException("Unknown Field Type: " + type.getCanonicalName()); throw new DataAccessException("Unknown Field Type: " + type.getCanonicalName());
} }
@ -501,7 +543,9 @@ public class DBAccessSQL extends DBAccess {
if (rs.wasNull()) { if (rs.wasNull()) {
field.set(data, null); field.set(data, null);
} else { } else {
field.set(data, Date.from(tmp.toInstant())); final OffsetDateTime odt = tmp.toInstant().atOffset(ZoneOffset.UTC);
odt.atZoneSameInstant(ZoneId.systemDefault());
field.set(data, Date.from(odt.toInstant()));
countNotNull.inc(); countNotNull.inc();
} }
} catch (final SQLException ex) { } catch (final SQLException ex) {
@ -516,6 +560,28 @@ public class DBAccessSQL extends DBAccess {
countNotNull.inc(); countNotNull.inc();
} }
} }
} else if (type == OffsetDateTime.class) {
try {
final Timestamp tmp = rs.getTimestamp(count.value);
if (rs.wasNull()) {
field.set(data, null);
} else {
final OffsetDateTime odt = tmp.toInstant().atOffset(ZoneOffset.UTC);
field.set(data, odt);
countNotNull.inc();
}
} catch (final SQLException ex) {
final String tmp = rs.getString(count.value);
LOGGER.error("Fail to parse the SQL time !!! {}", tmp);
if (rs.wasNull()) {
field.set(data, null);
} else {
final OffsetDateTime date = DateTools.parseOffsetDateTime(tmp);
LOGGER.error("Fail to parse the SQL time !!! {}", date);
field.set(data, date);
countNotNull.inc();
}
}
} else if (type == Instant.class) { } else if (type == Instant.class) {
final String tmp = rs.getString(count.value); final String tmp = rs.getString(count.value);
if (rs.wasNull()) { if (rs.wasNull()) {
@ -882,11 +948,13 @@ public class DBAccessSQL extends DBAccess {
continue; continue;
} }
final DataAccessAddOn addOn = findAddOnforField(field); final DataAccessAddOn addOn = findAddOnforField(field);
if (addOn != null && !addOn.canInsert(field)) { if (addOn != null) {
if (addOn.isInsertAsync(field)) { if (addOn.isInsertAsync(field)) {
asyncFieldUpdate.add(field); asyncFieldUpdate.add(field);
} }
continue; if (!addOn.canInsert(field)) {
continue;
}
} }
final boolean createTime = field.getDeclaredAnnotationsByType(CreationTimestamp.class).length != 0; final boolean createTime = field.getDeclaredAnnotationsByType(CreationTimestamp.class).length != 0;
if (createTime) { if (createTime) {
@ -1098,6 +1166,31 @@ public class DBAccessSQL extends DBAccess {
query.append("UPDATE `"); query.append("UPDATE `");
query.append(tableName); query.append(tableName);
query.append("` SET "); query.append("` SET ");
//Some mode need to get the previous data to perform a correct update...
boolean needPreviousValues = false;
for (final Field field : clazz.getFields()) {
// field is only for internal global declaration ==> remove it ..
if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
continue;
}
final FieldName name = AnnotationTools.getFieldName(field, options);
if (!filter.getValues().contains(name.inStruct())) {
continue;
} else if (AnnotationTools.isGenericField(field)) {
continue;
}
final DataAccessAddOn addOn = findAddOnforField(field);
if (addOn != null && addOn.isPreviousDataNeeded(field)) {
needPreviousValues = true;
break;
}
}
Object previousData = null;
if (needPreviousValues) {
final List<TransmitKey> transmitKey = options.get(TransmitKey.class);
previousData = this.get(data.getClass(), transmitKey.get(0).getKey(), new AccessDeletedItems(),
new ReadAllColumn());
}
boolean firstField = true; boolean firstField = true;
for (final Field field : clazz.getFields()) { for (final Field field : clazz.getFields()) {
@ -1112,17 +1205,19 @@ public class DBAccessSQL extends DBAccess {
continue; continue;
} }
final DataAccessAddOn addOn = findAddOnforField(field); final DataAccessAddOn addOn = findAddOnforField(field);
if (addOn != null && !addOn.canInsert(field)) { if (addOn != null) {
if (addOn.isInsertAsync(field)) { if (addOn.isUpdateAsync(field)) {
final List<TransmitKey> transmitKey = options.get(TransmitKey.class); final List<TransmitKey> transmitKey = options.get(TransmitKey.class);
if (transmitKey.size() != 1) { if (transmitKey.size() != 1) {
throw new DataAccessException( throw new DataAccessException(
"Fail to transmit Key to update the async update... (must have only 1)"); "Fail to transmit Key to update the async update... (must have only 1)");
} }
addOn.asyncUpdate(this, tableName, transmitKey.get(0).getKey(), field, field.get(data), addOn.asyncUpdate(this, previousData, tableName, transmitKey.get(0).getKey(), field,
asyncActions, options); field.get(data), asyncActions, options);
}
if (!addOn.canInsert(field)) {
continue;
} }
continue;
} }
if (!field.getClass().isPrimitive()) { if (!field.getClass().isPrimitive()) {
final Object tmp = field.get(data); final Object tmp = field.get(data);
@ -1147,7 +1242,6 @@ public class DBAccessSQL extends DBAccess {
query.append(" "); query.append(" ");
final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz); final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
condition.whereAppendQuery(query, tableName, null, deletedFieldName); condition.whereAppendQuery(query, tableName, null, deletedFieldName);
// If the first field is not set, then nothing to update n the main base: // If the first field is not set, then nothing to update n the main base:
if (!firstField) { if (!firstField) {
LOGGER.debug("generate update query: '{}'", query.toString()); LOGGER.debug("generate update query: '{}'", query.toString());
@ -1183,6 +1277,9 @@ public class DBAccessSQL extends DBAccess {
addOn.insertData(this, ps, field, data, iii); addOn.insertData(this, ps, field, data, iii);
} }
} }
for (final LazyGetter action : asyncActions) {
action.doRequest();
}
condition.injectQuery(this, ps, iii); condition.injectQuery(this, ps, iii);
final int out = ps.executeUpdate(); final int out = ps.executeUpdate();
return out; return out;
@ -1191,9 +1288,6 @@ public class DBAccessSQL extends DBAccess {
} catch (final SQLException ex) { } catch (final SQLException ex) {
ex.printStackTrace(); ex.printStackTrace();
} }
for (final LazyGetter action : asyncActions) {
action.doRequest();
}
return 0L; return 0L;
} }
@ -1276,7 +1370,7 @@ public class DBAccessSQL extends DBAccess {
) throws Exception { ) throws Exception {
final boolean readAllfields = QueryOptions.readAllColomn(options); final boolean readAllfields = QueryOptions.readAllColomn(options);
final String tableName = AnnotationTools.getTableName(clazz, options); final String tableName = AnnotationTools.getTableName(clazz, options);
final String primaryKey = AnnotationTools.getPrimaryKeyField(clazz).getName(); final String primaryKey = AnnotationTools.getFieldNameRaw(AnnotationTools.getPrimaryKeyField(clazz));
boolean firstField = true; boolean firstField = true;
for (final Field elem : clazz.getFields()) { for (final Field elem : clazz.getFields()) {
@ -1364,7 +1458,7 @@ public class DBAccessSQL extends DBAccess {
final T out = (T) data; final T out = (T) data;
outs.add(out); outs.add(out);
} }
LOGGER.info("Async calls: {}", lazyCall.size()); LOGGER.trace("Async calls: {}", lazyCall.size());
for (final LazyGetter elem : lazyCall) { for (final LazyGetter elem : lazyCall) {
elem.doRequest(); elem.doRequest();
} }

View File

@ -1,14 +1,15 @@
package org.kar.archidata.dataAccess; package org.atriasoft.archidata.dataAccess;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import org.kar.archidata.dataAccess.options.Condition; import org.atriasoft.archidata.dataAccess.options.Condition;
import org.kar.archidata.dataAccess.options.QueryOption; import org.atriasoft.archidata.dataAccess.options.QueryOption;
import org.kar.archidata.exception.DataAccessException; import org.atriasoft.archidata.exception.DataAccessException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import edu.umd.cs.findbugs.annotations.Nullable;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.ws.rs.InternalServerErrorException; import jakarta.ws.rs.InternalServerErrorException;
@ -110,12 +111,6 @@ public class DataAccess {
} }
} }
/** @param <T>
* @param data
* @param id
* @param filterValue
* @return the affected rows.
* @throws Exception */
public static <T, ID_TYPE> long update( public static <T, ID_TYPE> long update(
final T data, final T data,
final ID_TYPE id, final ID_TYPE id,
@ -189,6 +184,7 @@ public class DataAccess {
} }
} }
@Nullable
public static <T, ID_TYPE> T get(final Class<T> clazz, final ID_TYPE id, final QueryOption... options) public static <T, ID_TYPE> T get(final Class<T> clazz, final ID_TYPE id, final QueryOption... options)
throws Exception { throws Exception {
try (DBAccess db = DBAccess.createInterface()) { try (DBAccess db = DBAccess.createInterface()) {
@ -223,7 +219,6 @@ public class DataAccess {
/** Delete items with the specific condition and some options. If the Entity is manage as a softDeleted model, then it is flag as removed (if not already done before). /** Delete items with the specific condition and some options. If the Entity is manage as a softDeleted model, then it is flag as removed (if not already done before).
* @param clazz Data model that might remove element. * @param clazz Data model that might remove element.
* @param condition Condition to remove elements.
* @param options (Optional) Options of the request. * @param options (Optional) Options of the request.
* @return Number of element that is removed. */ * @return Number of element that is removed. */
public static long deleteWhere(final Class<?> clazz, final QueryOption... options) throws Exception { public static long deleteWhere(final Class<?> clazz, final QueryOption... options) throws Exception {

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess; package org.atriasoft.archidata.dataAccess;
import java.io.IOException; import java.io.IOException;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
@ -16,16 +16,16 @@ import java.util.Date;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import org.kar.archidata.dataAccess.exportTools.TableQuery; import org.atriasoft.archidata.dataAccess.exportTools.TableQuery;
import org.kar.archidata.dataAccess.exportTools.TableQueryTypes; import org.atriasoft.archidata.dataAccess.exportTools.TableQueryTypes;
import org.kar.archidata.dataAccess.options.Condition; import org.atriasoft.archidata.dataAccess.options.Condition;
import org.kar.archidata.dataAccess.options.GroupBy; import org.atriasoft.archidata.dataAccess.options.GroupBy;
import org.kar.archidata.dataAccess.options.Limit; import org.atriasoft.archidata.dataAccess.options.Limit;
import org.kar.archidata.dataAccess.options.OrderBy; import org.atriasoft.archidata.dataAccess.options.OrderBy;
import org.kar.archidata.dataAccess.options.QueryOption; import org.atriasoft.archidata.dataAccess.options.QueryOption;
import org.kar.archidata.exception.DataAccessException; import org.atriasoft.archidata.exception.DataAccessException;
import org.kar.archidata.tools.ContextGenericTools; import org.atriasoft.archidata.tools.ContextGenericTools;
import org.kar.archidata.tools.DateTools; import org.atriasoft.archidata.tools.DateTools;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess; package org.atriasoft.archidata.dataAccess;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.sql.Timestamp; import java.sql.Timestamp;
@ -10,16 +10,16 @@ import java.util.Date;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import org.atriasoft.archidata.annotation.AnnotationTools;
import org.atriasoft.archidata.annotation.CreationTimestamp;
import org.atriasoft.archidata.annotation.DataIfNotExists;
import org.atriasoft.archidata.annotation.UpdateTimestamp;
import org.atriasoft.archidata.dataAccess.addOnSQL.DataAccessAddOn;
import org.atriasoft.archidata.dataAccess.options.CreateDropTable;
import org.atriasoft.archidata.dataAccess.options.OptionSpecifyType;
import org.atriasoft.archidata.exception.DataAccessException;
import org.atriasoft.archidata.tools.ConfigBaseVariable;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.CreationTimestamp;
import org.kar.archidata.annotation.DataIfNotExists;
import org.kar.archidata.annotation.UpdateTimestamp;
import org.kar.archidata.dataAccess.addOnSQL.DataAccessAddOn;
import org.kar.archidata.dataAccess.options.CreateDropTable;
import org.kar.archidata.dataAccess.options.OptionSpecifyType;
import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess; package org.atriasoft.archidata.dataAccess;
// Mark as deprecated while the concept is not ready ... // Mark as deprecated while the concept is not ready ...
@Deprecated @Deprecated

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess; package org.atriasoft.archidata.dataAccess;
public interface LazyGetter { public interface LazyGetter {
void doRequest() throws Exception; void doRequest() throws Exception;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess; package org.atriasoft.archidata.dataAccess;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.util.ArrayList; import java.util.ArrayList;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess; package org.atriasoft.archidata.dataAccess;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.util.List; import java.util.List;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess; package org.atriasoft.archidata.dataAccess;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.util.List; import java.util.List;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess; package org.atriasoft.archidata.dataAccess;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.util.List; import java.util.List;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess; package org.atriasoft.archidata.dataAccess;
import java.util.List; import java.util.List;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess; package org.atriasoft.archidata.dataAccess;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.util.List; import java.util.List;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess; package org.atriasoft.archidata.dataAccess;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.util.List; import java.util.List;

View File

@ -1,12 +1,12 @@
package org.kar.archidata.dataAccess; package org.atriasoft.archidata.dataAccess;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.kar.archidata.dataAccess.options.AccessDeletedItems; import org.atriasoft.archidata.dataAccess.options.AccessDeletedItems;
import org.kar.archidata.dataAccess.options.CreateDropTable; import org.atriasoft.archidata.dataAccess.options.CreateDropTable;
import org.kar.archidata.dataAccess.options.QueryOption; import org.atriasoft.archidata.dataAccess.options.QueryOption;
import org.kar.archidata.dataAccess.options.ReadAllColumn; import org.atriasoft.archidata.dataAccess.options.ReadAllColumn;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess; package org.atriasoft.archidata.dataAccess;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.util.ArrayList; import java.util.ArrayList;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess; package org.atriasoft.archidata.dataAccess;
import java.sql.ResultSet; import java.sql.ResultSet;

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess.addOnMongo; package org.atriasoft.archidata.dataAccess.addOnMongo;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
@ -6,23 +6,23 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import org.atriasoft.archidata.annotation.AnnotationTools;
import org.atriasoft.archidata.annotation.AnnotationTools.FieldName;
import org.atriasoft.archidata.dataAccess.CountInOut;
import org.atriasoft.archidata.dataAccess.DBAccess;
import org.atriasoft.archidata.dataAccess.DBAccessMorphia;
import org.atriasoft.archidata.dataAccess.DataFactory;
import org.atriasoft.archidata.dataAccess.LazyGetter;
import org.atriasoft.archidata.dataAccess.QueryAnd;
import org.atriasoft.archidata.dataAccess.QueryCondition;
import org.atriasoft.archidata.dataAccess.QueryOptions;
import org.atriasoft.archidata.dataAccess.addOnSQL.model.LinkTableGeneric;
import org.atriasoft.archidata.dataAccess.options.Condition;
import org.atriasoft.archidata.dataAccess.options.OptionSpecifyType;
import org.atriasoft.archidata.dataAccess.options.OverrideTableName;
import org.atriasoft.archidata.exception.DataAccessException;
import org.atriasoft.archidata.tools.ConfigBaseVariable;
import org.bson.Document; import org.bson.Document;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.AnnotationTools.FieldName;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DBAccess;
import org.kar.archidata.dataAccess.DBAccessMorphia;
import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.LazyGetter;
import org.kar.archidata.dataAccess.QueryAnd;
import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.dataAccess.addOnSQL.model.LinkTableGeneric;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.dataAccess.options.OptionSpecifyType;
import org.kar.archidata.dataAccess.options.OverrideTableName;
import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -173,34 +173,6 @@ public class AddOnManyToMany implements DataAccessAddOn {
count.inc(); count.inc();
} }
@Override
public void generateQuery(
@NotNull final String tableName,
@NotNull final String primaryKey,
@NotNull final Field field,
@NotNull final StringBuilder querySelect,
@NotNull final StringBuilder query,
@NotNull final String name,
@NotNull final CountInOut count,
final QueryOptions options) throws Exception {
if (field.getType() != List.class) {
return;
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass == Long.class || objectClass == UUID.class) {
generateConcatQuery(tableName, primaryKey, field, querySelect, query, name, count, options);
}
final ManyToMany decorators = field.getDeclaredAnnotation(ManyToMany.class);
if (decorators == null) {
return;
}
if (objectClass == decorators.targetEntity()) {
generateConcatQuery(tableName, primaryKey, field, querySelect, query, name, count, options);
}
}
@Override @Override
public void fillFromDoc( public void fillFromDoc(
final DBAccessMorphia ioDb, final DBAccessMorphia ioDb,

View File

@ -1,22 +1,21 @@
package org.kar.archidata.dataAccess.addOnMongo; package org.atriasoft.archidata.dataAccess.addOnMongo;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import org.atriasoft.archidata.annotation.AnnotationTools;
import org.atriasoft.archidata.annotation.AnnotationTools.FieldName;
import org.atriasoft.archidata.dataAccess.DBAccessMorphia;
import org.atriasoft.archidata.dataAccess.DataFactory;
import org.atriasoft.archidata.dataAccess.LazyGetter;
import org.atriasoft.archidata.dataAccess.QueryOptions;
import org.bson.Document; import org.bson.Document;
import org.kar.archidata.annotation.AnnotationTools; import org.bson.types.ObjectId;
import org.kar.archidata.annotation.AnnotationTools.FieldName;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DBAccessMorphia;
import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.LazyGetter;
import org.kar.archidata.dataAccess.QueryOptions;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import jakarta.persistence.ManyToOne; import jakarta.persistence.ManyToOne;
import jakarta.validation.constraints.NotNull;
public class AddOnManyToOne implements DataAccessAddOn { public class AddOnManyToOne implements DataAccessAddOn {
static final Logger LOGGER = LoggerFactory.getLogger(AddOnManyToMany.class); static final Logger LOGGER = LoggerFactory.getLogger(AddOnManyToMany.class);
@ -52,7 +51,7 @@ public class AddOnManyToOne implements DataAccessAddOn {
final Document docUnSet) throws Exception { final Document docUnSet) throws Exception {
final FieldName fieldName = AnnotationTools.getFieldName(field, options); final FieldName fieldName = AnnotationTools.getFieldName(field, options);
final Object data = field.get(rootObject); final Object data = field.get(rootObject);
if (field.get(data) == null) { if (data == null) {
docUnSet.append(fieldName.inTable(), ""); docUnSet.append(fieldName.inTable(), "");
return; return;
} else if (field.getType() == Long.class) { } else if (field.getType() == Long.class) {
@ -70,6 +69,9 @@ public class AddOnManyToOne implements DataAccessAddOn {
} else if (field.getType() == UUID.class) { } else if (field.getType() == UUID.class) {
final UUID dataTyped = (UUID) data; final UUID dataTyped = (UUID) data;
docSet.append(fieldName.inTable(), dataTyped); docSet.append(fieldName.inTable(), dataTyped);
} else if (field.getType() == ObjectId.class) {
final ObjectId dataTyped = (ObjectId) data;
docSet.append(fieldName.inTable(), dataTyped);
} else { } else {
final Field idField = AnnotationTools.getFieldOfId(field.getType()); final Field idField = AnnotationTools.getFieldOfId(field.getType());
final Object uid = idField.get(data); final Object uid = idField.get(data);
@ -84,7 +86,8 @@ public class AddOnManyToOne implements DataAccessAddOn {
@Override @Override
public boolean canInsert(final Field field) { public boolean canInsert(final Field field) {
if (field.getType() == Long.class || field.getType() == Integer.class || field.getType() == Short.class if (field.getType() == Long.class || field.getType() == Integer.class || field.getType() == Short.class
|| field.getType() == String.class || field.getType() == UUID.class) { || field.getType() == String.class || field.getType() == UUID.class
|| field.getType() == ObjectId.class) {
return true; return true;
} }
final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class); final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class);
@ -103,7 +106,7 @@ public class AddOnManyToOne implements DataAccessAddOn {
public boolean canRetrieve(final Field field) { public boolean canRetrieve(final Field field) {
final Class<?> classType = field.getType(); final Class<?> classType = field.getType();
if (classType == Long.class || classType == Integer.class || classType == Short.class if (classType == Long.class || classType == Integer.class || classType == Short.class
|| classType == String.class || classType == UUID.class) { || classType == String.class || classType == UUID.class || classType == ObjectId.class) {
return true; return true;
} }
final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class); final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class);
@ -113,36 +116,6 @@ public class AddOnManyToOne implements DataAccessAddOn {
return false; return false;
} }
@Override
public void generateQuery(
@NotNull final String tableName,
@NotNull final String primaryKey,
@NotNull final Field field,
@NotNull final StringBuilder querySelect,
@NotNull final StringBuilder query,
@NotNull final String name,
@NotNull final CountInOut count,
final QueryOptions options) throws Exception {
if (field.getType() == Long.class || field.getType() == Integer.class || field.getType() == Short.class
|| field.getType() == String.class || field.getType() == UUID.class) {
querySelect.append(" ");
querySelect.append(tableName);
querySelect.append(".");
querySelect.append(name);
count.inc();
return;
}
final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class);
if (field.getType() == decorators.targetEntity()) {
// no eager possible for no sql
querySelect.append(" ");
querySelect.append(tableName);
querySelect.append(".");
querySelect.append(name);
count.inc();
}
}
@Override @Override
public void fillFromDoc( public void fillFromDoc(
final DBAccessMorphia ioDb, final DBAccessMorphia ioDb,
@ -159,7 +132,8 @@ public class AddOnManyToOne implements DataAccessAddOn {
} }
// local field to manage no remote object to retrieve. // local field to manage no remote object to retrieve.
if (field.getType() == Long.class || field.getType() == Integer.class || field.getType() == Short.class if (field.getType() == Long.class || field.getType() == Integer.class || field.getType() == Short.class
|| field.getType() == String.class || field.getType() == UUID.class) { || field.getType() == String.class || field.getType() == UUID.class
|| field.getType() == ObjectId.class) {
ioDb.setValueFromDoc(field.getType(), data, field, doc, lazyCall, options); ioDb.setValueFromDoc(field.getType(), data, field, doc, lazyCall, options);
return; return;
} }
@ -174,9 +148,8 @@ public class AddOnManyToOne implements DataAccessAddOn {
final Class<?> remotePrimaryKeyType = remotePrimaryKeyField.getType(); final Class<?> remotePrimaryKeyType = remotePrimaryKeyField.getType();
if (remotePrimaryKeyType == Long.class) { if (remotePrimaryKeyType == Long.class) {
// here we have the field, the data and the the remote value ==> can create callback that generate the update of the value ... // here we have the field, the data and the the remote value ==> can create callback that generate the update of the value ...
final Long foreignKey = doc.getLong(fieldName); final Long foreignKey = doc.getLong(fieldName.inTable());
if (foreignKey != null) { if (foreignKey != null) {
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
final LazyGetter lambda = () -> { final LazyGetter lambda = () -> {
// TODO: update to have get with abstract types .... // TODO: update to have get with abstract types ....
final Object foreignData = ioDb.get(decorators.targetEntity(), foreignKey); final Object foreignData = ioDb.get(decorators.targetEntity(), foreignKey);
@ -189,9 +162,22 @@ public class AddOnManyToOne implements DataAccessAddOn {
} }
} else if (remotePrimaryKeyType == UUID.class) { } else if (remotePrimaryKeyType == UUID.class) {
// here we have the field, the data and the the remote value ==> can create callback that generate the update of the value ... // here we have the field, the data and the the remote value ==> can create callback that generate the update of the value ...
final UUID foreignKey = doc.get(fieldName, UUID.class); final UUID foreignKey = doc.get(fieldName.inTable(), UUID.class);
if (foreignKey != null) {
final LazyGetter lambda = () -> {
// TODO: update to have get with abstract types ....
final Object foreignData = ioDb.get(decorators.targetEntity(), foreignKey);
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
}
} else if (remotePrimaryKeyType == ObjectId.class) {
// here we have the field, the data and the the remote value ==> can create callback that generate the update of the value ...
final ObjectId foreignKey = doc.get(fieldName.inTable(), ObjectId.class);
if (foreignKey != null) { if (foreignKey != null) {
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
final LazyGetter lambda = () -> { final LazyGetter lambda = () -> {
// TODO: update to have get with abstract types .... // TODO: update to have get with abstract types ....
final Object foreignData = ioDb.get(decorators.targetEntity(), foreignKey); final Object foreignData = ioDb.get(decorators.targetEntity(), foreignKey);
@ -221,7 +207,7 @@ public class AddOnManyToOne implements DataAccessAddOn {
final QueryOptions options) throws Exception { final QueryOptions options) throws Exception {
final Class<?> classType = field.getType(); final Class<?> classType = field.getType();
if (classType == Long.class || classType == Integer.class || classType == Short.class if (classType == Long.class || classType == Integer.class || classType == Short.class
|| classType == String.class || classType == UUID.class) { || classType == String.class || classType == UUID.class || classType == ObjectId.class) {
DataFactory.createTablesSpecificType(tableName, primaryField, field, mainTableBuilder, preActionList, DataFactory.createTablesSpecificType(tableName, primaryField, field, mainTableBuilder, preActionList,
postActionList, createIfNotExist, createDrop, fieldId, classType, options); postActionList, createIfNotExist, createDrop, fieldId, classType, options);
} else { } else {

View File

@ -0,0 +1,166 @@
package org.atriasoft.archidata.dataAccess.addOnMongo;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.atriasoft.archidata.annotation.AnnotationTools;
import org.atriasoft.archidata.annotation.AnnotationTools.FieldName;
import org.atriasoft.archidata.dataAccess.DBAccessMorphia;
import org.atriasoft.archidata.dataAccess.LazyGetter;
import org.atriasoft.archidata.dataAccess.QueryCondition;
import org.atriasoft.archidata.dataAccess.QueryOptions;
import org.atriasoft.archidata.dataAccess.options.Condition;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.persistence.OneToMany;
public class AddOnOneToMany implements DataAccessAddOn {
static final Logger LOGGER = LoggerFactory.getLogger(AddOnOneToMany.class);
static final String SEPARATOR_LONG = "-";
@Override
public Class<?> getAnnotationClass() {
return OneToMany.class;
}
@Override
public boolean isCompatibleField(final Field field) {
final OneToMany decorators = field.getDeclaredAnnotation(OneToMany.class);
return decorators != null;
}
@Override
public void insertData(
final DBAccessMorphia ioDb,
final Field field,
final Object rootObject,
final QueryOptions options,
final Document docSet,
final Document docUnSet) throws Exception {
throw new IllegalAccessException("Can not generate an inset of @OneToMany");
}
@Override
public boolean canInsert(final Field field) {
return false;
}
@Override
public boolean isInsertAsync(final Field field) throws Exception {
// TODO: can be implemented later...
return false;
}
@Override
public boolean canRetrieve(final Field field) {
if (field.getType() != List.class) {
return false;
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass == Long.class || objectClass == UUID.class || objectClass == ObjectId.class) {
return true;
}
final OneToMany decorators = field.getDeclaredAnnotation(OneToMany.class);
if (decorators == null) {
return false;
}
if (decorators.targetEntity() == objectClass) {
return true;
}
return false;
}
// in first implementation we did not keep the data in the 2 Objects, bun we will do it after to have a faster table interactions.
@Override
public void fillFromDoc(
final DBAccessMorphia ioDb,
final Document doc,
final Field field,
final Object data,
final QueryOptions options,
final List<LazyGetter> lazyCall) throws Exception {
if (field.getType() != List.class) {
LOGGER.error("Can not OneToMany with other than List Model: {}", field.getType().getCanonicalName());
return;
}
final FieldName fieldName = AnnotationTools.getFieldName(field, options);
// in step 1 the fields are not stored in the local element
// if (!doc.containsKey(fieldName.inTable())) {
// field.set(data, null);
// return;
// }
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
final OneToMany decorators = field.getDeclaredAnnotation(OneToMany.class);
if (decorators == null) {
return;
}
if (objectClass == Long.class || objectClass == UUID.class || objectClass == ObjectId.class) {
if (true) {
// DEVELOPMENT step 1 we search all the element in the list:
// get the curentObject primary key
final Field primaryField = AnnotationTools.getPrimaryKeyField(data.getClass());
final String primaryKeyName = AnnotationTools.getFieldNameRaw(primaryField);
final Object primaryKey = doc.get(primaryKeyName, primaryField.getType());
// get the remotes objects
final List<?> returnValue = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryCondition(decorators.mappedBy(), "=", primaryKey)));
// extract the primary key of the remote objects
final Field remotePrimaryField = AnnotationTools.getPrimaryKeyField(decorators.targetEntity());
final String remotePrimaryKeyName = AnnotationTools.getFieldNameRaw(remotePrimaryField);
final List<Object> listOfRemoteKeys = new ArrayList<>();
for (final var item : returnValue) {
listOfRemoteKeys.add(remotePrimaryField.get(item));
}
// inject in the current data field
if (listOfRemoteKeys.size() != 0) {
field.set(data, listOfRemoteKeys);
}
} else {
// DEVELOPMENT In step 2 this will work well:
final Object value = doc.get(fieldName.inTable(), field.getType());
field.set(data, value);
}
return;
}
if (objectClass == decorators.targetEntity()) {
// Maybe in a second step we do not like this but this way is efficient too.
// get the curentObject primary key
final Field primaryField = AnnotationTools.getPrimaryKeyField(data.getClass());
final String primaryKeyName = AnnotationTools.getFieldNameRaw(primaryField);
final Object primaryKey = doc.get(primaryKeyName, primaryField.getType());
// get the remotes objects
final List<?> returnValue = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryCondition(decorators.mappedBy(), "=", primaryKey)));
// inject in the current data field
if (returnValue.size() != 0) {
field.set(data, returnValue);
}
}
}
// TODO : refacto this table to manage a generic table with dynamic name to be serialize with the default system
@Override
public void createTables(
final String tableName,
final Field primaryField,
final Field field,
final StringBuilder mainTableBuilder,
final List<String> preActionList,
final List<String> postActionList,
final boolean createIfNotExist,
final boolean createDrop,
final int fieldId,
final QueryOptions options) throws Exception {
// This is a remote field ==> nothing to generate (it is stored in the remote object
}
}

View File

@ -1,16 +1,13 @@
package org.kar.archidata.dataAccess.addOnMongo; package org.atriasoft.archidata.dataAccess.addOnMongo;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List; import java.util.List;
import org.atriasoft.archidata.dataAccess.DBAccessMorphia;
import org.atriasoft.archidata.dataAccess.LazyGetter;
import org.atriasoft.archidata.dataAccess.QueryOptions;
import org.bson.Document; import org.bson.Document;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DBAccessMorphia;
import org.kar.archidata.dataAccess.LazyGetter;
import org.kar.archidata.dataAccess.QueryOptions;
import jakarta.validation.constraints.NotNull;
public interface DataAccessAddOn { public interface DataAccessAddOn {
/** Get the Class of the declaration annotation /** Get the Class of the declaration annotation
@ -29,12 +26,6 @@ public interface DataAccessAddOn {
* @return True of the field is manage by the current Add-on. */ * @return True of the field is manage by the current Add-on. */
boolean isCompatibleField(Field elem); boolean isCompatibleField(Field elem);
/** Insert data in the specific field (the field must be in the current db, otherwiise it does not work at all.
* @param ps DB statement interface.
* @param data The date to inject.
* @param iii The index of injection
* @return the new index of injection in case of multiple value management
* @throws SQLException */
void insertData( void insertData(
final DBAccessMorphia ioDb, final DBAccessMorphia ioDb,
final Field field, final Field field,
@ -57,16 +48,6 @@ public interface DataAccessAddOn {
return false; return false;
} }
void generateQuery(
@NotNull String tableName,
@NotNull final String primaryKey,
@NotNull Field field,
@NotNull final StringBuilder querySelect,
@NotNull final StringBuilder query,
@NotNull String name,
@NotNull CountInOut count,
QueryOptions options) throws Exception;
// Return the number of colomn read // Return the number of colomn read
void fillFromDoc( void fillFromDoc(
final DBAccessMorphia ioDb, final DBAccessMorphia ioDb,
@ -78,14 +59,7 @@ public interface DataAccessAddOn {
throws Exception, SQLException, IllegalArgumentException, IllegalAccessException; throws Exception, SQLException, IllegalArgumentException, IllegalAccessException;
/** Create associated table of the specific element. /** Create associated table of the specific element.
* @param tableName */
* @param elem
* @param mainTableBuilder
* @param ListOtherTables
* @param createIfNotExist
* @param createDrop
* @param fieldId
* @throws Exception */
void createTables( void createTables(
String tableName, String tableName,
final Field primaryField, final Field primaryField,

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess.addOnSQL; package org.atriasoft.archidata.dataAccess.addOnSQL;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
@ -10,23 +10,22 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import org.atriasoft.archidata.annotation.AnnotationTools;
import org.atriasoft.archidata.annotation.AnnotationTools.FieldName;
import org.atriasoft.archidata.annotation.DataJson;
import org.atriasoft.archidata.dataAccess.CountInOut;
import org.atriasoft.archidata.dataAccess.DBAccess;
import org.atriasoft.archidata.dataAccess.DBAccessSQL;
import org.atriasoft.archidata.dataAccess.DataFactory;
import org.atriasoft.archidata.dataAccess.LazyGetter;
import org.atriasoft.archidata.dataAccess.QueryOptions;
import org.atriasoft.archidata.dataAccess.addOnSQL.model.TableCoversGeneric;
import org.atriasoft.archidata.dataAccess.options.OptionRenameColumn;
import org.atriasoft.archidata.dataAccess.options.OptionSpecifyType;
import org.atriasoft.archidata.dataAccess.options.OverrideTableName;
import org.atriasoft.archidata.exception.DataAccessException;
import org.atriasoft.archidata.tools.ContextGenericTools;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.AnnotationTools.FieldName;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DBAccess;
import org.kar.archidata.dataAccess.DBAccessMorphia;
import org.kar.archidata.dataAccess.DBAccessSQL;
import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.LazyGetter;
import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.dataAccess.addOnSQL.model.TableCoversGeneric;
import org.kar.archidata.dataAccess.options.OptionRenameColumn;
import org.kar.archidata.dataAccess.options.OptionSpecifyType;
import org.kar.archidata.dataAccess.options.OverrideTableName;
import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.tools.ContextGenericTools;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -220,68 +219,63 @@ public class AddOnDataJson implements DataAccessAddOn {
public static void addLink( public static void addLink(
final DBAccess ioDb, final DBAccess ioDb,
final Class<?> clazz, final Class<?> clazz,
final String columnId, String clazzPrimaryKeyName,
final Object id, final Object clazzPrimaryKeyValue,
final String columnList, final String fieldNameToUpdate,
final Object remoteKey) throws Exception { final Object valueToAdd) throws Exception {
final String tableName = AnnotationTools.getTableName(clazz); final String tableName = AnnotationTools.getTableName(clazz);
final QueryOptions options = new QueryOptions(new OverrideTableName(tableName), final QueryOptions options = new QueryOptions(new OverrideTableName(tableName),
new OptionSpecifyType("id", id.getClass()), new OptionSpecifyType("idOfTheObject", clazzPrimaryKeyValue.getClass()),
new OptionSpecifyType("covers", remoteKey.getClass(), true)); new OptionSpecifyType("filedNameOfTheObject", valueToAdd.getClass(), true));
if (columnId != null && !columnId.equals("id")) { if (clazzPrimaryKeyName == null) {
options.add(new OptionRenameColumn("id", columnId)); clazzPrimaryKeyName = "id";
} }
if (columnList != null && !columnList.equals("covers")) { options.add(new OptionRenameColumn("idOfTheObject", clazzPrimaryKeyName));
options.add(new OptionRenameColumn("covers", columnList)); options.add(new OptionRenameColumn("filedNameOfTheObject", fieldNameToUpdate));
final TableCoversGeneric data = ioDb.get(TableCoversGeneric.class, clazzPrimaryKeyValue, options.getAllArray());
if (data.filedNameOfTheObject == null) {
data.filedNameOfTheObject = new ArrayList<>();
} }
final TableCoversGeneric data = ioDb.get(TableCoversGeneric.class, id, options.getAllArray()); for (final Object elem : data.filedNameOfTheObject) {
if (data.covers == null) { if (elem.equals(valueToAdd)) {
data.covers = new ArrayList<>();
}
for (final Object elem : data.covers) {
if (elem.equals(remoteKey)) {
return; return;
} }
} }
data.covers.add(remoteKey); data.filedNameOfTheObject.add(valueToAdd);
ioDb.update(data, data.id, List.of("covers"), options.getAllArray()); ioDb.update(data, data.idOfTheObject, List.of("filedNameOfTheObject"), options.getAllArray());
} }
public static void removeLink( public static void removeLink(
final DBAccess ioDb, final DBAccess ioDb,
final Class<?> clazz, final Class<?> clazz,
final String columnId, String clazzPrimaryKeyName,
final Object id, final Object clazzPrimaryKeyValue,
final String columnList, final String fieldNameToUpdate,
final Object remoteKey) throws Exception { final Object valueToRemove) throws Exception {
if (ioDb instanceof final DBAccessSQL daSQL) { final String tableName = AnnotationTools.getTableName(clazz);
final String tableName = AnnotationTools.getTableName(clazz); final QueryOptions options = new QueryOptions(new OverrideTableName(tableName),
final QueryOptions options = new QueryOptions(new OverrideTableName(tableName), new OptionSpecifyType("idOfTheObject", clazzPrimaryKeyValue.getClass()),
new OptionSpecifyType("id", id.getClass()), new OptionSpecifyType("filedNameOfTheObject", valueToRemove.getClass(), true));
new OptionSpecifyType("covers", remoteKey.getClass(), true)); if (clazzPrimaryKeyName == null) {
if (columnId != null && !columnId.equals("id")) { clazzPrimaryKeyName = "id";
options.add(new OptionRenameColumn("id", columnId));
}
if (columnList != null && !columnList.equals("covers")) {
options.add(new OptionRenameColumn("covers", columnList));
}
final TableCoversGeneric data = ioDb.get(TableCoversGeneric.class, id, options.getAllArray());
if (data.covers == null) {
return;
}
final List<Object> newList = new ArrayList<>();
for (final Object elem : data.covers) {
if (elem.equals(remoteKey)) {
continue;
}
newList.add(elem);
}
data.covers = newList;
ioDb.update(data, data.id, List.of("covers"), options.getAllArray());
} else if (ioDb instanceof final DBAccessMorphia dam) {
} else {
throw new DataAccessException("DataAccess Not managed");
} }
options.add(new OptionRenameColumn("idOfTheObject", clazzPrimaryKeyName));
options.add(new OptionRenameColumn("filedNameOfTheObject", fieldNameToUpdate));
final TableCoversGeneric data = ioDb.get(TableCoversGeneric.class, clazzPrimaryKeyValue, options.getAllArray());
if (data.filedNameOfTheObject == null) {
return;
}
final List<Object> newList = new ArrayList<>();
for (final Object elem : data.filedNameOfTheObject) {
if (elem.equals(valueToRemove)) {
continue;
}
newList.add(elem);
}
data.filedNameOfTheObject = newList;
if (data.filedNameOfTheObject.isEmpty()) {
data.filedNameOfTheObject = null;
}
ioDb.update(data, data.idOfTheObject, List.of("filedNameOfTheObject"), options.getAllArray());
} }
} }

View File

@ -1,7 +1,9 @@
package org.kar.archidata.dataAccess.addOnSQL; package org.atriasoft.archidata.dataAccess.addOnSQL;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
@ -9,24 +11,26 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import org.kar.archidata.annotation.AnnotationTools; import org.atriasoft.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.AnnotationTools.FieldName; import org.atriasoft.archidata.annotation.AnnotationTools.FieldName;
import org.kar.archidata.dataAccess.CountInOut; import org.atriasoft.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DBAccess; import org.atriasoft.archidata.dataAccess.DBAccess;
import org.kar.archidata.dataAccess.DBAccessMorphia; import org.atriasoft.archidata.dataAccess.DBAccessMorphia;
import org.kar.archidata.dataAccess.DBAccessSQL; import org.atriasoft.archidata.dataAccess.DBAccessSQL;
import org.kar.archidata.dataAccess.DataFactory; import org.atriasoft.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.LazyGetter; import org.atriasoft.archidata.dataAccess.LazyGetter;
import org.kar.archidata.dataAccess.QueryAnd; import org.atriasoft.archidata.dataAccess.QueryAnd;
import org.kar.archidata.dataAccess.QueryCondition; import org.atriasoft.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.QueryInList; import org.atriasoft.archidata.dataAccess.QueryInList;
import org.kar.archidata.dataAccess.QueryOptions; import org.atriasoft.archidata.dataAccess.QueryOptions;
import org.kar.archidata.dataAccess.addOnSQL.model.LinkTableGeneric; import org.atriasoft.archidata.dataAccess.addOnSQL.model.LinkTableGeneric;
import org.kar.archidata.dataAccess.options.Condition; import org.atriasoft.archidata.dataAccess.options.Condition;
import org.kar.archidata.dataAccess.options.OptionSpecifyType; import org.atriasoft.archidata.dataAccess.options.OptionSpecifyType;
import org.kar.archidata.dataAccess.options.OverrideTableName; import org.atriasoft.archidata.dataAccess.options.OverrideTableName;
import org.kar.archidata.exception.DataAccessException; import org.atriasoft.archidata.exception.DataAccessException;
import org.kar.archidata.tools.ConfigBaseVariable; import org.atriasoft.archidata.exception.SystemException;
import org.atriasoft.archidata.tools.ConfigBaseVariable;
import org.bson.types.ObjectId;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -72,7 +76,7 @@ public class AddOnManyToMany implements DataAccessAddOn {
} }
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()) final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0]; .getActualTypeArguments()[0];
if (objectClass == Long.class || objectClass == UUID.class) { if (objectClass == Long.class || objectClass == UUID.class || objectClass == ObjectId.class) {
return true; return true;
} }
final ManyToMany decorators = field.getDeclaredAnnotation(ManyToMany.class); final ManyToMany decorators = field.getDeclaredAnnotation(ManyToMany.class);
@ -85,20 +89,102 @@ public class AddOnManyToMany implements DataAccessAddOn {
return false; return false;
} }
public static String generateLinkTableNameField( public static String hashTo64Chars(final String input) {
try {
final MessageDigest digest = MessageDigest.getInstance("SHA-256");
final byte[] hash = digest.digest(input.getBytes());
final StringBuilder hexString = new StringBuilder();
for (final byte b : hash) {
final String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString().substring(0, 64);
} catch (final NoSuchAlgorithmException e) {
throw new RuntimeException("Erreur lors du hachage de la chaîne", e);
}
}
public static String hashIfNeeded(final String input) {
if (input.length() > 64) {
// Keep only the 50 first chars
final String truncated = input.substring(0, Math.min(input.length(), 50));
final String fullHash = hashTo64Chars(input);
final String hashPart = fullHash.substring(0, 14);
return truncated + hashPart;
}
return input;
}
public record LinkTableWithMode(
String tableName,
boolean first,
boolean equals) {}
public static LinkTableWithMode generateLinkTableNameField(
final String tableName, final String tableName,
final Field field, final Field field,
final QueryOptions options) throws Exception { final QueryOptions options) throws Exception {
final FieldName name = AnnotationTools.getFieldName(field, options); return generateLinkTableName(tableName, field);
return generateLinkTableName(tableName, name.inTable());
} }
public static String generateLinkTableName(final String tableName, final String name) { public static LinkTableWithMode generateLinkTableName(
String localName = name; final String tableAName,
if (name.endsWith("s")) { final String tableAFieldName,
localName = name.substring(0, name.length() - 1); final String tableBName,
final String tableBFieldName) {
final String concatElementA = tableAName + "_" + tableAFieldName;
final String concatElementB = tableBName + "_" + tableBFieldName;
final int compareResult = concatElementA.compareTo(concatElementB);
if (compareResult == 0) {
return new LinkTableWithMode(hashIfNeeded(concatElementA + "_autolink"), true, true);
} }
return tableName + "_link_" + localName; if (compareResult < 0) {
return new LinkTableWithMode(hashIfNeeded(concatElementA + "_link_" + concatElementB), true, false);
}
return new LinkTableWithMode(hashIfNeeded(concatElementB + "_link_" + concatElementA), false, false);
}
public static LinkTableWithMode generateLinkTableName(
final String tableAName,
final String tableAFieldName,
final ManyToMany manyToMany) throws SystemException {
if (manyToMany == null) {
throw new SystemException("@ManyMany is a null pointer " + tableAName);
}
if (manyToMany.targetEntity() == null) {
throw new SystemException("@ManyMany target entity is a null pointer: " + tableAName);
}
if (manyToMany.mappedBy() == null || manyToMany.mappedBy().isEmpty()) {
throw new SystemException("@ManyMany mapped by is not defined: " + tableAName);
}
final String tableNameRemote = AnnotationTools.getTableName(manyToMany.targetEntity());
return generateLinkTableName(tableAName, tableAFieldName, tableNameRemote, manyToMany.mappedBy());
}
public static LinkTableWithMode generateLinkTableName(final String tableAName, final Field field)
throws SystemException {
if (field == null) {
// TODO: throw !!!!
}
final FieldName columnName = AnnotationTools.getFieldName(field, null);
final ManyToMany manyToMany = AnnotationTools.get(field, ManyToMany.class);
return generateLinkTableName(tableAName, columnName.inTable(), manyToMany);
}
public static LinkTableWithMode generateLinkTableName(final Class<?> clazz, final String fieldName)
throws SystemException {
if (clazz == null) {
throw new SystemException("@ManyMany class reference is a null pointer ");
}
if (fieldName == null || fieldName.isEmpty() || fieldName.isBlank()) {
throw new SystemException("@ManyMany field of class reference is not defined");
}
final String tableName = AnnotationTools.getTableName(clazz);
final Field requestedField = AnnotationTools.getFieldNamed(clazz, fieldName);
return generateLinkTableName(tableName, requestedField);
} }
public void generateConcatQuery( public void generateConcatQuery(
@ -111,18 +197,13 @@ public class AddOnManyToMany implements DataAccessAddOn {
@NotNull final CountInOut count, @NotNull final CountInOut count,
final QueryOptions options) throws Exception { final QueryOptions options) throws Exception {
final ManyToMany manyToMany = AnnotationTools.getManyToMany(field); final ManyToMany manyToMany = AnnotationTools.getManyToMany(field);
String linkTableName = generateLinkTableName(tableName, name); final LinkTableWithMode linkTable = generateLinkTableName(tableName, name, manyToMany);
if (manyToMany.mappedBy() != null && manyToMany.mappedBy().length() != 0) {
// TODO: get the remote table name .....
final String remoteTableName = AnnotationTools.getTableName(manyToMany.targetEntity());
linkTableName = generateLinkTableName(remoteTableName, manyToMany.mappedBy());
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()) final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0]; .getActualTypeArguments()[0];
final String tmpVariable = "tmp_" + Integer.toString(count.value); final String tmpVariable = "tmp_" + Integer.toString(count.value);
querySelect.append(" (SELECT GROUP_CONCAT("); querySelect.append(" (SELECT GROUP_CONCAT(");
querySelect.append(tmpVariable); querySelect.append(tmpVariable);
if (manyToMany.mappedBy() == null || manyToMany.mappedBy().length() == 0) { if (linkTable.first()) {
querySelect.append(".object2Id "); querySelect.append(".object2Id ");
} else { } else {
querySelect.append(".object1Id "); querySelect.append(".object1Id ");
@ -135,14 +216,18 @@ public class AddOnManyToMany implements DataAccessAddOn {
querySelect.append("'"); querySelect.append("'");
if (objectClass == Long.class) { if (objectClass == Long.class) {
querySelect.append(SEPARATOR_LONG); querySelect.append(SEPARATOR_LONG);
} else if (objectClass == UUID.class) {} else { } else if (objectClass == UUID.class) {
// ???
} else if (objectClass == ObjectId.class) {
// ???
} else {
final Class<?> foreignKeyType = AnnotationTools.getPrimaryKeyField(objectClass).getType(); final Class<?> foreignKeyType = AnnotationTools.getPrimaryKeyField(objectClass).getType();
if (foreignKeyType == Long.class) { if (foreignKeyType == Long.class) {
querySelect.append(SEPARATOR_LONG); querySelect.append(SEPARATOR_LONG);
} }
} }
querySelect.append("') FROM "); querySelect.append("') FROM ");
querySelect.append(linkTableName); querySelect.append(linkTable.tableName());
querySelect.append(" "); querySelect.append(" ");
querySelect.append(tmpVariable); querySelect.append(tmpVariable);
querySelect.append(" WHERE "); querySelect.append(" WHERE ");
@ -155,7 +240,7 @@ public class AddOnManyToMany implements DataAccessAddOn {
querySelect.append(" = "); querySelect.append(" = ");
querySelect.append(tmpVariable); querySelect.append(tmpVariable);
querySelect.append("."); querySelect.append(".");
if (manyToMany.mappedBy() == null || manyToMany.mappedBy().length() == 0) { if (linkTable.first()) {
querySelect.append("object1Id "); querySelect.append("object1Id ");
} else { } else {
querySelect.append("object2Id "); querySelect.append("object2Id ");
@ -163,7 +248,7 @@ public class AddOnManyToMany implements DataAccessAddOn {
if (!"sqlite".equals(ConfigBaseVariable.getDBType())) { if (!"sqlite".equals(ConfigBaseVariable.getDBType())) {
querySelect.append(" GROUP BY "); querySelect.append(" GROUP BY ");
querySelect.append(tmpVariable); querySelect.append(tmpVariable);
if (manyToMany.mappedBy() == null || manyToMany.mappedBy().length() == 0) { if (linkTable.first()) {
querySelect.append(".object1Id"); querySelect.append(".object1Id");
} else { } else {
querySelect.append(".object2Id"); querySelect.append(".object2Id");
@ -193,7 +278,7 @@ public class AddOnManyToMany implements DataAccessAddOn {
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()) final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0]; .getActualTypeArguments()[0];
// TODO: manage better the eager and lazy !! // TODO: manage better the eager and lazy !!
if (objectClass == Long.class || objectClass == UUID.class) { if (objectClass == Long.class || objectClass == UUID.class || objectClass == ObjectId.class) {
generateConcatQuery(tableName, primaryKey, field, querySelect, query, name, count, options); generateConcatQuery(tableName, primaryKey, field, querySelect, query, name, count, options);
} }
final ManyToMany decorators = field.getDeclaredAnnotation(ManyToMany.class); final ManyToMany decorators = field.getDeclaredAnnotation(ManyToMany.class);
@ -219,8 +304,7 @@ public class AddOnManyToMany implements DataAccessAddOn {
final QueryOptions options, final QueryOptions options,
final List<LazyGetter> lazyCall) throws Exception { final List<LazyGetter> lazyCall) throws Exception {
if (field.getType() != List.class) { if (field.getType() != List.class) {
LOGGER.error("Can not ManyToMany with other than List Model: {}", field.getType().getCanonicalName()); throw new SystemException("@ManyToMany must contain a List");
return;
} }
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()) final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0]; .getActualTypeArguments()[0];
@ -229,12 +313,19 @@ public class AddOnManyToMany implements DataAccessAddOn {
field.set(data, idList); field.set(data, idList);
count.inc(); count.inc();
return; return;
} else if (objectClass == UUID.class) { }
if (objectClass == UUID.class) {
final List<UUID> idList = ioDb.getListOfRawUUIDs(rs, count.value); final List<UUID> idList = ioDb.getListOfRawUUIDs(rs, count.value);
field.set(data, idList); field.set(data, idList);
count.inc(); count.inc();
return; return;
} }
if (objectClass == ObjectId.class) {
final List<ObjectId> idList = ioDb.getListOfRawOIDs(rs, count.value);
field.set(data, idList);
count.inc();
return;
}
final ManyToMany decorators = field.getDeclaredAnnotation(ManyToMany.class); final ManyToMany decorators = field.getDeclaredAnnotation(ManyToMany.class);
if (decorators == null) { if (decorators == null) {
return; return;
@ -285,6 +376,27 @@ public class AddOnManyToMany implements DataAccessAddOn {
}; };
lazyCall.add(lambda); lazyCall.add(lambda);
} }
} else if (foreignKeyType == ObjectId.class) {
final List<ObjectId> idList = ioDb.getListOfRawOIDs(rs, count.value);
// field.set(data, idList);
count.inc();
if (idList != null && idList.size() > 0) {
final FieldName idField = AnnotationTools.getFieldName(AnnotationTools.getIdField(objectClass),
options);
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
final LazyGetter lambda = () -> {
final List<ObjectId> childs = new ArrayList<>(idList);
// TODO: update to have get with abstract types ....
@SuppressWarnings("unchecked")
final Object foreignData = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryInList<>(idField.inTable(), childs)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
}
} }
} }
} }
@ -297,6 +409,7 @@ public class AddOnManyToMany implements DataAccessAddOn {
@Override @Override
public void asyncUpdate( public void asyncUpdate(
final DBAccessSQL ioDb, final DBAccessSQL ioDb,
final Object previousData,
final String tableName, final String tableName,
final Object localKey, final Object localKey,
final Field field, final Field field,
@ -309,18 +422,19 @@ public class AddOnManyToMany implements DataAccessAddOn {
} }
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()) final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0]; .getActualTypeArguments()[0];
if (objectClass != Long.class && objectClass != UUID.class) { if (objectClass != Long.class && objectClass != UUID.class && objectClass != ObjectId.class) {
throw new DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<" throw new DataAccessException(
+ objectClass.getCanonicalName() + ">"); "Can not ManyToMany with other than List<Long> or List<UUID> or List<ObjectId> Model: List<"
+ objectClass.getCanonicalName() + ">");
} }
final FieldName columnName = AnnotationTools.getFieldName(field, options); final LinkTableWithMode linkTable = generateLinkTableName(tableName, field);
final String linkTableName = generateLinkTableName(tableName, columnName.inTable()); final String obj1 = linkTable.first ? "object1Id" : "object2Id";
final String obj2 = linkTable.first ? "object2Id" : "object1Id";
actions.add(() -> { actions.add(() -> {
ioDb.deleteWhere(LinkTableGeneric.class, new OverrideTableName(linkTableName), ioDb.deleteWhere(LinkTableGeneric.class, new OverrideTableName(linkTable.tableName()),
new Condition(new QueryCondition("object1Id", "=", localKey)), new Condition(new QueryCondition(obj1, "=", localKey)),
new OptionSpecifyType("object1Id", localKey.getClass()), new OptionSpecifyType(obj1, localKey.getClass()), new OptionSpecifyType(obj2, objectClass));
new OptionSpecifyType("object2Id", objectClass));
}); });
asyncInsert(ioDb, tableName, localKey, field, data, actions, options); asyncInsert(ioDb, tableName, localKey, field, data, actions, options);
} }
@ -348,49 +462,52 @@ public class AddOnManyToMany implements DataAccessAddOn {
} }
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()) final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0]; .getActualTypeArguments()[0];
if (objectClass != Long.class && objectClass != UUID.class) { if (objectClass != Long.class && objectClass != UUID.class && objectClass != ObjectId.class) {
throw new DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<" throw new DataAccessException(
+ objectClass.getCanonicalName() + ">"); "Can not ManyToMany with other than List<Long> or List<UUID> or List<ObjectId> Model: List<"
+ objectClass.getCanonicalName() + ">");
} }
final FieldName columnName = AnnotationTools.getFieldName(field, options); final LinkTableWithMode linkTable = generateLinkTableName(tableName, field);
final String linkTableName = generateLinkTableName(tableName, columnName.inTable());
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final List<Object> dataCasted = (List<Object>) data; final List<Object> dataCasted = (List<Object>) data;
if (dataCasted.size() == 0) { if (dataCasted.size() == 0) {
return; return;
} }
final String obj1 = linkTable.first ? "object1Id" : "object2Id";
final String obj2 = linkTable.first ? "object2Id" : "object1Id";
final List<LinkTableGeneric> insertElements = new ArrayList<>(); final List<LinkTableGeneric> insertElements = new ArrayList<>();
for (final Object remoteKey : dataCasted) { for (final Object remoteKey : dataCasted) {
if (remoteKey == null) { if (remoteKey == null) {
throw new DataAccessException("Try to insert remote key with null value"); throw new DataAccessException("Try to insert remote key with null value");
} }
insertElements.add(new LinkTableGeneric(localKey, remoteKey)); if (linkTable.first) {
insertElements.add(new LinkTableGeneric(localKey, remoteKey));
} else {
insertElements.add(new LinkTableGeneric(remoteKey, localKey));
}
} }
if (insertElements.size() == 0) { if (insertElements.size() == 0) {
LOGGER.warn("Insert multiple link without any value (may have null in the list): {}", dataCasted); LOGGER.warn("Insert multiple link without any value (may have null in the list): {}", dataCasted);
return; return;
} }
actions.add(() -> { actions.add(() -> {
ioDb.insertMultiple(insertElements, new OverrideTableName(linkTableName), ioDb.insertMultiple(insertElements, new OverrideTableName(linkTable.tableName()),
new OptionSpecifyType("object1Id", localKey.getClass()), new OptionSpecifyType(obj1, localKey.getClass()), new OptionSpecifyType(obj2, objectClass));
new OptionSpecifyType("object2Id", objectClass));
}); });
} }
@Override @Override
public void drop(final DBAccessSQL ioDb, final String tableName, final Field field, final QueryOptions options) public void drop(final DBAccessSQL ioDb, final String tableName, final Field field, final QueryOptions options)
throws Exception { throws Exception {
final FieldName columnName = AnnotationTools.getFieldName(field, options); final LinkTableWithMode linkTable = generateLinkTableName(tableName, field);
final String linkTableName = generateLinkTableName(tableName, columnName.inTable()); ioDb.drop(LinkTableGeneric.class, new OverrideTableName(linkTable.tableName()));
ioDb.drop(LinkTableGeneric.class, new OverrideTableName(linkTableName));
} }
@Override @Override
public void cleanAll(final DBAccessSQL ioDb, final String tableName, final Field field, final QueryOptions options) public void cleanAll(final DBAccessSQL ioDb, final String tableName, final Field field, final QueryOptions options)
throws Exception { throws Exception {
final FieldName columnName = AnnotationTools.getFieldName(field, options); final LinkTableWithMode linkTable = generateLinkTableName(tableName, field);
final String linkTableName = generateLinkTableName(tableName, columnName.inTable()); ioDb.cleanAll(LinkTableGeneric.class, new OverrideTableName(linkTable.tableName()));
ioDb.cleanAll(LinkTableGeneric.class, new OverrideTableName(linkTableName));
} }
public static void addLink( public static void addLink(
@ -400,18 +517,19 @@ public class AddOnManyToMany implements DataAccessAddOn {
final String column, final String column,
final Object remoteKey) throws Exception { final Object remoteKey) throws Exception {
if (ioDb instanceof final DBAccessSQL daSQL) { if (ioDb instanceof final DBAccessSQL daSQL) {
final String tableName = AnnotationTools.getTableName(clazz); final LinkTableWithMode linkTable = generateLinkTableName(clazz, column);
final String linkTableName = generateLinkTableName(tableName, column); final LinkTableGeneric insertElement = linkTable.first ? new LinkTableGeneric(localKey, remoteKey)
final LinkTableGeneric insertElement = new LinkTableGeneric(localKey, remoteKey); : new LinkTableGeneric(remoteKey, localKey);
daSQL.insert(insertElement, new OverrideTableName(linkTableName), final String obj1 = linkTable.first ? "object1Id" : "object2Id";
new OptionSpecifyType("object1Id", localKey.getClass()), final String obj2 = linkTable.first ? "object2Id" : "object1Id";
new OptionSpecifyType("object2Id", remoteKey.getClass())); daSQL.insert(insertElement, new OverrideTableName(linkTable.tableName()),
new OptionSpecifyType(obj1, localKey.getClass()),
new OptionSpecifyType(obj2, remoteKey.getClass()));
} else if (ioDb instanceof final DBAccessMorphia dam) { } else if (ioDb instanceof final DBAccessMorphia dam) {
} else { } else {
throw new DataAccessException("DataAccess Not managed"); throw new DataAccessException("DataAccess Not managed");
} }
} }
public static long removeLink( public static long removeLink(
@ -421,13 +539,14 @@ public class AddOnManyToMany implements DataAccessAddOn {
final String column, final String column,
final Object remoteKey) throws Exception { final Object remoteKey) throws Exception {
if (ioDb instanceof final DBAccessSQL daSQL) { if (ioDb instanceof final DBAccessSQL daSQL) {
final String tableName = AnnotationTools.getTableName(clazz); final LinkTableWithMode linkTable = generateLinkTableName(clazz, column);
final String linkTableName = generateLinkTableName(tableName, column); final String obj1 = linkTable.first ? "object1Id" : "object2Id";
return daSQL.deleteWhere(LinkTableGeneric.class, new OverrideTableName(linkTableName), final String obj2 = linkTable.first ? "object2Id" : "object1Id";
new Condition(new QueryAnd(new QueryCondition("object1Id", "=", localKey), return daSQL.deleteWhere(LinkTableGeneric.class, new OverrideTableName(linkTable.tableName()),
new QueryCondition("object2Id", "=", remoteKey))), new Condition(new QueryAnd(new QueryCondition(obj1, "=", localKey),
new OptionSpecifyType("object1Id", localKey.getClass()), new QueryCondition(obj2, "=", remoteKey))),
new OptionSpecifyType("object2Id", remoteKey.getClass())); new OptionSpecifyType(obj1, localKey.getClass()),
new OptionSpecifyType(obj2, remoteKey.getClass()));
} else if (ioDb instanceof final DBAccessMorphia dam) { } else if (ioDb instanceof final DBAccessMorphia dam) {
return 0L; return 0L;
} else { } else {
@ -435,6 +554,8 @@ public class AddOnManyToMany implements DataAccessAddOn {
} }
} }
private static List<String> tableAlreadyCreated = new ArrayList<>();
@Override @Override
public void createTables( public void createTables(
final String tableName, final String tableName,
@ -448,18 +569,21 @@ public class AddOnManyToMany implements DataAccessAddOn {
final int fieldId, final int fieldId,
final QueryOptions options) throws Exception { final QueryOptions options) throws Exception {
final ManyToMany manyToMany = AnnotationTools.getManyToMany(field); final ManyToMany manyToMany = AnnotationTools.getManyToMany(field);
if (manyToMany.mappedBy() != null && manyToMany.mappedBy().length() != 0) { if (manyToMany.mappedBy() == null || manyToMany.mappedBy().length() == 0) {
// not the reference model to create base: throw new SystemException("MappedBy must be set in ManyMany: " + tableName + " " + field.getName());
return; }
final LinkTableWithMode linkTable = generateLinkTableNameField(tableName, field, options);
if (linkTable.first() || linkTable.equals()) {
final QueryOptions options2 = new QueryOptions(new OverrideTableName(linkTable.tableName()));
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
final Class<?> primaryType = primaryField.getType();
final String obj1 = linkTable.first ? "object1Id" : "object2Id";
final String obj2 = linkTable.first ? "object2Id" : "object1Id";
options2.add(new OptionSpecifyType(obj1, primaryType));
options2.add(new OptionSpecifyType(obj2, objectClass));
final List<String> sqlCommand = DataFactory.createTable(LinkTableGeneric.class, options2);
postActionList.addAll(sqlCommand);
} }
final String linkTableName = generateLinkTableNameField(tableName, field, options);
final QueryOptions options2 = new QueryOptions(new OverrideTableName(linkTableName));
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
final Class<?> primaryType = primaryField.getType();
options2.add(new OptionSpecifyType("object1Id", primaryType));
options2.add(new OptionSpecifyType("object2Id", objectClass));
final List<String> sqlCommand = DataFactory.createTable(LinkTableGeneric.class, options2);
postActionList.addAll(sqlCommand);
} }
} }

View File

@ -0,0 +1,464 @@
package org.atriasoft.archidata.dataAccess.addOnSQL;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import org.atriasoft.archidata.annotation.AnnotationTools;
import org.atriasoft.archidata.annotation.AnnotationTools.FieldName;
import org.atriasoft.archidata.annotation.ManyToManyLocal;
import org.atriasoft.archidata.dataAccess.CountInOut;
import org.atriasoft.archidata.dataAccess.DBAccess;
import org.atriasoft.archidata.dataAccess.DBAccessSQL;
import org.atriasoft.archidata.dataAccess.DataFactory;
import org.atriasoft.archidata.dataAccess.LazyGetter;
import org.atriasoft.archidata.dataAccess.QueryInList;
import org.atriasoft.archidata.dataAccess.QueryOptions;
import org.atriasoft.archidata.dataAccess.addOnSQL.model.TableCoversGeneric;
import org.atriasoft.archidata.dataAccess.options.Condition;
import org.atriasoft.archidata.dataAccess.options.OptionRenameColumn;
import org.atriasoft.archidata.dataAccess.options.OptionSpecifyType;
import org.atriasoft.archidata.dataAccess.options.OverrideTableName;
import org.atriasoft.archidata.exception.SystemException;
import org.atriasoft.archidata.tools.ContextGenericTools;
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.validation.constraints.NotNull;
public class AddOnManyToManyLocal implements DataAccessAddOn {
static final Logger LOGGER = LoggerFactory.getLogger(AddOnManyToManyLocal.class);
static final String SEPARATOR_LONG = "-";
static final String SEPARATOR_UUID = "_";
@Override
public Class<?> getAnnotationClass() {
return ManyToManyLocal.class;
}
@Override
public boolean isCompatibleField(final Field field) {
if (field.getType() != List.class) {
return false;
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass == Long.class || objectClass == UUID.class || objectClass == ObjectId.class) {
return true;
}
final ManyToManyLocal decorators = field.getDeclaredAnnotation(ManyToManyLocal.class);
if (decorators == null) {
return false;
}
if (decorators.targetEntity() == objectClass) {
return true;
}
return false;
}
@Override
public void insertData(
final DBAccessSQL ioDb,
final PreparedStatement ps,
final Field field,
final Object rootObject,
final CountInOut iii)
throws SQLException, IllegalArgumentException, IllegalAccessException, JsonProcessingException {
final Object data = field.get(rootObject);
if (data == null) {
ps.setNull(iii.value, Types.VARCHAR);
}
final ObjectMapper objectMapper = ContextGenericTools.createObjectMapper();
final String dataString = objectMapper.writeValueAsString(data);
ps.setString(iii.value, dataString);
iii.inc();
}
@Override
public boolean isUpdateAsync(final Field field) {
return true;
}
@Override
public void asyncUpdate(
final DBAccessSQL ioDb,
final Object previousData,
final String tableName,
final Object primaryKeyValue,
final Field field,
final Object insertedData,
final List<LazyGetter> actions,
final QueryOptions options) throws Exception {
final Object previousDataValue = field.get(previousData);
Collection<?> previousDataCollection = new ArrayList<>();
if (previousDataValue instanceof final Collection<?> tmpCollection) {
previousDataCollection = tmpCollection;
}
final Object insertedDataValue = insertedData;
Collection<?> insertedDataCollection = new ArrayList<>();
if (insertedDataValue instanceof final Collection<?> tmpCollection) {
insertedDataCollection = tmpCollection;
}
// add new Values
for (final Object value : insertedDataCollection) {
if (previousDataCollection.contains(value)) {
continue;
}
actions.add(() -> {
addLinkRemote(ioDb, field, primaryKeyValue, value);
});
}
// remove old values:
for (final Object value : previousDataCollection) {
if (insertedDataCollection.contains(value)) {
continue;
}
actions.add(() -> {
removeLinkRemote(ioDb, field, primaryKeyValue, value);
});
}
}
/** Some action must be done asynchronously for update or remove element
* @param field
* @return */
@Override
public boolean isInsertAsync(final Field field) throws Exception {
return true;
}
/** When insert is mark async, this function permit to create or update the data
* @param tableName Name of the Table.
* @param localId Local ID of the current table
* @param field Field that is updated.
* @param data Data that might be inserted.
* @param actions Asynchronous action to do after main request. */
@Override
public void asyncInsert(
final DBAccessSQL ioDb,
final String tableName,
final Object primaryKeyValue,
final Field field,
final Object data,
final List<LazyGetter> actions,
final QueryOptions options) throws Exception {
final Object insertedData = data;
if (insertedData == null) {
return;
}
if (insertedData instanceof final Collection<?> insertedDataCollection) {
for (final Object value : insertedDataCollection) {
actions.add(() -> {
addLinkRemote(ioDb, field, primaryKeyValue, value);
});
}
}
}
@Override
public boolean isPreviousDataNeeded(final Field field) {
return true;
}
@Override
public boolean canInsert(final Field field) {
return isCompatibleField(field);
}
@Override
public boolean canRetrieve(final Field field) {
return isCompatibleField(field);
}
@Override
public void generateQuery(
@NotNull final String tableName,
@NotNull final String primaryKey,
@NotNull final Field field,
@NotNull final StringBuilder querySelect,
@NotNull final StringBuilder query,
@NotNull final String name,
@NotNull final CountInOut count,
final QueryOptions options) throws Exception {
querySelect.append(" ");
querySelect.append(tableName);
querySelect.append(".");
querySelect.append(name);
count.inc();
}
@Override
public void fillFromQuery(
final DBAccessSQL ioDb,
final ResultSet rs,
final Field field,
final Object data,
final CountInOut count,
final QueryOptions options,
final List<LazyGetter> lazyCall) throws Exception {
if (field.getType() != List.class) {
throw new SystemException("@ManyToManyLocal must contain a List");
}
final String jsonData = rs.getString(count.value);
count.inc();
if (rs.wasNull()) {
return;
}
final ObjectMapper objectMapper = ContextGenericTools.createObjectMapper();
final ParameterizedType listType = (ParameterizedType) field.getGenericType();
final Class<?> objectClass = (Class<?>) listType.getActualTypeArguments()[0];
if (objectClass == Long.class) {
final List<Long> dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<Long>>() {});
field.set(data, dataParsed);
return;
}
if (objectClass == String.class) {
final List<String> dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<String>>() {});
field.set(data, dataParsed);
return;
}
if (objectClass == UUID.class)
{
final List<UUID> dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<UUID>>() {});
field.set(data, dataParsed);
return;
}
if (objectClass == ObjectId.class) {
final List<ObjectId> dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<ObjectId>>() {});
field.set(data, dataParsed);
return;
}
final ManyToManyLocal decorators = field.getDeclaredAnnotation(ManyToManyLocal.class);
if (decorators == null) {
return;
}
if (objectClass == decorators.targetEntity()) {
final Class<?> foreignKeyType = AnnotationTools.getPrimaryKeyField(objectClass).getType();
if (foreignKeyType == Long.class) {
final List<Long> idList = objectMapper.readValue(jsonData, new TypeReference<List<Long>>() {});
if (idList != null && idList.size() > 0) {
final FieldName idField = AnnotationTools.getFieldName(AnnotationTools.getIdField(objectClass),
options);
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
final LazyGetter lambda = () -> {
// TODO: update to have get with abstract types ....
final Object foreignData = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryInList<>(idField.inTable(), idList)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
}
} else if (foreignKeyType == UUID.class) {
final List<UUID> idList = objectMapper.readValue(jsonData, new TypeReference<List<UUID>>() {});
if (idList != null && idList.size() > 0) {
final FieldName idField = AnnotationTools.getFieldName(AnnotationTools.getIdField(objectClass),
options);
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
final LazyGetter lambda = () -> {
final List<UUID> childs = new ArrayList<>(idList);
// TODO: update to have get with abstract types ....
final Object foreignData = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryInList<>(idField.inTable(), childs)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
}
} else if (foreignKeyType == ObjectId.class) {
final List<ObjectId> idList = objectMapper.readValue(jsonData, new TypeReference<List<ObjectId>>() {});
if (idList != null && idList.size() > 0) {
final FieldName idField = AnnotationTools.getFieldName(AnnotationTools.getIdField(objectClass),
options);
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
final LazyGetter lambda = () -> {
final List<ObjectId> childs = new ArrayList<>(idList);
// TODO: update to have get with abstract types ....
final Object foreignData = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryInList<>(idField.inTable(), childs)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
}
}
}
}
@Override
public void createTables(
final String tableName,
final Field primaryField,
final Field field,
final StringBuilder mainTableBuilder,
final List<String> preActionList,
final List<String> postActionList,
final boolean createIfNotExist,
final boolean createDrop,
final int fieldId,
final QueryOptions options) throws Exception {
// store data as json to response like a no-sql
DataFactory.createTablesSpecificType(tableName, primaryField, field, mainTableBuilder, preActionList,
postActionList, createIfNotExist, createDrop, fieldId, JsonValue.class, options);
}
private static void addLinkLocal(
final DBAccess ioDb,
final Class<?> clazz,
final String clazzPrimaryKeyName,
final Object clazzPrimaryKeyValue,
final String fieldNameToUpdate,
final Object valueToAdd) throws Exception {
final String tableName = AnnotationTools.getTableName(clazz);
final QueryOptions options = new QueryOptions(new OverrideTableName(tableName),
new OptionSpecifyType("idOfTheObject", clazzPrimaryKeyValue.getClass()),
new OptionSpecifyType("filedNameOfTheObject", valueToAdd.getClass(), true));
options.add(new OptionRenameColumn("idOfTheObject", clazzPrimaryKeyName));
options.add(new OptionRenameColumn("filedNameOfTheObject", fieldNameToUpdate));
final TableCoversGeneric data = ioDb.get(TableCoversGeneric.class, clazzPrimaryKeyValue, options.getAllArray());
if (data.filedNameOfTheObject == null) {
data.filedNameOfTheObject = new ArrayList<>();
}
for (final Object elem : data.filedNameOfTheObject) {
if (elem.equals(valueToAdd)) {
return;
}
}
data.filedNameOfTheObject.add(valueToAdd);
ioDb.update(data, data.idOfTheObject, List.of("filedNameOfTheObject"), options.getAllArray());
}
public static void addLink(
final DBAccess ioDb,
final Class<?> clazz,
final Object clazzPrimaryKeyValue,
final String fieldNameToUpdate,
final Object valueToAdd) throws Exception {
final Field localField = AnnotationTools.getFieldNamed(clazz, fieldNameToUpdate);
{
//get local field to find the remote field name:
final Field primaryKeyField = AnnotationTools.getPrimaryKeyField(clazz);
final FieldName primaryKeyColomnName = AnnotationTools.getFieldName(primaryKeyField, null);
final FieldName localFieldName = AnnotationTools.getFieldName(localField, null);
addLinkLocal(ioDb, clazz, primaryKeyColomnName.inTable(), clazzPrimaryKeyValue, localFieldName.inTable(),
valueToAdd);
}
addLinkRemote(ioDb, localField, clazzPrimaryKeyValue, valueToAdd);
}
private static void addLinkRemote(
final DBAccess ioDb,
final Field localField,
final Object localPrimaryKeyValue,
final Object remotePrimaryKeyValue) throws Exception {
final ManyToManyLocal manyLocal = AnnotationTools.get(localField, ManyToManyLocal.class);
// Update the remote elements:
if (manyLocal == null || manyLocal.targetEntity() == null || manyLocal.remoteField() == null
|| manyLocal.remoteField().isEmpty()) {
return;
}
{
//get local field to find the remote field name:
final Field primaryKeyField = AnnotationTools.getPrimaryKeyField(manyLocal.targetEntity());
final FieldName primaryKeyColomnName = AnnotationTools.getFieldName(primaryKeyField, null);
final Field remoteField = AnnotationTools.getFieldNamed(manyLocal.targetEntity(), manyLocal.remoteField());
final FieldName localFieldName = AnnotationTools.getFieldName(remoteField, null);
addLinkLocal(ioDb, manyLocal.targetEntity(), primaryKeyColomnName.inTable(), remotePrimaryKeyValue,
localFieldName.inTable(), localPrimaryKeyValue);
}
}
private static void removeLinkLocal(
final DBAccess ioDb,
final Class<?> clazz,
final String clazzPrimaryKeyName,
final Object clazzPrimaryKeyValue,
final String fieldNameToUpdate,
final Object valueToRemove) throws Exception {
final String tableName = AnnotationTools.getTableName(clazz);
final QueryOptions options = new QueryOptions(new OverrideTableName(tableName),
new OptionSpecifyType("idOfTheObject", clazzPrimaryKeyValue.getClass()),
new OptionSpecifyType("filedNameOfTheObject", valueToRemove.getClass(), true));
options.add(new OptionRenameColumn("idOfTheObject", clazzPrimaryKeyName));
options.add(new OptionRenameColumn("filedNameOfTheObject", fieldNameToUpdate));
final TableCoversGeneric data = ioDb.get(TableCoversGeneric.class, clazzPrimaryKeyValue, options.getAllArray());
if (data.filedNameOfTheObject == null) {
return;
}
final List<Object> newList = new ArrayList<>();
for (final Object elem : data.filedNameOfTheObject) {
if (elem.equals(valueToRemove)) {
continue;
}
newList.add(elem);
}
data.filedNameOfTheObject = newList;
if (data.filedNameOfTheObject.isEmpty()) {
data.filedNameOfTheObject = null;
}
ioDb.update(data, data.idOfTheObject, List.of("filedNameOfTheObject"), options.getAllArray());
}
public static void removeLink(
final DBAccess ioDb,
final Class<?> clazz,
final Object clazzPrimaryKeyValue,
final String fieldNameToUpdate,
final Object valueToRemove) throws Exception {
final Field localField = AnnotationTools.getFieldNamed(clazz, fieldNameToUpdate);
{
//get local field to find the remote field name:
final Field primaryKeyField = AnnotationTools.getPrimaryKeyField(clazz);
final FieldName primaryKeyColomnName = AnnotationTools.getFieldName(primaryKeyField, null);
final FieldName localFieldName = AnnotationTools.getFieldName(localField, null);
removeLinkLocal(ioDb, clazz, primaryKeyColomnName.inTable(), clazzPrimaryKeyValue, localFieldName.inTable(),
valueToRemove);
}
removeLinkRemote(ioDb, localField, clazzPrimaryKeyValue, valueToRemove);
}
private static void removeLinkRemote(
final DBAccess ioDb,
final Field localField,
final Object localPrimaryKeyValue,
final Object remotePrimaryKeyValue) throws Exception {
final ManyToManyLocal manyLocal = AnnotationTools.get(localField, ManyToManyLocal.class);
// Update the remote elements:
if (manyLocal == null || manyLocal.targetEntity() == null || manyLocal.remoteField() == null
|| manyLocal.remoteField().isEmpty()) {
return;
}
{
//get local field to find the remote field name:
final Field primaryKeyField = AnnotationTools.getPrimaryKeyField(manyLocal.targetEntity());
final FieldName primaryKeyColomnName = AnnotationTools.getFieldName(primaryKeyField, null);
final Field remoteField = AnnotationTools.getFieldNamed(manyLocal.targetEntity(), manyLocal.remoteField());
final FieldName localFieldName = AnnotationTools.getFieldName(remoteField, null);
removeLinkLocal(ioDb, manyLocal.targetEntity(), primaryKeyColomnName.inTable(), remotePrimaryKeyValue,
localFieldName.inTable(), localPrimaryKeyValue);
}
}
}

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess.addOnSQL; package org.atriasoft.archidata.dataAccess.addOnSQL;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
@ -7,15 +7,16 @@ import java.sql.Types;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import org.kar.archidata.annotation.AnnotationTools; import org.atriasoft.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.AnnotationTools.FieldName; import org.atriasoft.archidata.annotation.AnnotationTools.FieldName;
import org.kar.archidata.dataAccess.CountInOut; import org.atriasoft.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DBAccessSQL; import org.atriasoft.archidata.dataAccess.DBAccessSQL;
import org.kar.archidata.dataAccess.DataFactory; import org.atriasoft.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.LazyGetter; import org.atriasoft.archidata.dataAccess.LazyGetter;
import org.kar.archidata.dataAccess.QueryOptions; import org.atriasoft.archidata.dataAccess.QueryOptions;
import org.kar.archidata.exception.DataAccessException; import org.atriasoft.archidata.exception.DataAccessException;
import org.kar.archidata.tools.UuidUtils; import org.atriasoft.archidata.tools.UuidUtils;
import org.bson.types.ObjectId;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -66,6 +67,8 @@ public class AddOnManyToOne implements DataAccessAddOn {
ps.setNull(iii.value, Types.VARCHAR); ps.setNull(iii.value, Types.VARCHAR);
} else if (field.getType() == UUID.class) { } else if (field.getType() == UUID.class) {
ps.setNull(iii.value, Types.BINARY); ps.setNull(iii.value, Types.BINARY);
} else if (field.getType() == ObjectId.class) {
ps.setNull(iii.value, Types.BINARY);
} }
} else if (field.getType() == Long.class) { } else if (field.getType() == Long.class) {
final Long dataTyped = (Long) data; final Long dataTyped = (Long) data;
@ -84,6 +87,11 @@ public class AddOnManyToOne implements DataAccessAddOn {
LOGGER.info("Generate UUTD for DB: {}", dataTyped); LOGGER.info("Generate UUTD for DB: {}", dataTyped);
final byte[] dataByte = UuidUtils.asBytes(dataTyped); final byte[] dataByte = UuidUtils.asBytes(dataTyped);
ps.setBytes(iii.value, dataByte); ps.setBytes(iii.value, dataByte);
} else if (field.getType() == ObjectId.class) {
final ObjectId dataTyped = (ObjectId) data;
LOGGER.info("Generate ObjectId for DB: {}", dataTyped);
final byte[] dataByte = dataTyped.toByteArray();
ps.setBytes(iii.value, dataByte);
} else { } else {
final Field idField = AnnotationTools.getFieldOfId(field.getType()); final Field idField = AnnotationTools.getFieldOfId(field.getType());
final Object uid = idField.get(data); final Object uid = idField.get(data);
@ -101,7 +109,8 @@ public class AddOnManyToOne implements DataAccessAddOn {
@Override @Override
public boolean canInsert(final Field field) { public boolean canInsert(final Field field) {
if (field.getType() == Long.class || field.getType() == Integer.class || field.getType() == Short.class if (field.getType() == Long.class || field.getType() == Integer.class || field.getType() == Short.class
|| field.getType() == String.class || field.getType() == UUID.class) { || field.getType() == String.class || field.getType() == UUID.class
|| field.getType() == ObjectId.class) {
return true; return true;
} }
final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class); final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class);
@ -120,7 +129,7 @@ public class AddOnManyToOne implements DataAccessAddOn {
public boolean canRetrieve(final Field field) { public boolean canRetrieve(final Field field) {
final Class<?> classType = field.getType(); final Class<?> classType = field.getType();
if (classType == Long.class || classType == Integer.class || classType == Short.class if (classType == Long.class || classType == Integer.class || classType == Short.class
|| classType == String.class || classType == UUID.class) { || classType == String.class || classType == UUID.class || classType == ObjectId.class) {
return true; return true;
} }
final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class); final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class);
@ -141,7 +150,8 @@ public class AddOnManyToOne implements DataAccessAddOn {
@NotNull final CountInOut count, @NotNull final CountInOut count,
final QueryOptions options) throws Exception { final QueryOptions options) throws Exception {
if (field.getType() == Long.class || field.getType() == Integer.class || field.getType() == Short.class if (field.getType() == Long.class || field.getType() == Integer.class || field.getType() == Short.class
|| field.getType() == String.class || field.getType() == UUID.class) { || field.getType() == String.class || field.getType() == UUID.class
|| field.getType() == ObjectId.class) {
querySelect.append(" "); querySelect.append(" ");
querySelect.append(tableName); querySelect.append(tableName);
querySelect.append("."); querySelect.append(".");
@ -230,6 +240,15 @@ public class AddOnManyToOne implements DataAccessAddOn {
} }
return; return;
} }
if (field.getType() == ObjectId.class) {
final byte[] tmp = rs.getBytes(count.value);
count.inc();
if (!rs.wasNull()) {
final ObjectId foreignKey = new ObjectId(tmp);
field.set(data, foreignKey);
}
return;
}
final Class<?> objectClass = field.getType(); final Class<?> objectClass = field.getType();
final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class); final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class);
if (decorators == null) { if (decorators == null) {
@ -279,6 +298,22 @@ public class AddOnManyToOne implements DataAccessAddOn {
}; };
lazyCall.add(lambda); lazyCall.add(lambda);
} }
} else if (remotePrimaryKeyType == ObjectId.class) {
// here we have the field, the data and the the remote value ==> can create callback that generate the update of the value ...
final ObjectId foreignKey = ioDb.getListOfRawOID(rs, count.value);
count.inc();
if (foreignKey != null) {
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
final LazyGetter lambda = () -> {
// TODO: update to have get with abstract types ....
final Object foreignData = ioDb.get(decorators.targetEntity(), foreignKey);
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
}
} }
} }
} }
@ -298,7 +333,7 @@ public class AddOnManyToOne implements DataAccessAddOn {
final QueryOptions options) throws Exception { final QueryOptions options) throws Exception {
final Class<?> classType = field.getType(); final Class<?> classType = field.getType();
if (classType == Long.class || classType == Integer.class || classType == Short.class if (classType == Long.class || classType == Integer.class || classType == Short.class
|| classType == String.class || classType == UUID.class) { || classType == String.class || classType == UUID.class || classType == ObjectId.class) {
DataFactory.createTablesSpecificType(tableName, primaryField, field, mainTableBuilder, preActionList, DataFactory.createTablesSpecificType(tableName, primaryField, field, mainTableBuilder, preActionList,
postActionList, createIfNotExist, createDrop, fieldId, classType, options); postActionList, createIfNotExist, createDrop, fieldId, classType, options);
} else { } else {

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess.addOnSQL; package org.atriasoft.archidata.dataAccess.addOnSQL;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
@ -10,17 +10,18 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.kar.archidata.annotation.AnnotationTools; import org.atriasoft.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.AnnotationTools.FieldName; import org.atriasoft.archidata.annotation.AnnotationTools.FieldName;
import org.kar.archidata.dataAccess.CountInOut; import org.atriasoft.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DBAccessSQL; import org.atriasoft.archidata.dataAccess.DBAccessSQL;
import org.kar.archidata.dataAccess.DataFactory; import org.atriasoft.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.LazyGetter; import org.atriasoft.archidata.dataAccess.LazyGetter;
import org.kar.archidata.dataAccess.QueryCondition; import org.atriasoft.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.QueryOptions; import org.atriasoft.archidata.dataAccess.QueryOptions;
import org.kar.archidata.dataAccess.options.Condition; import org.atriasoft.archidata.dataAccess.options.Condition;
import org.kar.archidata.exception.DataAccessException; import org.atriasoft.archidata.exception.DataAccessException;
import org.kar.archidata.tools.ConfigBaseVariable; import org.atriasoft.archidata.tools.ConfigBaseVariable;
import org.bson.types.ObjectId;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -110,7 +111,7 @@ public class AddOnOneToMany implements DataAccessAddOn {
} }
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()) final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0]; .getActualTypeArguments()[0];
if (objectClass == Long.class || objectClass == UUID.class) { if (objectClass == Long.class || objectClass == UUID.class || objectClass == ObjectId.class) {
return true; return true;
} }
final OneToMany decorators = field.getDeclaredAnnotation(OneToMany.class); final OneToMany decorators = field.getDeclaredAnnotation(OneToMany.class);
@ -201,7 +202,7 @@ public class AddOnOneToMany implements DataAccessAddOn {
return; return;
} }
// TODO: manage better the eager and lazy !! // TODO: manage better the eager and lazy !!
if (objectClass == Long.class || objectClass == UUID.class) { if (objectClass == Long.class || objectClass == UUID.class || objectClass == ObjectId.class) {
generateConcatQuery(tableName, primaryKey, field, querySelect, query, name, count, options, generateConcatQuery(tableName, primaryKey, field, querySelect, query, name, count, options,
decorators.targetEntity(), decorators.mappedBy()); decorators.targetEntity(), decorators.mappedBy());
return; return;
@ -230,81 +231,115 @@ public class AddOnOneToMany implements DataAccessAddOn {
final CountInOut count, final CountInOut count,
final QueryOptions options, final QueryOptions options,
final List<LazyGetter> lazyCall) throws Exception { final List<LazyGetter> lazyCall) throws Exception {
if (field.getType() != List.class) { try {
LOGGER.error("Can not OneToMany with other than List Model: {}", field.getType().getCanonicalName()); if (field.getType() != List.class) {
return; LOGGER.error("Can not OneToMany with other than List Model: {}", field.getType().getCanonicalName());
} return;
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()) }
.getActualTypeArguments()[0]; final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
final OneToMany decorators = field.getDeclaredAnnotation(OneToMany.class); .getActualTypeArguments()[0];
if (decorators == null) { final OneToMany decorators = field.getDeclaredAnnotation(OneToMany.class);
return; if (decorators == null) {
} return;
if (objectClass == Long.class) {
final List<Long> idList = ioDb.getListOfIds(rs, count.value, SEPARATOR_LONG);
field.set(data, idList);
count.inc();
return;
} else if (objectClass == UUID.class) {
final List<UUID> idList = ioDb.getListOfRawUUIDs(rs, count.value);
field.set(data, idList);
count.inc();
return;
}
if (objectClass == decorators.targetEntity()) {
Long parentIdTmp = null;
UUID parendUuidTmp = null;
try {
final String modelData = rs.getString(count.value);
parentIdTmp = Long.valueOf(modelData);
count.inc();
} catch (final NumberFormatException ex) {
final List<UUID> idList = ioDb.getListOfRawUUIDs(rs, count.value);
parendUuidTmp = idList.get(0);
count.inc();
} }
final Long parentId = parentIdTmp;
final UUID parendUuid = parendUuidTmp;
final String mappingKey = decorators.mappedBy();
// We get the parent ID ... ==> need to request the list of elements
if (objectClass == Long.class) { if (objectClass == Long.class) {
LOGGER.error("Need to retreive all primary key of all elements"); final List<Long> idList = ioDb.getListOfIds(rs, count.value, SEPARATOR_LONG);
//field.set(data, idList); field.set(data, idList);
count.inc();
return; return;
} else if (objectClass == UUID.class) { } else if (objectClass == UUID.class) {
LOGGER.error("Need to retreive all primary key of all elements"); final List<UUID> idList = ioDb.getListOfRawUUIDs(rs, count.value);
//field.set(data, idList); field.set(data, idList);
count.inc();
return;
} else if (objectClass == ObjectId.class) {
final List<ObjectId> idList = ioDb.getListOfRawOIDs(rs, count.value);
field.set(data, idList);
count.inc();
return; return;
} }
if (objectClass == decorators.targetEntity()) { if (objectClass == decorators.targetEntity()) {
if (decorators.fetch() == FetchType.EAGER) { final String destinationField = decorators.mappedBy();
throw new DataAccessException("EAGER is not supported for list of element..."); final Field typeDestination = AnnotationTools.getFieldNamed(objectClass, destinationField);
} else if (parentId != null) { final Class<?> destinationClass = typeDestination.getType();
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
final LazyGetter lambda = () -> { Long parentIdTmp = null;
@SuppressWarnings("unchecked") UUID parendUuidTmp = null;
final Object foreignData = ioDb.getsWhere(decorators.targetEntity(), ObjectId parendOidTmp = null;
new Condition(new QueryCondition(mappingKey, "=", parentId))); if (destinationClass == Long.class) {
if (foreignData == null) { final String modelData = rs.getString(count.value);
return; parentIdTmp = Long.valueOf(modelData);
} count.inc();
field.set(data, foreignData); } else if (destinationClass == UUID.class) {
}; final List<UUID> idList = ioDb.getListOfRawUUIDs(rs, count.value);
lazyCall.add(lambda); parendUuidTmp = idList.get(0);
} else if (parendUuid != null) { count.inc();
final LazyGetter lambda = () -> { } else if (destinationClass == ObjectId.class) {
@SuppressWarnings("unchecked") final List<ObjectId> idList = ioDb.getListOfRawOIDs(rs, count.value);
final Object foreignData = ioDb.getsWhere(decorators.targetEntity(), parendOidTmp = idList.get(0);
new Condition(new QueryCondition(mappingKey, "=", parendUuid))); count.inc();
if (foreignData == null) { }
return; final Long parentId = parentIdTmp;
} final UUID parendUuid = parendUuidTmp;
field.set(data, foreignData); final ObjectId parendOid = parendOidTmp;
}; final String mappingKey = decorators.mappedBy();
lazyCall.add(lambda); // We get the parent ID ... ==> need to request the list of elements
if (objectClass == Long.class) {
LOGGER.error("Need to retreive all primary key of all elements.");
//field.set(data, idList);
return;
} else if (objectClass == UUID.class) {
LOGGER.error("Need to retreive all primary key of all elements");
//field.set(data, idList);
return;
} else if (objectClass == ObjectId.class) {
LOGGER.error("Need to retreive all primary key of all elements");
//field.set(data, idList);
return;
}
if (objectClass == decorators.targetEntity()) {
if (decorators.fetch() == FetchType.EAGER) {
throw new DataAccessException("EAGER is not supported for list of element...");
} else if (parentId != null) {
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
final LazyGetter lambda = () -> {
@SuppressWarnings("unchecked")
final Object foreignData = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryCondition(mappingKey, "=", parentId)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
} else if (parendUuid != null) {
final LazyGetter lambda = () -> {
@SuppressWarnings("unchecked")
final Object foreignData = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryCondition(mappingKey, "=", parendUuid)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
} else if (parendOid != null) {
final LazyGetter lambda = () -> {
@SuppressWarnings("unchecked")
final Object foreignData = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryCondition(mappingKey, "=", parendOid)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
}
} }
} }
} catch (final Exception ex) {
ex.printStackTrace();
LOGGER.error("Fail to parse remote {}", ex.getMessage());
} }
} }

View File

@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess.addOnSQL; package org.atriasoft.archidata.dataAccess.addOnSQL;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
@ -6,10 +6,10 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List; import java.util.List;
import org.kar.archidata.dataAccess.CountInOut; import org.atriasoft.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DBAccessSQL; import org.atriasoft.archidata.dataAccess.DBAccessSQL;
import org.kar.archidata.dataAccess.LazyGetter; import org.atriasoft.archidata.dataAccess.LazyGetter;
import org.kar.archidata.dataAccess.QueryOptions; import org.atriasoft.archidata.dataAccess.QueryOptions;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
@ -31,10 +31,9 @@ public interface DataAccessAddOn {
boolean isCompatibleField(Field elem); boolean isCompatibleField(Field elem);
/** Insert data in the specific field (the field must be in the current db, otherwiise it does not work at all. /** Insert data in the specific field (the field must be in the current db, otherwiise it does not work at all.
* @param ps DB statement interface. * @param ioDb DB statement interface.
* @param data The date to inject. * @param data The date to inject.
* @param iii The index of injection * @param iii The index of injection
* @return the new index of injection in case of multiple value management
* @throws SQLException */ * @throws SQLException */
void insertData(final DBAccessSQL ioDb, PreparedStatement ps, final Field field, Object data, CountInOut iii) void insertData(final DBAccessSQL ioDb, PreparedStatement ps, final Field field, Object data, CountInOut iii)
throws Exception, SQLException, IllegalArgumentException, IllegalAccessException; throws Exception, SQLException, IllegalArgumentException, IllegalAccessException;
@ -76,9 +75,11 @@ public interface DataAccessAddOn {
/** Create associated table of the specific element. /** Create associated table of the specific element.
* @param tableName * @param tableName
* @param elem * @param primaryField
* @param field
* @param mainTableBuilder * @param mainTableBuilder
* @param ListOtherTables * @param preActionList
* @param postActionList
* @param createIfNotExist * @param createIfNotExist
* @param createDrop * @param createDrop
* @param fieldId * @param fieldId
@ -136,6 +137,7 @@ public interface DataAccessAddOn {
* @param actions Asynchronous action to do after main request. */ * @param actions Asynchronous action to do after main request. */
default void asyncUpdate( default void asyncUpdate(
final DBAccessSQL ioDb, final DBAccessSQL ioDb,
final Object previousData,
final String tableName, final String tableName,
final Object localId, final Object localId,
final Field field, final Field field,
@ -145,6 +147,13 @@ public interface DataAccessAddOn {
} }
/** Some annotation need to collect data before updating the current values
* @param field
* @return */
default boolean isPreviousDataNeeded(final Field field) {
return false;
}
default void drop(final DBAccessSQL ioDb, final String tableName, final Field field, final QueryOptions options) default void drop(final DBAccessSQL ioDb, final String tableName, final Field field, final QueryOptions options)
throws Exception { throws Exception {

View File

@ -1,6 +1,6 @@
package org.kar.archidata.dataAccess.addOnSQL.model; package org.atriasoft.archidata.dataAccess.addOnSQL.model;
import org.kar.archidata.model.OIDGenericDataSoftDelete; import org.atriasoft.archidata.model.OIDGenericDataSoftDelete;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column; import jakarta.persistence.Column;

Some files were not shown because too many files have changed in this diff Show More