Spring Hibernate SQL Server - problem in updating columns - java

I get the below error while not updating the mentioned column at all. I only update two another columns that one of them is used to compute the column "Available".
The column "Available" cannot be modified because it is either a computed column or is the result of a UNION operator.
I also used native query (as below) to be sure that there is no problem during translating hql to sql, but the problem still exists
query = session.createQuery("update Retail.Account SET Balance = Balance + :Amount, RowVersion = RowVersion + 1 WHERE RowVersion = :RowVersion AND Id = :Id")
Here is my model(table) definition:
#Entity
#Table(name = "Account", schema = "Retail")
public class Account {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "Id")
public Integer Id;
// ..... some attributes
#Column(name = "Balance")
public BigDecimal Balance; // the column that I want to update
#Column(name = "Available")
public BigDecimal Available;// the computed column in my error
// ......
#Version
#Column(name = "RowVersion")
public Long RowVersion;
}
My hibernate Configuration is as below:
hibernate.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
hibernate.url=########
hibernate.dialect=org.hibernate.dialect.SQLServer2012Dialect
hibernate.username=**************
hibernate.password=**************
hibernate.hbm2ddl.auto=none
hibernate.setConnectionCachingEnabled=true
hibernate.show_sql=false
hibernate.format_sql=true
Also my table definition in SQL Server is as below:
CREATE TABLE [Retail].[Account](
[Id] [int] IDENTITY(1,1) NOT NULL,
[CustomerId] [int] NOT NULL,
[AccountTypeId] [int] NOT NULL,
[OpeningDate] [datetime] NOT NULL,
[StatusId] [int] NOT NULL,
[Balance] [decimal](18, 2) NOT NULL,
[Credit] [decimal](18, 2) NOT NULL,
[Blocked] [decimal](18, 2) NULL,
[Available] AS (([Balance]+[Credit])-[Blocked]),
[RowVersion] [bigint] NOT NULL,
CONSTRAINT [PK_Account] PRIMARY KEY CLUSTERED
([Id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

My problem was solved when I added #Generated tag as below:
#Generated( value = GenerationTime.ALWAYS )
#Column(name = "Available")
public BigDecimal Available;
But I can't understand why ?!!! (because it does not seem to be mandatory specially when I use native query)

Related

Get only the latest status by MAX function for each Application using WHERE clausule instead of ORDER BY

I would like to obtain the same result of query using WHERE instead of GROUP BY. I have applications and each application has statuses with date. I need to return applications with their latest status date.
My query with ORDER BY: (the result is correct)
select a.guid, MAX(s.request_time) as last_request_time
from public.applications_status s inner join public.applications a on a.guid = s.guid_application
group by a.guid ;
result:
guid |last_request_time |
------------------------------------|-------------------|
330d32d5-2496-4cce-9d11-29e59333766a|2020-07-22 13:06:25|
5b46cda9-b954-4d8b-82cf-f1d83f77b175|2020-07-22 13:07:25|
34071189-ab3d-47ff-9ee1-aca6fa806bc9|2020-08-03 10:45:15|
a8961058-a6ee-4d71-b325-9aca83b22237|2020-08-03 10:45:39|
ff98695f-e1a8-439e-8a6c-7991348b6cd7|2020-07-29 14:38:18|
I try this but it return me only the one application with latest status date:
select a.guid, s.request_time
from public.applications_status s inner join public.applications a on a.guid = s.guid_application
where request_time = (select MAX(applications_status.request_time) from applications_status );
result:
guid |request_time |
------------------------------------|-------------------|
a8961058-a6ee-4d71-b325-9aca83b22237|2020-08-03 10:45:39|
Applications table
CREATE TABLE public.applications (
id bigserial NOT NULL,
guid varchar(40) NOT NULL,
"name" varchar(60) NOT NULL,
latest_status_date timestamp NULL,
latest_status bool NOT NULL,
id_production bigserial NOT NULL,
CONSTRAINT applications_guid_key UNIQUE (guid),
CONSTRAINT applications_pkey PRIMARY KEY (id),
CONSTRAINT uk_gtuqgycxk8ulkir3io2p49yn1 UNIQUE (guid),
CONSTRAINT fkaid_prod FOREIGN KEY (id_production) REFERENCES productions(id) ON DELETE CASCADE
);
Applications_status table
CREATE TABLE public.applications_status (
id bigserial NOT NULL,
status bool NOT NULL,
guid_application varchar(50) NOT NULL,
log varchar(200) NULL,
request_time timestamp NOT NULL,
CONSTRAINT status_pkey PRIMARY KEY (id),
CONSTRAINT fkaguid_application FOREIGN KEY (guid_application) REFERENCES applications(guid) ON UPDATE CASCADE ON DELETE CASCADE
);
Why I need this way? I try to return Applications with their latest status in Spring Boot using #Where annotation in #OneToMany relation in Entity.
#OneToMany(cascade = CascadeType.ALL, mappedBy = "application", fetch = LAZY)
#JsonManagedReference
#Where(clause = "request_time = (SELECT MAX(applications_status.request_time) FROM applications_status )")
#OrderBy("requestTime DESC")
private List<ApplicationStatus> applicationStatuses;
I also try to use #BatchSize(size = 1) but it doesn't work.
The question is tagged both "sql" and "postgres", so this is a Postgres solution.
Use distinct on:
select distinct on (a.guid) a.*, s.*
from public.applications_status s inner join
public.applications a
on a.guid = s.guid_application
order by a.guid, s.request_time desc;
distinct on is a very handy Postgres extension that returns one row (the "first" row) for each group in the parentheses. The particular row is based on the order by.
Through trial and error, I found a solution:
SQL query:
select a.guid, s.request_time
from public.applications_status s inner join public.applications a on a.guid = s.guid_application
where request_time = (select MAX(applications_status.request_time)
from applications_status
where applications_status.guid_application = s.guid_application );
Spring-Boot:
#OneToMany(cascade = CascadeType.ALL, mappedBy = "application", fetch = LAZY)
#JsonManagedReference
#Where(clause = "request_time = (SELECT MAX(applications_status.request_time) FROM applications_status where guid_application = applications_status.guid_application )")
#OrderBy("requestTime DESC")
private List<ApplicationStatus> applicationStatuses;

JPA insert identity id starts at 0 instead of 1?

I have a problem when I am inserting an new entity to a table in SQL Server, even when I set these annotation for my PK entity in java code.
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
The index still started at 0, this case always happens when I drop and re-create table (when identity is set to null) and use insert by JPA repository. (index start at 1 when I used insert statement at Microsoft SSMS).
But when I delete table's data and use DBCC CHECKIDENT ('table', RESEED, 0);, then index of inserted record is 1.
Here is my CREATE statement:
CREATE TABLE [dbo].[tbl_member_info](
[id] [int] IDENTITY(1,1) NOT NULL,
[type] [int] NULL,
[id_card_number] [varchar](20) NULL,
[prefix] [int] NULL,
[first_name] [nvarchar](30) NULL,
[last_name] [nvarchar](30) NULL,
[dob] [datetime] NULL,
[email_personal] [varchar](70) NULL
)
Could anyone help me solve this problem?

JPA inserting foreign key as null

Well, I have tried every thing I could find online and still no luck.
I have 2 classes. TrendsChart & Trends. It's OneToManyMapping. TrendsChart can have multiple Trends.
DB : SQL Server
TrendsChart Table
CREATE TABLE [dbo].[TrendsChart](
[id] [int] NOT NULL IDENTITY,
[Name] [varchar] (50) NULL,
PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
Trends Table
CREATE TABLE [dbo].[Trends](
[TrendsID] [int] NOT NULL IDENTITY,
[ItemName] [varchar] (100) NULL,
[chart_id] [int] NULL
PRIMARY KEY CLUSTERED
(
[TrendsID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Trends] WITH CHECK ADD CONSTRAINT [FK_Trends_TrendsChart] FOREIGN KEY([chart_id])
REFERENCES [dbo].[TrendsChart] ([id]) ON DELETE CASCADE
GO
ALTER TABLE [dbo].[Trends] CHECK CONSTRAINT [FK_Trends_TrendsChart]
GO
TrendsChart Model
#Entity
#Table(name = "TrendsChart")
public class TrendsChart {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
#Column(name="Name")
private String name;
#OneToMany(mappedBy="trendsChart", cascade = CascadeType.ALL)
List<Trends> trends = new ArrayList<>();
Trends Model
#Entity
#Table(name = "Trends")
public class Trends {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="TrendsID")
private int trendsID;
#Column(name="ItemName")
private String itemName;
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name ="chart_id", nullable=false, updatable = false, insertable = true,referencedColumnName = "id")
#OnDelete(action = OnDeleteAction.CASCADE)
private TrendsChart trendsChart;
In the controller I am doing
trendsChartRepository.save(trendsChart);
All the data is being populated correctly except chart_id column which is always null
You have to actively set each trend for the TrandChart:
#Entity
#Table(name = "TrendsChart")
public class TrendsChart {
//...
public void addTrend(Trends trend) {
this.trends.add(trend);
trend.setTrendsChart(this);
}
}
And then for saving:
TrendsChart trendsChart = new TrendsChart();
Trends trend = new Trend();
trendsChart.setTrend(trend);
trendsChartRepository.save(trendsChart);
Here is a good tutorial on this topic:
https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/

Default value in newly created row isn't loaded

I have a MySQL table:
mysql> show create table items\G
*************************** 1. row ***************************
Table: items
Create Table: CREATE TABLE `items` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
1 row in set (0.02 sec)
I create new rows from a Java program via an Entity class:
#Entity
#Table(name = "items", schema = "office_db")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Items.findAll", query = "SELECT i FROM Items i"),
#NamedQuery(name = "Items.findById", query = "SELECT i FROM Items i WHERE i.id = :id"),
#NamedQuery(name = "Items.findByName", query = "SELECT i FROM Items i WHERE i.name = :name"),
#NamedQuery(name = "Items.findByCreated", query = "SELECT i FROM Items i WHERE i.created = :created")
})
public class Items implements Serializable {
#Column(name = "name",length = 128)
private String name;
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Basic(optional = false)
#Column(name = "created")
#Temporal(TemporalType.TIMESTAMP)
private Date created;
#OneToMany(mappedBy = "itemId")
private Collection<Documents> documentsCollection;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "items")
private Collection<ItemAttributes> itemAttributesCollection;
(more stuff ...)
I only set the NAME column, and as expected, the ID and CREATED are set by default:
mysql> select * from items;
+----+--------+---------------------+
| id | name | created |
+----+--------+---------------------+
| 2 | Case 2 | 2017-10-31 13:47:52 |
| 3 | Case 3 | 2017-10-31 13:48:02 |
+----+--------+---------------------+
2 rows in set (0.00 sec)
However, when I reload the table into Java later in the same session:
public List<Items>findItems(){
TypedQuery<Items> query=
em.createNamedQuery("Items.findAll",Items.class);
return query.getResultList();
}
the ID column is loaded correctly, but the CREATED column comes up as blank. CREATED shows up correctly if I relaunch the application (this runs on a glassfish server). My guess is that the reason for this difference is the #GeneratedValue annotation on ID, but I can't apply it on the CREATED field, it seems, or at least not naively. What is the correct way to make the generated timestamp load?
The answer to my conundrum, it appears, is to call em.refresh(), according to this: Invalidating JPA EntityManager session - this is for Hibernate, but it seems to be the same for EclipseLink. I run in to an exception, in my case, but I will post that in another question.

JPA joined column allow every value

I'm testing JPA, in a simple case File/FileVersions tables (Master/Details), with OneToMany relation, I have this problem: in FileVersions table, the field "file_id" (responsable for the relation with File table) accepts every values, not only values from File table.
How can I use the JPA mapping to limit the input in FileVersion.file_id only for values existing in File.id?
My class are File and FileVersion:
FILE CLASS
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="FILE_ID")
private Long id;
#Column(name="NAME", nullable = false, length = 30)
private String name;
//RELATIONS -------------------------------------------
#OneToMany(mappedBy="file", fetch=FetchType.EAGER)
private Collection <FileVersion> fileVersionsList;
//-----------------------------------------------------
FILEVERSION CLASS
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="VERSION_ID")
private Long id;
#Column(name="FILENAME", nullable = false, length = 255)
private String fileName;
#Column(name="NOTES", nullable = false, length = 200)
private String notes;
//RELATIONS -------------------------------------------
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name="FILE_ID", referencedColumnName="FILE_ID", nullable=false)
private File file;
//-----------------------------------------------------
and this is the FILEVERSION TABLE
CREATE TABLE `JPA-Support`.`FILEVERSION` (
`VERSION_ID` bigint(20) NOT NULL AUTO_INCREMENT,
`FILENAME` varchar(255) NOT NULL,
`NOTES` varchar(200) NOT NULL,
`FILE_ID` bigint(20) NOT NULL,
PRIMARY KEY (`VERSION_ID`),
KEY `FK_FILEVERSION_FILE_ID` (`FILE_ID`)
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=latin1
Thanks for help,
I know the SQL constraint to limit the input, but it is possible to create this SQL costraint using some annotation, without writing by hand the SQL in the database?
I'm new on JPA, I was thinking that using #JoinColumn annotation, JPA could create also the costraint...
Thank you again.
At the Java level, you describe and annotate associations between classes - which and you did - and your mapping looks fine.
At the database level, if you want to restrict the possible values in the file_id column to values that are primary keys in the FILE table, you should use a foreign key constraint. To do so, you will need to use InnoDB tables. Something like that:
CREATE TABLE `JPA-Support`.`FILEVERSION` (
`VERSION_ID` bigint(20) NOT NULL AUTO_INCREMENT,
`FILENAME` varchar(255) NOT NULL,
`NOTES` varchar(200) NOT NULL,
`FILE_ID` bigint(20) NOT NULL,
PRIMARY KEY (`VERSION_ID`),
FOREIGN KEY `FK_FILEVERSION_FILE_ID` (`FILE_ID`) REFERENCES FILE(ID)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1
The table FILE also has to use InnoDB. Actually, use InnoDB tables for the tables for which you want to use referential integrity.

Categories