jOOQ and bridge tables - java

I'm trying to imagine how to use jOOQ with bridge tables.
Suppose you have
CREATE TABLE TableA (
id BIGSERIAL PRIMARY KEY
)
CREATE TABLE TableB (
id BIGSERIAL PRIMARY KEY
)
CREATE TABLE TableBridge (
id BIGSERIAL,
table_a_id INTEGER NOT NULL,
table_b_id INTEGER NOT NULL,
CONSTRAINT tablea_pk_id PRIMARY KEY (table_a_id)
REFERENCES TableA (id) MATCH SIMPLE,
CONSTRAINT tableb_pk_id PRIMARY KEY (table_b_id)
REFERENCES TableB (id) MATCH SIMPLE
)
When mapping this schema using jOOQ there will be three record classes, TableARecord, TableBRecord and TableBridgeRecord.
If I want to persist through an insert a record for TableA, should I simply first create and persist the TableB records, then persit rows for TableB and then manually add the TableBridge rows? Isn't there any way to automatically save also the rows in the bridge table?

There are several ways to solve this kind of problem:
1. Do it with a "single" jOOQ statement (running three SQL statements)
The most idiomatic way to solve this kind of problem with standard jOOQ would be to write a single SQL statement that takes care of all three insertions in one go:
ctx.insertInto(TABLE_BRIDGE)
.columns(TABLE_BRIDGE.TABLE_A_ID, TABLE_BRIDGE.TABLE_B_ID)
.values(
ctx.insertInto(TABLE_A)
.columns(TABLE_A.VAL)
.values(aVal)
.returning(TABLE_A.ID)
.fetchOne()
.get(TABLE_A.ID),
ctx.insertInto(TABLE_B)
.columns(TABLE_B.VAL)
.values(bVal)
.returning(TABLE_B.ID)
.fetchOne()
.get(TABLE_B.ID)
)
.execute();
The above works with jOOQ 3.8. Quite possibly, future versions will remove some of the verbosity around returning() .. fetchOne() .. get().
2. Do it with a single SQL statement
I assume you're using PostgreSQL from your BIGSERIAL data type usage, so the following SQL statement might be an option to you as well:
WITH
new_a(id) AS (INSERT INTO table_a (val) VALUES (:aVal) RETURNING id),
new_b(id) AS (INSERT INTO table_b (val) VALUES (:bVal) RETURNING id)
INSERT INTO table_bridge (table_a_id, table_b_id)
SELECT new_a.id, new_b.id
FROM new_a, new_b
The above query is currently not supported entirely via jOOQ 3.8 API, but you can work around the jOOQ API's limitations by using some plain SQL:
ctx.execute(
"WITH "
+ " new_a(id) AS ({0}), "
+ " new_b(id) AS ({1}) "
+ "{2}",
// {0}
insertInto(TABLE_A)
.columns(TABLE_A.VAL)
.values(aVal)
.returning(TABLE_A.ID),
// {1}
insertInto(TABLE_B)
.columns(TABLE_B.VAL)
.values(bVal)
.returning(TABLE_B.ID),
// {2}
insertInto(TABLE_BRIDGE)
.columns(TABLE_BRIDGE.TABLE_A_ID, TABLE_BRIDGE.TABLE_B_ID)
.select(
select(field("new_a.id", Long.class), field("new_b.id", Long.class))
.from("new_a, new_b")
)
);
Clearly also here, there will be improvements in future jOOQ APIs.
3. Do it with UpdatableRecords
In this particular simple case, you could get away simply by calling:
TableARecord a = ctx.newRecord(TABLE_A);
a.setVal(aVal);
a.store();
TableBRecord b = ctx.newRecord(TABLE_B);
b.setVal(bVal);
b.store();
TableBridgeRecord bridge = ctx.newRecord(TABLE_BRIDGE);
bridge.setTableAId(a.getId());
bridge.setTableBId(b.getId());
bridge.store();

Related

JPA Join with aggregate function

I am a beginner when it comes to JPA and I am struggling to write the proper entity classes for my database structure.
Firstly, here are my tables:
create table article (
id varchar(100) not null primary key,
title varchar(255) not null
)
create table article_provider (
providerId varchar(60) not null,
articleId varchar(100) null,
isOnSale BOOL NOT NULL,
constraint article_provider_articleid_fk foreign key (articleId) references article (id) on update cascade on delete cascade
)
As you can see I have a one-to-many relationship (every article can have 0..n providers).
Now when accessing articles I would also like to know, if the article is on sale by any of it's providers. For this I would use the following SQL query:
SELECT article.*, MAX(article_provider.isOnSale) AS isOnSale FROM article
LEFT JOIN article_provider ON article.id = article_provider.articleId
GROUP BY article.id
How would I best go about fetching this type of result using Hibernate/JPA?
Your query is not right -- you use attributes in SELECT clause that are not in GROUP BY clause, but for example to select Article.id + MAX(whatever) you can use something like:
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Article> query = builder.createQuery(Article.class);
Root<Article> root = query.from(Article.class);
Join<Article, ArticleProvider> join = root.join(Article_.provider, JoinType.LEFT);
query.multiselect(root.get(Article_.id), builder.max(sale));
query.groupBy(root.get(Article_.id));
// query will return collection of scalar attributes (ID + Number)
Object result = entityManager.createQuery(query).getResultList()
To select exactly Article.* + MAX(sales) you would need to use the above as a subquery and join again with Article entity.
But there are simillar of examples elsewhere: How do I write a MAX query with a where clause in JPA 2.0?
EDIT:
another tip: https://www.thoughts-on-java.org/hibernate-tip-subquery-criteriaquery/
correction/clarification: I have only assumed you want to use Criteria API where subquery and combining the results for Hibernate is probably the only way. But you may try to use JPA query language and/or different provider where subquery with multiple results can be used -- jpa eclipselink subquery in from clause

Merge and When Matched query giving an error sql server

I have a query which I am trying to test. The query should update the data if it finds data in the table with existing primary key. If it doesn't then insert into the table.
The Primary key is of type int and in the properties I can see Identity is set to "True" which I assume it means that it will automatically set the new id for the primary if it is inserted.
MERGE INTO Test_table t
USING (SELECT 461232 ID,'Test1-data' Fascia FROM Test_table) s
ON (t.ID = s.ID)
WHEN MATCHED THEN
UPDATE SET t.Fascia = s.Fascia
WHEN NOT MATCHED THEN
INSERT (Fascia)
VALUES (s.Fascia);
The issue here is this query doesn't work and it never inserts the data or updates. Also, query gets compiled and I don't get any compilation error
Also the reason I want this query is to work because then I will use Java prepared statement to query the database so I am assuming I can do
SELECT ? ID,? Fascia FROM Test_table
So that I can pass the values with set methods in java.
Please let me know if there is something wrong in my query.
You are selecting from the target table as your source.
You either need to remove your FROM Test_table or have at least 1 row in Test_table prior to your merge.
rextester demo: http://rextester.com/XROJD28508
MERGE INTO Test_table t
USING (SELECT 461232 ID,'Test1-data' Fascia --FROM Test_table
) s
ON (t.ID = s.ID)
WHEN MATCHED THEN
UPDATE SET t.Fascia = s.Fascia
WHEN NOT MATCHED THEN
INSERT (Fascia)
VALUES (s.Fascia);

Create table with primary key using jOOQ

jOOQ has CREATE TABLE syntax as stated in the documentation:
create.createTable(AUTHOR)
.column(AUTHOR.ID, SQLDataType.INTEGER)
.column(AUTHOR.FIRST_NAME, SQLDataType.VARCHAR.length(50))
.column(AUTHOR_LAST_NAME, SQLDataType.VARCHAR.length(50))
.execute();
I'm wondering how to define which column belongs to the primary key? So is there a way in jOOQ to create a CREATE TABLE statement with PRIMARY KEY information?
I'm specifically interested in a solution for SQLite, which doesn't have syntax to add the primary key afterwards, so I think in worst case I have to go to a DB specific solution?
This feature has been implemented for jOOQ 3.8: #4050.
create.createTable(AUTHOR)
.column(AUTHOR.ID, SQLDataType.INTEGER)
.column(AUTHOR.FIRST_NAME, SQLDataType.VARCHAR.length(50))
.column(AUTHOR_LAST_NAME, SQLDataType.VARCHAR.length(50))
.constraints(
constraint("PK_AUTHOR").primaryKey(AUTHOR.ID)
)
.execute();
Since jOOQ 3.6 (#3338), you can also use the ALTER TABLE statement to add a constraint after creating the table:
create.alterTable(AUTHOR)
.add(constraint("PK_AUTHOR").primaryKey(AUTHOR.ID))
.execute();

Java DB How to Insert Values for Foreign Keys into Table Column

I am using java DB database and NetBeans 8.0 for a desktop application
I am also using a PreparedStatement to query the database.
below is the code for creating the tables.
CREATE TABLE ALUMNUS (
ALUMNUA_ID INT NOT NULL PRIMARY KEY
GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
FIRST_NAME VARCHAR (45),
LAST_NAME VARCHAR (45),
OTHER_NAME VARCHAR (100)
);
CREATE TABLE DUES (
ID INT NOT NULL PRIMARY KEY
GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
PAYMENT_YEAR DATE,
AMOUNT DOUBLE,
ALUMNUS_ID INT
);
--FOREIGN KEY
ALTER TABLE APP.DUES
ADD FOREIGN KEY (ALUMNUS_ID) REFERENCES APP.ALUMNUS(ID);
Now I want to insert, delete and update the foreign key values in APP.DUES table. what is the best option; trigger , stored procedure or the preparedstatement?
An example will be good.
If you want to primarily insert into the DUES table, you would use a sub select in SQL. I havent tested it with Java DB, but it basically looks like:
INSERT INTO DUES(PAYMENT_YEAR, AMOUNT,ALUMNUS_ID)
VALUES(2014, 100.0,
(SELECT ALUMNUA_ID from ALUMNUS where ...));
You need to catch the "not found" error case and prepend a INSERT (and need to catch the duplicate case for that as well).
See also: Insert Data Into Tables Linked by Foreign Key

Howto return ids on Inserts with Ibatis ( with RETURNING keyword )

I'm using iBatis/Java and Postgres 8.3.
When I do an insert in ibatis i need the id returned.
I use the following table for describing my question:
CREATE TABLE sometable ( id serial NOT NULL, somefield VARCHAR(10) );
The Sequence sometable_id_seq gets autogenerated by running the create statement.
At the moment i use the following sql map:
<insert id="insertValue" parameterClass="string" >
INSERT INTO sometable ( somefield ) VALUES ( #value# );
<selectKey keyProperty="id" resultClass="int">
SELECT last_value AS id FROM sometable_id_seq
</selectKey>
</insert>
It seems this is the ibatis way of retrieving the newly inserted id. Ibatis first runs a INSERT statement and afterwards it asks the sequence for the last id.
I have doubts that this will work with many concurrent inserts. ( discussed in this question )
I'd like to use the following statement with ibatis:
INSERT INTO sometable ( somefield ) VALUES ( #value# ) RETURNING id;
But when i try to use it within a <insert> sqlMap ibatis does not return the id. It seems to need the <selectKey> tag.
So here comes the question:
How can i use the above statement with ibatis?
The <selectKey> element is a child of the <insert> element and its content is executed before the main INSERT statement. You can use two approaches.
Fetch the key after you have inserted the record
This approach works depending on your driver. Threading can be a problem with this.
Fetching the key before inserting the record
This approach avoids threading problems but is more work. Example:
<insert id="insert">
<selectKey keyProperty="myId"
resultClass="int">
SELECT nextVal('my_id_seq')
</selectKey>
INSERT INTO my
(myId, foo, bar)
VALUES
(#myId#, #foo#, #bar#)
</insert>
On the Java side you can then do
Integer insertedId = (Integer) sqlMap.insert("insert", params)
This should give you the key selected from the my_id_seq sequence.
Here is simple example:
<statement id="addObject"
parameterClass="test.Object"
resultClass="int">
INSERT INTO objects(expression, meta, title,
usersid)
VALUES (#expression#, #meta#, #title#, #usersId#)
RETURNING id
</statement>
And in Java code:
Integer id = (Integer) executor.queryForObject("addObject", object);
object.setId(id);
This way more better than use :
It's simpler;
It have not requested to know sequence name (what usually hidden from postgresql developers).

Categories

Resources