FastAPI Postgres Cascade Delete - postgresql

I've 2 table, "Book" and "Bookmark".
How to set a cascade on models so when they delete "Book", they will also delete "Bookmark" based on "Book".
Here's the models:
class Book(Base):
__tablename__ ="book"
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
title = Column(String, nullable=False)
description = Column(String, nullable=True)
# relation
r_bookmark = relationship("BookMark", back_populates="r_book")
class BookMark(Base):
__tablename__ ="bookmark"
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
title = Column(String, nullable=False)
description = Column(String, nullable=True)
book_id = Column(Integer, ForeignKey("book.id", ondelete='CASCADE'), nullable=False)
# relation
r_book = relationship("Book", back_populates="r_bookmark", cascade="all,delete")
Please help, thanks.

Thanks to MatsLindh, here's the correct answer:
class Book(Base):
__tablename__ ="book"
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
title = Column(String, nullable=False)
description = Column(String, nullable=True)
# relation
r_bookmark = relationship("BookMark", back_populates="r_book", cascade="all,delete")
class BookMark(Base):
__tablename__ ="bookmark"
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
title = Column(String, nullable=False)
description = Column(String, nullable=True)
book_id = Column(Integer, ForeignKey("book.id"), nullable=False)
# relation
r_book = relationship("Book", back_populates="r_bookmark")
I've add the cascade to the relation on the parent table, NOT the child table.

Related

Spring Boot OneToOne referencedColumnName not applied

I have 2 tables:
CREATE TABLE user_profile (
id VARCHAR(36) PRIMARY KEY,
first_name VARCHAR(255) NOT NULL
);
CREATE TABLE user_phone (
id VARCHAR(36) PRIMARY KEY,
user_id VARCHAR(36),
phone_number VARCHAR(255) NOT NULL,
code VARCHAR(10) NOT NULL,
validated_at DATETIME NULL
);
ALTER TABLE user_phone ADD CONSTRAINT user_phone_FK FOREIGN KEY (user_id) REFERENCES user_profile(id) ON DELETE CASCADE;
so child has ID reference to his parent, because if I will delete parent I want this child to be deleted also.
In User class I have:
#Entity
#Table(name = "user_profile")
public class UserProfile {
...
#OneToOne
#JoinColumn(name = "id", referencedColumnName = "user_id")
private UserPhone phoneNumber;
but then when I run it I see in JPA logs:
from
user_profile userprofil0_
where
userprofil0_.id=?
why it looks there by ID ? should be by user_id field. Or I missunderstood smth there..
thanks!
One day later and finally I found the solution.
Its pretty tricky in spring and not maybe so intuitive.
So - from user_phone table Ive removed additional ID column, and now user_id is there primary key, only 1 change in sql.
Then in spring:
in UserProfile:
#OneToOne(mappedBy = "userProfile", cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private UserPhone userPhone;
and in UserPhone:
#Id
#Column(name = "user_id", nullable = false)
#Type(type = "uuid-char")
private UUID id;
#OneToOne(fetch = FetchType.LAZY)
#MapsId
#JoinColumn(name = "user_id")
private UserProfile userProfile;
#PrimaryKeyJoinColumn annotation indicates that the primary key of the UserProfile is used as the foreign key value for the associated UserPhone.

JPA - WhereJoinTable to foreign key or nested select

I'm trying to add #WhereJoinTable to a property, unfortunately the field is a foreign key, and I need to look up by a property on the other table (called 'type').
I have the following tables:
business:
id
name
...
appuser:
id
email
...
role:
id
type
user_busines_role_permission:
id
appuser_id
business_id
role_id
I can do the following and it works:
#OneToMany()
#JoinTable(name = "user_business_role_permission",
joinColumns = #JoinColumn(name = "business_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "appuser_id", referencedColumnName = "id")
)
#WhereJoinTable(clause = "role_id ='3'")
private Set<AppUser> managedBy;
But I need something like:
#WhereJoinTable(clause = "role.type ='BUSINESS_MANAGER'")
or a nested select so I can select the role id and return it, is this doable?

NamedNativeQuery JPA null primary keys exception

I created a named query that looks like this with a result set mapping:
#NamedNativeQueries({ #NamedNativeQuery(name = "Q_INSTRUMENTS", query = "SELECT i.ID, i.TICKER, i.ISIN, i.SEDOL, i.NAME, i.COUNTRY_ID, i.CONTRACT_SIZE, i.EXPIRY_DATE, i.TYPE_ID FROM INSTRUMENT i INNER JOIN COUNTRY c ON i.COUNTRY_ID = c.ID"
+ " GROUP BY i.ID, i.TICKER, i.ISIN, i.SEDOL, i.COUNTRY_ID, i.CONTRACT_SIZE, i.EXPIRY_DATE, i.TYPE_ID, c.NAME HAVING i.ID = MAX(i.ID) ORDER BY i.NAME, c.NAME ASC", resultClass = Instrument.class, resultSetMapping = "InstrumentMapping") })
#SqlResultSetMapping(name = "InstrumentMapping", entities = { #EntityResult(entityClass = Instrument.class, fields = {
#FieldResult(name = "id", column = "ID"), #FieldResult(name = "ticker", column = "TICKER"),
#FieldResult(name = "sedol", column = "SEDOL"), #FieldResult(name = "isin", column = "ISIN"),
#FieldResult(name = "name", column = "NAME"), #FieldResult(name = "countryId", column = "COUNTRY_ID"),
#FieldResult(name = "contractSize", column = "CONTRACT_SIZE"),
#FieldResult(name = "expiryDate", column = "EXPIRY_DATE"), #FieldResult(name = "typeId", column = "TYPE_ID") }) })
and here is the class that is annotated with this named query
public class Instrument extends ManagedEntityBase{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(length = 10)
private String ticker;
#Column(length = 30)
private String isin;
#Column(length = 10, unique = true, nullable = false)
private String sedol;
#Column(length = 60, nullable = false)
private String name;
#Column(name = "COUNTRY_ID", nullable = false, insertable = false, updatable = false)
private long countryId;
#Column(name = "TYPE_ID", nullable = false, insertable = false, updatable = false)
private byte typeId;
#Column(name = "CONTRACT_SIZE", nullable = false)
private Long contractSize;
#Transient
private String contractSizeString;
#Temporal(TemporalType.DATE)
#Column(name = "EXPIRY_DATE")
private Date expiryDate;
public Instrument() {
// Default constructor
this.contractSize = 1L;
}
ublic Instrument(String sedol, Country country, InstrumentType type) {
this();
this.sedol = sedol;
this.country = country;
this.type = type;
}
/**
* Creates an {#link Instrument} with the given sedol, isin, country and type.
*
* #param sedol instrument sedol
* #param isin instrument isin
* #param country instrument country
* #param name
* #param ticker
* #param type instrument type
*/
public Instrument(String sedol, String isin, Country country, String name, String ticker, InstrumentType type) {
this(sedol, country, type);
this.isin = isin;
this.name = name;
this.ticker = ticker;
}
}
When I try to call the name query:
public static List<Instrument> getInstruments() {
return DatabaseUtility.getEntityManager().createNamedQuery("Q_INSTRUMENTS").getResultList();
}
I get the following error:
[EL Warning]: 2020-01-31 15:55:40.732--UnitOfWork(1788318093)--Exception [EclipseLink-6044] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.QueryException
Exception Description: The primary key read from the row [ArrayRecord(
=> 3000000002285
=> 00577
=> BMG8827A1045
=> BYX9N24
=> 13 HOLDINGS LTD, THE
=> 2
=> 1
=> null
=> 1)] during the execution of the query was detected to be null. Primary keys must not contain null.
Query: ReadAllQuery(name="Q_INSTRUMENTS" referenceClass=Instrument sql="SELECT i.ID, i.TICKER, i.ISIN, i.SEDOL, i.NAME, i.COUNTRY_ID, i.CONTRACT_SIZE, i.EXPIRY_DATE, i.TYPE_ID FROM INSTRUMENT i INNER JOIN COUNTRY c ON i.COUNTRY_ID = c.ID GROUP BY i.ID, i.TICKER, i.ISIN, i.SEDOL, i.COUNTRY_ID, i.CONTRACT_SIZE, i.EXPIRY_DATE, i.TYPE_ID, c.NAME HAVING i.ID = MAX(i.ID) ORDER BY i.NAME, c.NAME ASC")
In the table the expiry_date is indeed null but this does not take part from primary key and by default the nullable value is true for expiry date.
I use EclipseLink with PostreSQL.
I try to migrate the current code from old Sybase to new PostreSQL. With Sybase datasource the error does not happen.
I tried using
<property name="eclipselink.jpa.uppercase-column-names" value="true"/>
to make sure the mapping to the columns is not made wrong, but it didn't fix my issue
It seems that my question had its answer in it. The problem was due to wrong mapping for id. The mapping was done using "id" but the column in database is ID (capitalized). That's why I had null for id.
The solution was to put
<property name="eclipselink.jpa.uppercase-column-names" value="true"/>
in the right persistance-unit.

Select username and role name

As the title indicates I'm trying to select both username and role name by using the following query.
select u.username, r.name
from users u, role r
inner join users_roles ur
on ur.user_id = u.id
where username = ?;
However I'm getting the below error
[2017-04-05 21:34:49] [42P01] ERROR: invalid reference to FROM-clause entry for table "u"
[2017-04-05 21:34:49] Hint: There is an entry for table "u", but it cannot be referenced from this part of the query.
[2017-04-05 21:34:49] Position: 79
My user entity is as follows
#Entity(name = "users") // Postgres doesn't like the table name "user"
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String username;
...
#ManyToMany
#JoinTable(
name = "users_roles",
joinColumns = #JoinColumn(
name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(
name = "role_id", referencedColumnName = "id"))
private Collection<Role> roles;
...
And my role entity is as follows
#Entity
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
#ManyToMany(mappedBy = "roles")
private Collection<User> users;
...
Any clues about what I'm doing wrong?
SELECT users.username, role.name
FROM users
LEFT OUTER JOIN users_roles
ON users.id = users_roles.user_id
LEFT OUTER JOIN role
ON users_roles.role_id = role.id
WHERE username = ?

On Delete Cascade - SQL Alchemy + Postgres

I am trying to setup a delete cascade between a parent (Product) and a child table (ProductVariant), but it is not working. What Am I doing wrong?
from sqlalchemy.orm import relationship, backref
Base = declarative_base()
class Product(Base):
__tablename__ = 'product'
id = Column(Integer, primary_key=True)
title = Column(String)
collection_id = Column(Integer, ForeignKey('collection.id'))
class ProductVariant(Base):
__tablename__ = 'product_variant'
id = Column(Integer, primary_key=True)
title = Column(String)
product_id = Column(Integer, ForeignKey(Product.id, ondelete='CASCADE'), nullable=False)
product = relationship(Product, backref= backref("variants", cascade='delete'))