#ColumnTransformer have no alias when select column - java

I need encrypted and decrypted some column such.
#Entity
#Table(name = "Person")
#Data
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", unique = true, nullable = false)
private Long id;
#Column(name = "NAME", columnDefinition = "varchar")
#ColumnTransformer(read = "pgp_sym_decrypt(decode(NAME, 'hex'), 'key')", write = "encode(pgp_sym_encrypt(?, 'key'), 'hex')")
private String name;
#ManyToOne
#JoinColumn(name = "PARENT_ID")
private Person parent;
}
When I use PersonRepository.findOneByParentId(10L); and see log sql is
select
person0_."id" as id1_99_,
pgp_sym_decrypt(decode(NAME, 'hex'), 'key') as name2_99_
from
"public"."person" person0_
left outer join
"public"."person" person1_
on person0_."parent_id"=person1_."id"
where
person1_."id"=?
This error message
ERROR: column reference "name" is ambiguous
Why #ColumnTransformer don't add table alias of column "name", What should I do ?

I had this issue and adding the forColumn property worked for me.
#ColumnTransformer(
forColumn = "lastName",

Related

Table doesn't get created JPA

Table "books" doesn't get created somehow.
My books table:
#Entity
#Table(name = "books")
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", nullable = false)
private Long id;
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "publisher_id", nullable = false)
private Publisher publisher;
#ManyToMany
#JoinTable(name = "authors_books", joinColumns = #JoinColumn(name = "book_id"),
inverseJoinColumns = #JoinColumn(name = "author_id"))
private Set<Author> authors;
#Column(name = "is_rented", nullable = false, columnDefinition = "DEFAULT FALSE")
private Boolean is_rented;
#Column(name = "isbn", nullable = false, unique = true, length = 300)
private String isbn;
Publishers table:
#Entity
#Table(name = "publishers")
public class Publisher {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", nullable = false)
private Long id;
#OneToMany(mappedBy = "publisher")
private Set<Book> books;
Authors table:
#Entity
#Table(name = "authors")
public class Author {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", nullable = false)
private Long id;
#ManyToMany(mappedBy = "authors")
private Set<Book> books;
application.properties:
spring.jpa.hibernate.ddl-auto=create
spring.jpa.database=mysql
spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/<schema_name>?createDatabaseIfNotExist=true
spring.datasource.username=${MYSQL_USERNAME}
spring.datasource.password=${MYSQL_PASSWORD}
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
Omitted other fields, only reference fields left. I also ovverrid equals and hashcode methods in all entities. And I have empty constructors and full constructors, as well as getters and setters in all of them. Also added #EntityScan to SpringBootApplication file.
I get error:
Table '<schema_name>.authors_books' doesn't exist.
Table '<schema_name>.books' doesn't exist.
But all other tables do exist. Does anybody see what I am missing?
EDIT
Checked database manually. Table "authors_books" DOES exist(despite jpa telling me that it doesn't). Only "books" DOES NOT.
EDIT #2
I added to application.properties:
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
And it shows me:
org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL "
create table books (
id bigint not null,
amount integer not null,
image varchar(300),
is_rented DEFAULT FALSE,
isbn varchar(300) not null,
published_on date,
title varchar(300) not null,
publisher_id bigint not null,
primary key (id)
) engine=InnoDB"
Caused by: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DEFAULT FALSE,
isbn varchar(300) not null,
' at line 5
Looks like problem was with:
#Column(name = "is_rented", nullable = false, columnDefinition = "DEFAULT FALSE")
private Boolean is_rented;
If you configure columnDefinition, then Hibernate does not care about java-based data type you provided.
So the SQL was like:
is_rented DEFAULT FALSE,
which obviously lacks data type.
So I changed it into:
#Column(name = "is_rented", columnDefinition = "BOOLEAN DEFAULT FALSE")
private Boolean is_rented;
And it worked :)

Exclude table using CrudRepository in Java

i have two tables Person and PersonType and there is a relation "ManyToMany" between these tables. During loading my application i am getting all the PersonTypes, but when i create new Person, i have an exception
org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "person_type_person_type_name_key"
Detail: Key (person_type_name)=(TYPE1) already exists.
person_type_person_type_name_key is my table where i should store the relations between Person and PersonType. When i create a new Person i DO NOT want to insert into PersonType table because the person type already exists. What should i do, not to insert into DB ? I am using personService.save(person); which is trying to insert also in person_type table into DB.
#Table(name = "person")
public class Person {
#Id
#Column(name = "id")
#GeneratedValue(generator = "person_id_seq")
#SequenceGenerator(sequenceName = "person_id_seq", name = "person_id_seq", schema = "manager", allocationSize = 1, initialValue = 1)
private Integer id;
#Column(name = "name")
private String name;
#Column(name = "password")
private String password;
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(
name = "person_person_types",
joinColumns = #JoinColumn(name = "person_fk"),
inverseJoinColumns = #JoinColumn(name = "person_type_fk"))
private List<PersonType> personTypes;
}
#Entity
#Table(name = "person_type")
public class PersonType {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "person_type_name", unique=true)
private String personType;
#ManyToMany(mappedBy = "personTypes", cascade = {CascadeType.ALL})
private Set<Person> persons;
}```
Maybe the problem is with inserting the PersonType. Ensure that you put PersonType with the same ID and same name into the DB. Also change CascadeType.ALL to be CascadeType.MERGE

Hibernate two ManyToOne relations on one Table, the first gets Eager and the second LAZY loaded

I have got the following Entities, an item which can has up to two categories, a primary and a secondary.
Both categories are mapped ManyToOne to the category table using a JoinColumnsOrFormulas.
The first one gets fetched EAGER as expected, but the second one does not occur in the SQL statement and gets lazy loaded.
This lazy loading results in a classical n+1 problem.
This is my item entity with the both category entities which should gets joined:
#Entity
#Table(name = "item", schema = "public", catalog = "stackoverflow_question")
#DynamicUpdate
public class Item extends StackOverflowQuestionEntity {
#Id
#Column(name = "id")
protected Long id;
#Column(name = "site")
private String site;
#ManyToOne
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(formula = #JoinFormula(value = "site", referencedColumnName = "site")),
#JoinColumnOrFormula(formula = #JoinFormula(value = "primary_category_id", referencedColumnName = "category_id"))
})
private Category primaryCategory;
#Column(name = "primary_category_id")
private Long primaryCategoryId;
#ManyToOne
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(formula = #JoinFormula(value = "site", referencedColumnName = "site")),
#JoinColumnOrFormula(formula = #JoinFormula(value = "secondary_category_id", referencedColumnName = "category_id"))
})
private Category secondaryCategory;
#Column(name = "secondary_category_id")
private Long secondaryCategoryId;
}
This is the category entity:
#Entity
#Table(name = "category", schema = "public", catalog = "stackoverflow_question")
public class Category extends StackOverflowQuestionEntity {
#Column(name = "category_id")
private Long categoryId;
#Column(name = "name")
private String name;
#Column(name = "site")
private String site;
}
The resulting query contains only the primary category:
SELECT this_.id AS id1_9_9_,
this_.inserted AS inserted2_9_9_,
this_.updated AS updated3_9_9_,
this_.primary_category_id AS formula174_9_,
this_.secondary_category_id AS formula176_9_,
category2_.id AS id1_0_0_,
category2_.inserted AS inserted2_0_0_,
category2_.updated AS updated3_0_0_,
category2_.name AS name7_0_0_
FROM public.item this_
LEFT OUTER JOIN public.category category2_ ON this_.site=category2_.site
AND this_.primary_category_id=category2_.category_id
WHERE True;
Hence the secondary category get joined lazy:
SELECT category0_.id AS id1_0_0_,
category0_.inserted AS inserted2_0_0_,
category0_.updated AS updated3_0_0_,
category0_.name AS name4_0_0_,
category0_.site AS site5_0_0_
FROM public.category category0_
WHERE category0_.site=?
AND category0_.category_id=?;
Why is Hibernate joining the secondary category lazy, the annotations seems the be the same.
The hibernate version I am using is 5.0.10.Final.
This is how the base entity looks like:
#MappedSuperclass
abstract public class StackOverflowQuestionEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, insertable = true, updatable = false, nullable = false)
protected Long id;
#Type(type="LocalDateTime")
#Column(name = "created", nullable = false, insertable = true, updatable = false)
protected LocalDateTime created;
#Type(type="LocalDateTime")
#Column(name = "refreshed", nullable = false, insertable = true, updatable = true)
protected LocalDateTime refreshed;
#PreUpdate
protected void onUpdate() {
refreshed = now();
}
#PrePersist
protected void onCreate() {
created = refreshed = now();
}
}
Here is an example "query", as said I am using hibernate criteria as well as HQL, the problem occurs with both methods.
session
.createCriteria(Item.class)
.add(eq("id", id))
.uniqueResult();
With standard JPA annotations it would look like this (updated):
#ManyToOne
#JoinColumns({
#JoinColumn(name="site", referencedColumnName="site", insertable = false, updatable = false),
#JoinColumn(name="primary_category_id", referencedColumnName="category_id", insertable = false, updatable = false)
})
private Category primaryCategory;
#ManyToOne
#JoinColumns({
#JoinColumn(name="site", referencedColumnName="site", insertable = false, updatable = false),
#JoinColumn(name="secondary_category_id", referencedColumnName="category_id", insertable = false, updatable = false)
})
private Category secondaryCategory;
UPDATE: I found that the second select statement is generated only when you use join by a composite key: Hibernate tries to resolve associations for {site=site, id=null} using TwoPhaseLoad. But if you write
#ManyToOne
#JoinColumn(name="secondary_category_id")
private Category secondaryCategory;
and secondary_category_id is null then the only one select statement will be generated, and the secondaryCategory value will be null. Maybe it will help you somehow. For example, you could add a constraint on site field while building your criteria:
Category c = (Category) session.createCriteria(Category.class)
.add(Restrictions.eq("id", 1L)) // for example
// here you define additional restriction on site field
.createAlias("secondaryCategory", "sc", JoinType.LEFT_OUTER_JOIN, Restrictions.sqlRestriction("this_.site = {alias}.site"))
.uniqueResult();
I did a quick test using your classes, and the following query code (using JPA criteria queries rather than native Hibernate)
CriteriaQuery<Item> cq = em.getCriteriaBuilder().createQuery(Item.class);
EntityGraph<Item> entityGraph = em.createEntityGraph(Item.class);
entityGraph.addSubgraph("primaryCategory", Category.class);
entityGraph.addSubgraph("secondaryCategory", Category.class);
List<Item> items = em.createQuery(cq.select(cq.from(Item.class)))
.setHint("javax.persistence.loadgraph", entityGraph)
.getResultList();
results in the following SQL being generated (formatted for readability):
select item0_.id as id1_1_0_,
category1_.id as id1_0_1_,
category2_.id as id1_0_2_,
item0_.site as site4_1_0_,
item0_.primary_category_id as primary_2_1_0_,
item0_.secondary_category_id as secondar3_1_0_,
category1_.category_id as category2_0_1_,
category1_.name as name3_0_1_,
category1_.site as site4_0_1_,
category2_.category_id as category2_0_2_,
category2_.name as name3_0_2_,
category2_.site as site4_0_2_
from item item0_
left outer join category category1_
on item0_.site=category1_.site
and item0_.secondary_category_id=category1_.category_id
left outer join category category2_
on item0_.site=category2_.site
and item0_.primary_category_id=category2_.category_id
As you can see, both category tables are being joined in the same SELECT
Try the following solution:
#Entity
#Table(name = "item", schema = "public", catalog = "stackoverflow_question")
#DynamicUpdate
public class Item {
#ManyToOne
#JoinColumn(name="site")
private Category primaryCategory;
#ManyToOne
#JoinColumn(name="site")
private Category secondaryCategory;
}
#Entity
#Table(name = "category", schema = "public", catalog = "stackoverflow_question")
public class Category {
#OneToMany(targetEntity=Item.class, mappedBy="primaryCategory", cascade=CascadeType.ALL)
private List<Item> primaryCategoryList;
#OneToMany(targetEntity=Item.class, mappedBy="secondaryCategory", cascade=CascadeType.ALL)
private List<Item> secondaryCategoryList;
}

JPA 2.1 / Hibernate 5.0 Foreignkey name in #JoinColumn is ignored when generating DDL

I am migrating my project to Hibernate 5.0. We use a generated DDL for multiple reasons so the DDL should be exactly the same as it was before. I've got most of it working except for the #ForeignKey name in a #JoinColumn in a #OneToMany relationship
I have a Child entity:
#Entity
#Table(name = "CHILD", indexes = { #Index(columnList = "NAME", name = "IDX_CHILD_NAME") }, uniqueConstraints = { #UniqueConstraint(columnNames = "NAME", name = "UK_CHILD_NAME") })
#SequenceGenerator(allocationSize = 1, name = "CHILD_ID_GENERATOR", sequenceName = "CHILD_ID_SEQ")
public class Child {
#Id
#GeneratedValue(generator = "CHILD_ID_GENERATOR", strategy = SEQUENCE)
#Column(name = "ID")
private Long id;
#Column(name = "NAME")
private String name;
#OneToMany()
#JoinColumn(name = "CHILD_ID", foreignKey = #ForeignKey(name = "FK_CHILD_PET"))
private List<Pet> pets;
}
And a Pet entity:
#Entity
#Table(name = "PET", indexes = { #Index(columnList = "NAME", name = "IDX_PET_NAME") }, uniqueConstraints = { #UniqueConstraint(columnNames = "NAME", name = "UK_PET_NAME") })
#SequenceGenerator(allocationSize = 1, name = "PET_ID_GENERATOR", sequenceName = "PET_ID_SEQ")
public class Pet {
#Id
#GeneratedValue(generator = "PET_ID_GENERATOR", strategy = SEQUENCE)
#Column(name = "ID")
private Long id;
#Column(name = "NAME")
private String name;
}
But when I try to generate a DDL I get the following statement:
alter table PET add constraint FKaxwc707slbsrp6vgxyyydrvcy foreign key (CHILD_ID) references CHILD
When I change the annotations to include the deprecated ForeignKey of Hibernate like this
#OneToMany()
#org.hibernate.annotations.ForeignKey(name = "FK_CHILD_PET")
#JoinColumn(name = "CHILD_ID")
private List<Pet> pets;
I do get the desired output
alter table PET add constraint FK_CHILD_PET foreign key (CHILD_ID) references CHILD
But I want to eliminate that deprecated Hibernate ForeignKey annotations.
Any suggestions?
I also included the above code in a sample project:
https://github.com/vdmc/generate-ddl-example.git

Java Persistence Query Language JOIN

I have two tables :
A(bigint id, ...)
B(bigint id, varchar name, bigint id_A)
and now I want get all rows from A which exists in B (and those rows in B have name eg Andy)
Plase help me create dynamic query
class A
#Entity
#Table(name = "A", schema = "mySchema")
#Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
public class A{
#Id
private Long id;
}
class B
#Entity
#Table(name = "B",
schema = "mySchema",
uniqueConstraints = { #UniqueConstraint(columnNames = {
"some_id", "id_A" }) })
public class B{
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "Seq")
#SequenceGenerator(name = "Seq", sequenceName = "mySchema.mySeq")
private Long id;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_A", nullable = false)
private A a;
#Column(name = "id_A", updatable = false, insertable = false)
private Long IdA;
}
There are several weird parts. e.g. this:
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_A", nullable = false)
private A a;
#Column(name = "id_A", updatable = false, insertable = false)
private Long IdA;
With the #JoinColumn annotation you are telling the JPA provider that it should use the specified column for internal mapping, but with the IdA field, you are trying to manage the column yourself. Which is it going to be?

Categories