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
}
Related
I am new to using Spring Data Jpa but I understand one normally creates a 'Model'/ 'Entity' class in Java that represents a row of data in your database table. This makes sense to me when I have a small table with 3 columns/ class attributes, but what about the case of a table with 100 columns? One hardly creates a 'Model' class with 100 attributes? The reason I ask this is because I am storing a large adjacency matrix of train stations in a MySQL database table. I want to interact with this data in my Java Spring application but cannot get my head around how to do this.
I am open to the fact that I may be storing the data incorrectly or should not be using Spring Data JPA at all in this situation. Any advice would be greatly appreciated!
Thanks.
EDIT:
I'll try to explain what I'm trying to do more clearly. I would like to essentially import a copy of this 'Stations' (adjacency matrix) database table into my Java Spring application. I don't know how best to perform this 'import', or what Java data structure to store the table in. I would like to be able to run algorithms like BFS on the data once in Java.
The data in the table is an adjacency matrix showing a graph of a train network. A '1' shows the stations are connected by a track and a '0' shows no connection.
If your model is truly represented with a hundred attributes, then yes, you can create an entity that has a hundred properties. But I think your model needs reworked because the way I look at this, inserting a new record would require a schema change.
Since I don't know what those columns represent, I'm going to assume they're maybe next and previous stations? This would be a simple model to represent this.
#Entity
#Table(name = "station")
#Getter
#Setter
public class Station {
#Id
private Long id;
#Column(name = "name")
private String name;
#OneToOne
#JoinColumn(name = "previous_station_id")
private Station previous;
#OneToOne
#JoinColumn(name = "next_station_id")
private Station next;
}
Your DDL would look like this:
create table station
(
id bigint auto_increment,
name varchar(255) null,
previous_station_id bigint null,
next_station_id bigint null,
constraint station_id_uindex
unique (id),
constraint station_station_id_fk
foreign key (previous_station_id) references station (id),
constraint station_station_id_fk_2
foreign key (next_station_id) references station (id)
);
create index station_next_station_id_index
on station (next_station_id);
create index station_previous_station_id_index
on station (previous_station_id);
alter table station
add primary key (id);
Because this table actually has keys back to itself, you would first insert a station with no previous or next set, then you would update the record to set these values. If I'm off base on what those values are, update your question to better describe what you're trying to model for more accurate answers.
Representing it as a many to many relationship could work well in this case. I'm not very familiar with JPA but I think it would look something like this:
#Getter
#Setter
#Entity
public class Station {
#Id
private Long id;
private String name;
#ManyToMany
private Set<Station> adjacentStations;
}
The stations are interconnected, such that when station B is adjacent to station A (i.e. B is in A's set of adjacent stations), then station A is also adjacent to station B (i.e. A is also in B's set of adjacent stations).
In the database, the relationship between stations would then be represented through a mapping table that has two columns (station_id_1, station_id_2) and one row for each connection.
you can use two classes like this :
#Entity
public class TestClass {
#Id
private long id;
#OneToMany(mappedBy="testClass")
private List<MatrixRow> matrix;
}
and one MatrixLine class:
#Entity
public class MatrixRow {
#Id
private long id;
#ManyToOne
private TestClass testClass;
#CollectionOfElements
private List<Value> row;
}
I think when you're doing BFS you need a previous station name, so either you have to create a model class with 100 names and use the #Getter and #Setter annotation or create a List with 100 elements and store the index representation in some other files as an enum in this case you've to change the database schema to stations and array as blob/text.
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.
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.
I am new to both stackoverflow and JPA so I will try to explain this the best i can.
In an entity I want to set the foreign key by giving the int value but also I want to set it by giving an object. Here is some code to explain it better.
#Entity
public class Thread implements Serializable {
#ManyToOne
#JoinColumn(name = "accountId", referencedColumnName = "id", nullable = false)
public Account getAccount() {
return account;
}
#Column(name = "accountId")
#Basic
public int getAccountId() {
return accountId;
}
}
I have tried several ways but the code above is the best example for what I am trying to achieve. I understand that setting insert = false and update = false, in either of the 2 methods, makes this code work as far as compiling and running. But I want to be able to insert the accountId by using an Account object AND by setting the actual int accountId.
The reason for this is because sometimes, in my server, I only have the accountId and sometimes I have the Account object.
I also understand that the best solution is probably to use account.getId() when creating the Thread and setting the accountId. But it would be logically nice in my server to be able to just use the object.
Thanks in advance!
I think you have hit a conceptual problem in your application. You should stick to set the entity and do not use any foreign key values when using JPA. The cause of the problem is that your application is only providing the accountId at some point.
This may be due to different reasons. If this is because the part of the application only providing the accountId is legacy, than I would think it is perfectly fine to have an adapter that converts the accountId into an Account entity and then set that entity. Also not that the adapter could create a JPA proxy so that no actual database access is required at that point. Another reason I can think of, is that the application is loosing information at some point during processing. This may be the case when the application is using the Account in some place and only hands over it's Id to the code in question. Then such code should be refactored to hand over the entity.
In your specific case you are also able to use both, account as entity and the foreign key as attribute with both being insertable and updatable. You just have to make sure, that the accountId attribute value is consistent with the foreign key pointing to the row represented by the account entity. JPA providers should be able to handle this (I know OpenJPA does for example). However you are a bit restricted with this. For example you are only able to read the accountId attribute value, because setting it to a different value would cause an inconsistency between the account entity value.
Do I need to add an index anoatation for the primary key of a hibernate table for decent performance, I assumed that marking a field with #id would mean an index was created
#Id
private String guid;
but I didnt notice anything being created in the ddl that was generated
But if I added an #index annotation
#Id
#org.hibernate.annotations.Index(name = "IDX_GUID")
private String guid;
then I do notice an index being created in the DDL.
So I'm thinking I need to do this for every table, but part of me is thinking is this really neccessary as surely hibernate would want indexes created for the primary key as a starting point ?
You do NOT have to create index explicitly. Instead of seeing DDL statements; I will recommend you to check the final schema created by hibernate. The index is created as part of create table statement.