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

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.

Related

Is there a way of using a lookup table as a converter in JPA?

Imagine that I have a simple entity as follows:
#Entity
#Table(name = "PERSON")
public class Person {
#Id
#Column(name = "NAME")
private String name;
#Column(name = "GENDER")
private String gender;
}
And two tables, the actual table holding the information and a lookup table.
TABLE PERSON (
NAME VARCHAR2 NOT NULL,
GENDER INT NOT NULL);
TABLE GENDER_LOOKUP (
GENDER_ID INT NOT NULL,
GENDER_NAME VARCHAR2 NOTNULL);
I want to save the information from my entity into the table, so that the String field gender is automatically converted to the corresponding gender int, using the lookup table as a reference. I thought of two approaches, but I was wondering if there was a more efficient way.
Create an enum and use ordinal enum to persist. I would rather avoid this because I'd like to have only one "source of truth" for the information and for various business reasons, it has to be a lookup table.
Use the #Converter annotation and write a custom converter. I think that this would require me to query the table to pull out the relevant row, so it would mean that I would have to make a JPA call to the database every time something was converted.
I'm currently planning to use 2, but I was wondering if there was any way to do it within the database itself, since I assume using JPA to do all of these operations has a higher cost than if I did everything in the database. Essentially attempt to persist a String gender, and then the database would look at the lookup table and translate it to the correct Id and save it.
I'm specifically using openJpa but hopefully this isn't implementation specific.
Since you seriously considered using enum, it means that GENDER_LOOKUP is static, i.e. the content doesn't change while the program is running.
Because of that, you should use option 2, but have the converter cache/load all the records from GENDER_LOOKUP on the first lookup. That way, you still only have one "source of truth", without the cost of hitting the database on every lookup.
If you need to add a new gender1, you'll just have to restart the app to refresh the cache.
1) These days, who know what new genders will be needed.

How to persist data through JPA without needing a primary key

I have data that flows through my application and normally it doesn't need to be bothered but to implement a new feature I need to store it temporarily (e.g. 1 hr). The data going in can be the exact same as something that is already in there so there is no need for a primary key. However, with JPA Entities need an Id but I don't need/want one. This is preventing me from getting it working.
This is through Spring using JPA. Since the data is moving in and out of the database frequently, the use of an auto generated ID is discouraged because it'll go through the IDs in a few years time. I have tried to make it embeddable to which it says I need to do a component scan to find where it is used but if I make it an entity then it gives me the error that it needs a primary key.
This is my entity that stores the data I need to persist.
#Entity
#Table(name = "quoteOrderHistory")
public class QuoteOrderHistory {
#Column(name = "storeNumber")
private int storeNumber;
#Column(name = "invoiceNumber")
private String invoiceNumber;
#Column(name = "quoteSaleDate")
private Date quoteSaleDate;
#Column(name="orderTotal")
private BigDecimal orderTotal;
#Column(name="orderHistoryDate")
private Timestamp orderHistoryDate;
// Constructors, Getters and Setters
}
This is my repository to access the data.
#Repository
public interface QuoteOrderHistoryRepository extends JpaRepository<QuoteOrderHistory, Long> {
#Query("DELETE FROM QuoteOrderHistory q WHERE q.orderHistoryDate > date")
void deleteAllExpired(Date date);
#Query("SELECT q FROM QuoteOrderHistory q WHERE q.storeNumber = ?1 AND q.invoiceNumber = ?2 ORDER BY q.orderHistoryDate DESC")
List<QuoteOrderHistory> findAllByStoreAndInvoiceDesc(int storeNumber, String invoiceNumber);
}
I can't figure out to get this to work. Again a primary key isn't needed since it's suppose to support duplicate entries. If there is another way around this without using JPA then I'm all for it but currently it seems to be the easiest to persist the data. If you need anymore information just let me know. I also might be missing something that can be done to avoid this all together but I'm not that familiar with JPA. So all help is appreciated.
You shouldn't run out of IDs for a column if you use the correct size. Stop trying to fight your framework and just add an auto-incrementing column.
https://hashrocket.com/blog/posts/running-out-of-ids
Let's say business is so good that we are inserting 10,000 records per
minute into our table. So, how long would it take to max out our
sequence? 1750380517 years
From How large can an id get in postgresql
Name Storage Size Description Range
smallint 2 bytes small-range integer -32768 to +32767
integer 4 bytes usual choice for integer -2147483648 to +2147483647
bigint 8 bytes large-range integer -9223372036854775808 to 9223372036854775807
serial 4 bytes autoincrementing integer 1 to 2147483647
bigserial 8 bytes large autoincrementing integer 1 to 9223372036854775807
If you're desperate to not use an id column for some reason I cannot possibly comprehend, it looks like you can do it in JPA by making every column part of the primary key description, but then your deletes and updates will delete/update any number of records. I HAVE NOT TRIED THIS. I WOULD NOT IMPLEMENT THIS ON A PRODUCTION SERVER.
https://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#No_Primary_Key
Sometimes your object or table has no primary key. The best solution
in this case is normally to add a generated id to the object and
table. If you do not have this option, sometimes there is a column or
set of columns in the table that make up a unique value. You can use
this unique set of columns as your id in JPA. The JPA Id does not
always have to match the database table primary key constraint, nor is
a primary key or a unique constraint required.
If your table truly has no unique columns, then use all of the columns
as the id. Typically when this occurs the data is read-only, so even
if the table allows duplicate rows with the same values, the objects
will be the same anyway, so it does not matter that JPA thinks they
are the same object. The issue with allowing updates and deletes is
that there is no way to uniquely identify the object's row, so all of
the matching rows will be updated or deleted.
If your object does not have an id, but its table does, this is fine.
Make the object an Embeddable object, embeddable objects do not have
ids. You will need a Entity that contains this Embeddable to persist
and query it.
Jazzepi stated was correct but I was strictly requested not to use an auto generated number as the ID. Therefore, people linked this here depicting using a UUID. This is the best choice for this problem since the objects in the database are timed to be in there no more than a few hours. Since this is the case, a UUID will never overflow and the likelihood of a repeated UUID inside of the table any given time is almost zero since most won't stay there.
New Entity class:
#Entity
#Table(name = "quoteOrderHistory")
public class QuoteOrderHistory {
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "org.hibernate.id.UUIDGenerator")
#Column(name = "uuid", unique = true)
private String uuid;
#Column(name = "storeNumber")
private int storeNumber;
#Column(name = "invoiceNumber")
private String invoiceNumber;
#Column(name = "quoteSaleDate")
private Date quoteSaleDate;
#Column(name="orderTotal")
private BigDecimal orderTotal;
#Column(name="orderHistoryDate")
private Timestamp orderHistoryDate;
// Constructor, getters, setters
}

Insert to hibernate audit table on condition

I am using hibernate envers to audit my entity. I have entity with next fields:
public class Settings
#Id
#Column(length = 80)
private String key;
#NotNull
#Column(length = 1200)
private String value;
#Version
#Column(columnDefinition = "numeric")
private Integer version;
}
It contains key-value pairs. Some of fields in this table are updated automatically. The question is: is it possible to insert or not insert record into _AUDIT table depending on value of 'key' property?
Example:
There are records in my table:
|KEY |VALUE |VERSION
_________________________________
|laskCheckDate|12-01-2017|0
|numberOfsmth |3 |0
I want to insert record to _AUDIT table if numberOfsmth is updated/deleted, but NOT insert if laskCheckDate is updated.
What you would need to do is extend the EnversPostUpdateEventListenerImpl event listener class and add your logic to check for the necessary entity type and values and decide whether to call into the super-class to audit the update or not.
Unfortunately the above approach is a bit intrusive for the novice user and I would certainly not recommend doing this if you're not super familar with Hibernate ORM and Envers.
There are some thoughts on conditional auditing in HHH-11326 which is tentatively planned for Envers 6.0 where you can influence auditing based on hooks you tie into your entities through annotations.
Should you decide to move forward and extend the listeners in 5.x, just be mindful that you should always allow the INSERT of your entity to occur. This becomes extremely important if you're using the ValidityAuditStrategy as the UPDATE expects an INSERT revision type to exist in the table or else the strategy asserts.
If all you want to control are UPDATEs, then this should not be a problem for you regardless of which strategy you leverage.

Does it make sense to create entity table with only ID attribute?

Does it make sense to create a single entity when it should only contain the #Id value as a String?
#Entity
class CountryCode {
#Id
String letterCode; //GBR, FRA, etc
}
#Entity
class Payment {
CountryCode code;
// or directly without further table: String countryCode;
}
Or would you just use the letterCode as the stringvalue instead of creating the CountryCode entity?
It should later be possible for example to fetch all payments that contain a specific countrycode. This might be possible with both solutions. But which is the better one (why)?
Yes you can if you are using the entity as a lookup. In your example, you may want to add a column for description congaing (France, Great Britain, etc.) for the letter code and a third column whether it is active or not and maybe columns for when inserted and when it was last changed.
It makes sense to create such table to provide consistency of data, that is that no Payment is created with non-existing CountryCode. Having a separate entity (that is table) together with foreign key on Payment allows checking for consistency in database.
Another possible approach is to have check constraint on the code field but this is error prone if codes are added/deleted and/or there are more than one column of this type.
Adding the letterCode the the Payment Class as String Attribute (Or Enum to prevent typo errors) will increase the fetch performance as you do not need to create a join over your CountryCode Table.

Jdo (datanucleus) integer_idx column and databinding

I've been trying to do a simple one to many object binding in DataNucleus JDO. It's just two classes (i stripped a simple fields):
#PersistenceCapable(table="ORDER",schema="mgr")
public class Order {
#PrimaryKey(column="id")
#Persistent(valueStrategy=IdGeneratorStrategy.NATIVE,column="id")
private Long id;
#Persistent(defaultFetchGroup="false",column="customer_id")
#Element(column="customer_id")
private Customer customer;
}
And a class Customer having a list of orders
#PersistenceCapable(table="customer",schema="mgr",identityType=IdentityType.DATASTORE)
#DatastoreIdentity(strategy=IdGeneratorStrategy.NATIVE)
public class Customer {
#PrimaryKey
#Persistent(valueStrategy=IdGeneratorStrategy.NATIVE,column="id")
private Long id;
#Persistent(mappedBy="customer")
private List<Order> orders;
}
The database table setup is extremely simple(a table for customer and a table for orders with a foreign key (customer_id) referencing customer). Yet, when i try to insert some orders for customer i receive an error
javax.jdo.JDODataStoreException: Insert of object
"test.Order#17dd585" using statement "INSERT INTO
ORDER
(USER_COMMENT,ORDER_DATE,STATUS,CUSTOMER_ID,ORDERS_INTEGER_IDX)
VALUES (?,?,?,?,?)" failed : Unknown column 'ORDERS_INTEGER_IDX' in
'field list'
Somehow DataNucleus is assuming, there is a column ORDERS_INTEGER_IDX (such column does not exist in the database). The only idea, that came to my mind is http://www.datanucleus.org/products/datanucleus/jdo/metadata_xml.html
In some situations DataNucleus will add a special datastore column to
a join table so that collections can allow the storage of duplicate
elements. This extension allows the specification of the column name
to be used. This should be specified within the field at the
collection end of the relationship. JDO2 doesnt allow a standard place
for such a specification and so is an extension tag.
So cool! 'in some situations'. I have no idea how to make my situation not to be a subset of 'some situations' but I have no idea, how to get this working. Perhaps someone has allready met the "INTEGER_IDX" problem? Or (it is also highly possible) - im not binding the data correctly :/
So you create the schema yourself. Your schema is inconsistent with metadata. You run persistence without validating your metadata against schema, and an exception results. DataNucleus provides you with SchemaTool to create or validate the schema against your metadata, so that would mean that you can detect the problem.
You're using an indexed list, so it needs an index for each element (or how else is it to know what position an element is in?). How can it assume there is an index? well it's a thing called the JDO spec (publically available), which defines indexed lists. If you don't want positions of elements storing then don't use a List (the Java util class for retaining the position of elements) ... so I'd suggest using a Set since that doesn't need position info (hence no index).
You also have a class marked as datastore identity, and then have a primary-key. That is a contradiction ... you have one or the other. The docs define all of that, as well as how to have a 1-N List relation ("JDO API" -> "Mapping" -> "Fields/Properties" -> "1-N Relations" -> "Lists" or "Sets")

Categories

Resources