JPA inserting foreign key as null - java

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/

Related

Spring Hibernate SQL Server - problem in updating columns

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)

JPA ManyToMany self bidirectional reference by one field

I have one entity User. I need ManyToMany bidirectional relation User-User pairs in one field. How I can do it?
#Entity
#Table(name = "users")
public class User {
#Id
private int id;
private Set<User> pairs;
}
I tried like this:
#ManyToMany
#JoinTable(name = "pairs", joinColumns = {
#JoinColumn(name = "a"),
#JoinColumn(name = "b")
})
private Set<User> pairs;
And got next result: org.hibernate.AnnotationException: A Foreign key refering com.calm.model.entity.User from com.calm.model.entity.User has the wrong number of column. should be 1
Db scmema generated by ddl:
CREATE TABLE `users` (
`id` int(11) NOT NULL,
...
PRIMARY KEY (`id`)
)
And expected pairs table:
CREATE TABLE `pairs` (
`a` int(11) NOT NULL, //user 1
`b` int(11) NOT NULL, //user 2
PRIMARY KEY (`a`,`b`)
)
And expected behavior like:
SELECT b as id2 FROM pairs WHERE a = :id1
UNION
SELECT a as id2 FROM pairs WHERE b = :id1

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.

Join 3 tables using javax.persistence in a OneToMany relationship

The 3 tables are "analyticalgroups", "labinstructions", "observedproperties". Each table has an "id" primary key column.
I'd like to use a 4th table ("analyticalgroups_observedproperties_labinstructions") to store the OneToMany relationship. Ultimately I'd like the output to be structured something like this:
analyticalGroup: {
id: "...",
observedPropertyLabInstructions: [
{observedProperty, labInstruction},
{observedProperty, labInstruction},
{observedProperty, labInstruction},
...etc...
]
}
I've followed some examples online, but can't get this to work. The problem is when I try this I get the following error:
"message" : "Error occurred at repository: PSQLException: ERROR: column observedpr0_.observedpropertyentitylabinstructionentitymap_id does not exist\n Position: 6550",
"errorCode" : "gaia.domain.exceptions.RepositoryException",
Here's the structure for the join table.
CREATE TABLE analyticalgroups_observedproperties_labinstructions
(
analyticalgroupid character varying(36) NOT NULL,
labinstructionid character varying(36) NOT NULL,
observedpropertyid character varying(36) NOT NULL,
CONSTRAINT fk_analyticalgroups_observedproperties_labinstructions_groupid FOREIGN KEY (analyticalgroupid)
REFERENCES analyticalgroups (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE CASCADE,
CONSTRAINT fk_analyticalgroups_observedproperties_labinstructions_labinstr FOREIGN KEY (labinstructionid)
REFERENCES labinstructions (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE CASCADE,
CONSTRAINT fk_analyticalgroups_observedproperties_labinstructions_observed FOREIGN KEY (observedpropertyid)
REFERENCES observedproperties (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE CASCADE
)
#Entity
#Data
public class AnalyticalGroupEntity {
public static final String ENTITY_NAME = "analyticalGroups";
public static final String JOIN_OBSERVEDPROPERTIES_LABINSTRUCTIONS_TABLE_NAME =
ENTITY_NAME +
IDomainEntity.UNDERSCORE +
ObservedPropertyEntity.ENTITY_NAME +
IDomainEntity.UNDERSCORE +
LabInstructionEntity.ENTITY_NAME;
#Id
#Column(name = IDomainEntity.ID_KEY, nullable = false, columnDefinition = IDomainEntity.COLUMN_TYPE_UUID)
private String id;
#OneToMany
#JoinTable(
name = JOIN_OBSERVEDPROPERTIES_LABINSTRUCTIONS_TABLE_NAME,
joinColumns = #JoinColumn(name = LabInstructionEntity.ID_KEY, referencedColumnName = IDomainEntity.ID_KEY, table = "labinstructions")
)
#MapKeyJoinColumn(name = ObservedPropertyEntity.ID_KEY, referencedColumnName = IDomainEntity.ID_KEY, table = "observedproperties")
private Map<ObservedPropertyEntity, LabInstructionEntity> observedPropertyLabInstructions;
}
Hopefully I've laid this all out as clearly as necessary.
Your help is much appreciated. Thanks for reading!
edit Actually... it turns out this doesn't work. It successfully gets the data I want, buuuuut it also deletes every row in the join table whenever I make a GET request *flip table*
So bizarre!
#OneToMany
#JoinTable(
name = JOIN_OBSERVEDPROPERTIES_LABINSTRUCTIONS_TABLE_NAME,
joinColumns = #JoinColumn(name = "analyticalgroupid", referencedColumnName = IDomainEntity.ID_KEY, table = "labinstructions"),
inverseJoinColumns = #JoinColumn(name = LabInstructionEntity.ID_KEY, referencedColumnName = IDomainEntity.ID_KEY, table = "labinstructions")
)
#MapKeyJoinColumn(name = ObservedPropertyEntity.ID_KEY, referencedColumnName = IDomainEntity.ID_KEY, table = "observedproperties")
private Map<ObservedPropertyEntity, LabInstructionEntity> observedPropertyEntityLabInstructionEntityMap;

#JoinTable to use Imported Key instead of primary key

Using JPA/Hibernate 3.6/DB2.
I have got the following exception:
Caused by: org.hibernate.AnnotationException: SecondaryTable JoinColumn cannot reference a non primary key
caused by:
public class XRequest {
#ManyToOne
#JoinTable(
name = "RequestBatch",
joinColumns = #JoinColumn(name = "requestBatchID", referencedColumnName="requestBatchID"),
inverseJoinColumns = #JoinColumn(name = "requestVersionID")
)
private Requestversion requestversion;
}
requestBatchID is not a primary key, but an imported key from the RequestBatch table (and there, it is indeed the primary key). Why does JoinTable have to use a primary key? I mean, didn't I just define that this is a many-to-one association?
Why does it have to be a primary key?
To specify: This is what the tables look like.
XRequest (
requestId int (primary)
requestBatchId int (imported key from RequestBatch)
)
RequestBatch (
requestBatchId int (primary)
requestVersionId int
)
RequestVersion (
requestVersionId int (primary)
)
The wanted outcome is this SQL query to be built for me by Hibernate:
select xr, rv
from XRequest xr
left outer join RequestBatch rb on rb.requestBatchId = xr.requestBatchId
inner join RequestVersion rv on rb.requestVersionId = rv.requestVersionId
If you read the JPA Documentation on #JoinTable, you will see descriptions for joinColumns and inverseJoinColumns mention:
The foreign key columns of the join table which reference the primary
table of the entity...
I guess that is enough to understand the constraints.

Categories