Hibernate Insert Options - java

I am trying to efficiently implement a Hibernate connection in Java and recently came across two ways of adding database rows using Hibernate which I would like to discuss:
Given are the following SQL Tables:
CREATE TABLE Customer (
customer_id INT NOT NULL AUTO_INCREMENT,
customer_name VARCHAR(255) NOT NULL UNIQUE KEY,
PRIMARY KEY (customer_id)
) ENGINE=InnoDB;
CREATE TABLE Orders (
order_id INT NOT NULL AUTO_INCREMENT,
customer_name VARCHAR(255) NOT NULL,
order_date DATETIME,
PRIMARY KEY(order_id),
FOREIGN KEY (customer_name) REFERENCES Customer(customer_name)
ON UPDATE CASCADE
ON DELETE CASCADE
) ENGINE=InnoDB;
Customer is parent table and Orders is child table, 1:n relationship.
When I want to insert new Orders for a certain customer, which is already in the database I know to options how to achieve it :
(1)
// Saving the child table
// Query the customer
Customer cu = (Customer) session.get(Customer.class, 1);
Orders od = new Orders(cu, new Date());
session.save(od);
// This seems to work fast and efficient
(2) Saving the parent table
// Due to bidirectional relationship one can also do:
Orders od = new Orders(cu, d);
cu.getOrderses().add(od);
session.save(cu);
// This is what is often shown in Hibernate tutorials, but seems to be really
// inefficient because the entire HashSet of the parent table needs to loaded //first,
//before the new object can be added.
Benchmark example : (1) 0 sec (2) 5 sec
Why is way (2) often shown in Tutorials even though it seems to be inefficient ?

Which option you should choose depents on your model. Do you have/need a collection of orders in the Customer? Then you have to add the object anyway and you should choose (2).
Take care of proper fetch configuration or otherwise Hibernate will load the whole collection even if you don't need it. For that case take a look at Hibernate - How to persist a new item in a Collection without loading the entire Collection.
If you don't have a collection in Customer then use option (1).
(2) is often shown in tutorials to show the capabilities of Hibernate/ORMs in general. It's also more object oriented but I would recomment to create a method Customer.addOrder() or even Customer.creatOrder() instead od directly manipulating the collection from outside.

Related

Spring data - force merge of manytoone element when inserting main entity

So I have a bit of an annoying database layout, where I have to have a dynamic list side table which uses a natural key. And this second table should get a new entity added if it doesn't exist yet, otherwise updated.
So to say:
citizen
city
id
code
postal_code
name
The Citizen entity has
#ManyTone(cascade = javax.persistence.CascadeType.ALL)
#Cascade(CascadeType.SAVE_UPDATE)
#JoinColumn(name = "postal_code"
private City city;
Now when I create a new Citizen and City instance, and insert them it works fine the first time. However the second time I get a PK violation on city since there's already a city with the given code. Now what I want to achieve is that the city gets saved or updated when the citizen gets saved. (not the best example I know)
To persist I used the 'JpaRepository -> saveAll' method.
Worst case I could ofcourse first persist all cities, and then set the persisted instances on the citizen but I'd like to avoid that.
The side entities to get created/updated as passed in without first having to manually insert them myself.
I think you will need to verify if the city exists before persist a new Citizen, and if it exists, you will need to set the City property with the object you get from the database.
Otherwise, it will try to persist a new city with the same key, and it will throw the PK violation error.

JOOQ: How to resolve foreign keys as objects?

Say I have a table that references another table, in this case "TestScenarios" references "TestSchemas". So each TestScenario HAS-A TestSchema.
I autogenerated DAOs, however, when fetching TestScenario instance via the DAO the TestSchema field is an integer, not a TestSchema-object. How can I get JOOQ to resolve foreign keys directly as objects up to a certain depth?
CREATE TABLE "TestScenarios"
(
id integer DEFAULT nextval('"TestScenarios_id_seq"'::regclass) NOT NULL,
name varchar,
version bigint,
"testSchema" integer,
);
ALTER TABLE "TestScenarios"
ADD CONSTRAINT "TestScenarios_pkey"
PRIMARY KEY (id);
ALTER TABLE "TestScenarios"
ADD CONSTRAINT "testSchemaFk" FOREIGN KEY ("testSchema")
REFERENCES "TestSchemas" (id)
ON UPDATE NO ACTION
ON DELETE NO ACTION;
COMMIT;
DAOs don't have such a feature, but with jOOQ's DSL API, you could use implicit joins to quickly fetch also parent tables for any given child table, e.g.
ctx.select(TestScenarios.asterisk(), TestScenarios.TestSchemas().asterisk())
.from(TestScenarios)
.fetch();
There are other approaches, but there's never going to be anything automatic about "object graph persistence" in the way JPA would offer it, for example. The jOOQ philosophy is to always express every query explicitly - maybe profit from some mapping sugar, but to never implicitly and automatically fetch entire object graphs.

JAVA: concurrent sequence

in my application I have a table to store User informations as Name, Surname, Address, Type (Employee, Manager, Administrator), UniqueNumber.
I need to store in field UniqueNumber for each record in the table User a sequence number between 1 and 100 univocal for Type: so can exist a record with Type = 'Employee' and UniqueNumber=1 and a record with Type='Manager' and UniqueNumber=1 but cannot exist another record with Type='Employee' and UniqueNumber=1.
I was thinking to use a sequence of the database for each Type. This help me to manage concurrency where more users can be inserted a record at the same time.
Is this a good solution?
Is there an alternative?
I see AtomicInteger class but I don't understand how to use it for each Type of my table.
Thanks.
AtomicInteger just provides a thread-safe usage of Integer values.
Your problem has little to do with Java, unless you're creating every single User entity in one pass. If that's the case, you can declare 3 int values and increment them for each entity you're creating (or 3 static or local AtomicInteger values if you need to handle concurrency).
Best way to do it would be to create one sequence per Type, or get the next available value for a given Type using a Max query. --> Depends on your needs
What happens when you have more than 100 values for a Type ?
We can create such code in java, but that's unnecessarily complicated. It involves a Map and AtomicInteger() and you'd use the incrementAndGet() method quite frequently.
On the other hand, you can create two tables. One for UserTypes, another for users. Then the users table can have a foreign key to the types. Problem solved on the database level. This is cool because the database will enforce this constraint.
This is how you can do it with MS SQL Server:
CREATE TABLE UserTypes (
ID INT Identity(1, 1) NOT NULL,
TypeName VARCHAR(128) NOT NULL,
PRIMARY KEY (ID)
)
CREATE TABLE Usr (
ID INT Identity(1, 1) NOT NULL,
TypeId INT NOT NULL,
MoreData VARCHAR(256) NULL,
CONSTRAINT fk_types FOREIGN KEY (TypeId) REFERENCES UserTypes (ID)
)

Inherited table with unique constraint is not generated as an UpdatableRecord

I have the following table definitions:
CREATE TABLE parent
(
id bigserial NOT NULL,
info text,
member_uuid uuid NOT NULL DEFAULT uuid_generate_v4(),
CONSTRAINT parent_pkey PRIMARY KEY (id)
);
CREATE TABLE child
(
-- Inherited from table parent: id bigint NOT NULL DEFAULT nextval('parent_id_seq"::regClass),
-- Inherited from table parent: info text
-- Inherited from table parent: member_uuid uuid NOT NULL DEFAULT uuid_generate_v4(),
member_info text,
CONSTRAINT child_member_uuid_unique UNIQUE (member_uuid)
)
INHERITS (parent);
I wish to generate the second table as a JOOQ POJO and be able to manipulate it. In particular, I wish to be able to do the following:
ChildRecord record = dslContext.newRecord(CHILD)
.setMemberInfo(...)
.insert();
record.getMemberUuid(); // autogenerated upon insert
However, in the above case with the given definition, JOOQ generates the following POJO:
public class ChildRecord extends TableRecordImpl<ChildRecord> implements Record4<Long, String, UUID, String>
Which is not an UpdatableRecord. This means that I am unable to call refresh() on this record and that the autogenerated UUID value is not available:
ChildRecord record = dslContext.newRecord(CHILD)
.setMemberInfo(...)
.insert();
record.getMemberUuid(); // null
There is a workaround, which is a bit dirty:
ChildRecord record = dslContext.newRecord(CHILD)
.setMemberInfo(...)
.setMemberUuid(UUID.randomUUID())
.insert();
record.getMemberUuid(); // available
However, there is no guarantee that the uuid_generate_v4() implementation is always going to be exactly the same as the java UUID.randomUUID() implementation.
Is this behaviour 'as expected'? Is there a workaround aside from what I have mentioned above?
After some more research, I read through the JOOQ docs, which state:
Any Record can be updatable, if
it represents a record from a table or view - a TableRecord
its underlying table or view has a "main unique key", i.e. a primary key or at least one unique key
As far as I can see, condition one is not fulfilled by my child table (due to the lack of a PK), while condition two is fulfilled. By the wording of the docs, I can surmise that both of these need to be true for the Record to be an UpdatableRecord. Insofar - this JOOQ behaviour is correct and as-expected.
Unfortunately this does not solve the issue of using values that should be autogenerated. One possible workaround might be to use an SQL statement like:
dslContext.update.<...>.setMemberUuid(select("select uuid_generate_v4()")).<...>
though this is extremely cumbersome and rather error-prone, and most likely will not solve the issue of the value not being refreshed. For now I will have to live with Java's UUID.randomUUID() method, until something better comes along.

JPA - How to use table default value for primary key generation?

I have a table with primary key generation of TO_NUMBER(TO_CHAR(SYSDATE#!,'YYDDD')||LPAD(TO_CHAR(SEQ_REFID.NEXTVAL),11,'0'))
This has been given as default value for the table. When I insert through JDBC, I could leave the column as NULL, so the pk would be generated/defaulted and i'll get the key using getGeneratedKeys() method.
I require similar behavior using JPA. I'm a beginner in JPA. Pl help.
Database used is Oracle 11g.
EDIT: The above mentioned value is not required to be table default. It can be applied from JPA layer if it is possible.
Other Entities depends on this Entity for the pk. PK must be passed over to all child tables.
#Entity
public class Entity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
Can also be
GenerationType.AUTO
GenerationType.SEQUENCE
GenerationType.TABLE
This reference describes the various strategies
Add the following annotation to the id field:
#Column(insertable = false)
This way, JPA will ignore the field when inserting new values and the database automatically generates the desired key.
However, you shouldn't use such a primary key. It effectively contains 2 different kinds of data in one column which should better be split into two seperate columns.
Make a simple id column with an ascending integer (and absolutely meaning other than "this is entry nr. x"). Then add an additional column with the current timestamp. This timestamp can have a default value and be protected against updates.
This is how it's supposed to be and not only simplifies your queries, but also improves the performance. You can query the table for entries of a specific hour, week and so on, or generate detailed statistics.
Don't try to put multiple information into one column. There's no advantage.
Where did you get the idea that this default PK was a good idea?
If you want the creation time of the row, add a column to your table. Don't embed it in the PK like this.

Categories

Resources