I'm trying to make some field readOnly -> insert and update aka save() should not send that field to DB but the field should be populated with select.
#ReadOnlyProperty from org.springframework.data.annotation.ReadOnlyProperty does not do the trick.
versions: spring-boot: 2.2.0.RC1, spring-data-jdbc: 1.1.0.RELEASE, spring-data-commons: 2.2.0.RELEASE
db: MSSQL
spring-data-jdbc readOnly
Should it work and is there any other way to do it?
NOTE: please don't mix spring-data-jdbc with spring-data-jpa
import java.util.Set;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.ReadOnlyProperty;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.MappedCollection;
public class Organization {
#Id
private Long id;
private String name;
#Column("readOnlyProperty")
#ReadOnlyProperty
private String readOnlyProperty;
#ReadOnlyProperty
#MappedCollection
private Set<Employee> employees;
}
import org.springframework.data.annotation.Id;
public class Employee {
#Id
private Long id;
private String name;
}
#Test
public void insert() {
// insert should not set readOnlyProperty
Organization organization = new Organization("org1", "readOnly");
Employee employee = new Employee("emp1");
Set<Employee> employess = new HashSet<>();
employess.add(employee);
organization.setEmployees(employess);
organizationRepository.save(organization);
}
LOG:
Executing prepared SQL statement [INSERT INTO organization (name, readOnlyProperty) VALUES (?, ?)]
Executing prepared SQL statement [INSERT INTO employee (name, organization) VALUES (?, ?)]
This is a bug.
I created DATAJDBC-431 for it and it will probably fixed in the next service release.
I didn't test, but according to this
The Column annotation and XML element defines insertable and updatable options. These allow for this column, or foreign key field to be omitted from the SQL INSERT or UPDATE statement. These can be used if constraints on the table prevent insert or update operations. They can also be used if multiple attributes map to the same database column, such as with a foreign key field through a ManyToOne and Id or Basic mapping. Setting both insertable and updatable to false, effectively mark the attribute as read-only.
#Column(name="COLUMN_NAME",updatable=false, insertable=false)
private String fieldName;
should make the field read-only.
Related
I'm using hibernate to autogenerate my tables.
Now I want to add a rather complex unique constraint as follows:
UNIQUE KEY person_unique ((coalesce(firstname, 'null')), (coalesce(lastname, 'null')), (coalesce(dob, 'null')));
#Entity
public class Person {
#Id long id;
#Column(nullable=true)
String firstname, lastname;
#Column(nullable=true)
LocalDate dob;
}
I want to use the constraint to prevent inserting duplicate entries also respecting null value fields.
But how can I tell hibernate to create those unique key?
Because a #Table annotation does not accept a native sql statement. The following is not sufficient:
#Table(name = "person",
uniqueConstraints = {#UniqueConstraint(name = "unique_person",
columnNames = {"firstname", "lastname", "dob"}) }
)
Question: how can I get the coalesce() there inside?
We have a deleted column on our table, is it possible to check that every time this table is queried the query has a condition for this column?
Some googling with better keywords (soft-delete) it seems I could do this with #When annotation. not exactly what I was looking but seems close enough.
You can check out #Where annotation.
org.hibernate.annotations.Where
Example:
If there's an Account Entity
#Entity(name = "Account")
#Where( clause = "active = true" )
public static class Account {
#Id
private Long id;
#ManyToOne
private Client client;
#Column(name = "account_type")
#Enumerated(EnumType.STRING)
private AccountType type;
private Double amount;
private Double rate;
private boolean active;
//Getters and setters omitted for brevity
}
and if the following code is used for fetching the accounts.
List<Account> accounts = entityManager.createQuery(
"select a from Account a", Account.class)
.getResultList();
then the following SQL will be generated
SELECT
a.id as id1_0_,
a.active as active2_0_,
a.amount as amount3_0_,
a.client_id as client_i6_0_,
a.rate as rate4_0_,
a.account_type as account_5_0_
FROM
Account a
WHERE ( a.active = true )
Hibernate ORM 5.2.18.Final User Guide
I have three entity classes: PlayerData, IpData, and TwinData.
Both TwinData and IpData refer to PlayerData with a foreign key.
Here is the code:
#DatabaseTable
public class PlayerData {
#DatabaseField(generatedId = true)
private int id;
#DatabaseField
private UUID uuid;
#ForeignCollectionField
private ForeignCollection<IpData> ips;
#ForeignCollectionField
private ForeignCollection<TwinData> twins;
// Getters and setters...
}
#DatabaseTable
public class IpData {
#DatabaseField(generatedId = true)
private int id;
#DatabaseField(foreign = true)
private PlayerData playerData;
#DatabaseField
private Date firstUsage;
#DatabaseField
private String address;
// Getters and setters...
}
#DatabaseTable
public class TwinData {
#DatabaseField(generatedId = true)
private int id;
#DatabaseField(foreign = true)
private PlayerData mainPlayer;
#DatabaseField
private boolean adminNoticed;
#DatabaseField
private UUID uuid;
// Getters and setters...
}
When I try to iterate over PlayerData.ips with streams, I get SQLException [SQLITE_ERROR] SQL error or missing database (no such column: playerData_id). Tables are auto-generated with TableUtils.createIfNotExists().
This is the code that throws that exception:
// playerData is retrieved from Dao<PlayerData, Integer>
playerData.getIps().stream().noneMatch(ip -> ip.getAddress().equals(playerIp))
I just don't really get why it attempts to select playerData_id column.
Should I manually create database tables for TwinData and IpData with playerData_id column? Haven't tried this yet, but I don't think it would help.
When I try to iterate over PlayerData.ips with streams, I get SQLException[SQLITE_ERROR] SQL error or missing database (no such column: playerData_id). Tables are auto-generated with TableUtils.createIfNotExists().
You will get that error if playerData_id is not a field in the table. I'm surprised that TableUtils.createIfNotExists() did not create a field playerData_id in the IpData table. I suspect that the tables existed already and you are not using ORMLite generated schema.
By default when ORMLite sees a foreign = true field, it will store the id of that type in the entity in question. See the foreign fields documentation. If your schema is defining the relationship between the entities differently, for example with another table to define the relationship, then you will need to setup that entity by yourself and query it using the DAO for that entity.
You maybe should look at the foreign object example for more information.
Hope this helps.
I have a model class that is mapped to a postgres database using hibernate. My model class is:
#Entity
#Table(name="USER")
public class User {
#Id
#GeneratedValue
#Column(name="id")
private long id;
#Column(name="username", unique=true)
private String username;
#Column(name="email")
private String email;
#Column(name="created")
private Timestamp created;
public User(long id, String username, String email) {
this.id = id;
this.username = username;
this.email = email;
}
}
I try to retrieve the user with username "adam" using the below query:
tx = session.beginTransaction();
TypedQuery<User> query = session.createQuery("FROM User u WHERE u.username = :username", User.class).setParameter("username", "adam");
user = query.getSingleResult();
I get an exception that says:
org.postgresql.util.PSQLException: ERROR: column user0_.id does not exist
My database from bash shell looks like:
How does hibernate map class attributes to table columns? Does it match based on the #Column(name="username") only or does it also try to match based on datatypes and constraints such as unique/auto-increment?
Solution
In PostgreSQL you have to specify the name of schema like so :
#Table(name="table_name", schema = "myapp")
^^^^^^^^^^^^^^^^
Long Story
you got this error :
org.postgresql.util.PSQLException: ERROR: column user0_.id does not exist
because when you create a database in PostgreSQL, it create a default schema named public, so when you don't specify the name in the Entity then Hibernate will check automatically in the public schema.
Good practices
Don't use Upper letters in the name of database, schema, tables or columns in PostgreSQL. Else you should to escape this names with quotes, and this can cause Syntax errors, so instead you can use :
#Table(name="table_name", schema = "schema_name")
^^^^^^^^^^ ^^^^^^^^^^^
the keyword USER is reserved keyword in PostgreSQL take a look at
+----------+-----------+----------+-----------+---------+
| Key Word |PostgreSQL |SQL:2003 | SQL:1999 | SQL-92 |
+----------+-----------+----------+-----------+---------+
| .... .... .... .... .... |
+----------+-----------+----------+-----------+---------+
| USER | reserved |reserved | reserved | reserved|
+----------+-----------+----------+-----------+---------+
to difference between Dto and Entity its good practice to use Entity in the end of the name of your Entity for example UserEntity
For people getting this exception ,In postgres Whenever you write an Entity Class try to associate it with the correct schema (where your table is present), like this:
#Entity
#Table(name = "user", schema = "users_details")
public class User implements Serializable{
#Column(name = "id")
Long id; //long is not recommended
// Other data
}
As #YCF_L has said Don't use Upper_case letters in a table name or column name otherwise you will get this exception.
This convention becomes more important when their is a scenario where you have to auto generate the tables from entity classes or vice-versa.
Should add schema name on the Entity class.
For this example, when the schema name is public
#Table(name = "user", schema = "public")
See the PostgreSQL Admin view below
See here for more about SpringBoot Java and Postgre SQL connectivity:
https://cmsoftwaretech.wordpress.com/2020/04/25/springboot-thymleaf-using-postgresql/
I obtained using general names like user are making troubles in the app.
I got the same issue as reported here with the following simple entity.
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
#Entity
public class User implements Serializable {
private static final long serialVersionUID = 6843302791607583447L;
#Id
#SequenceGenerator(name = "user_id_seq", sequenceName = "user_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_id_seq")
private Long id;
#Column
private String name;
#Column
private String password;
#Override
public String toString() {
return name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(final String password) {
this.password = password;
}
}
All i did was renaming the entity from User to Sessionxuser (and renaming the datatable from user to sessionxuser to fix this issue.
Schema was still public.
Since pre- or postfix some names like mycoolappuser or usermycoolapp to avoid troubles like this.
Find below a list with reserved keywords and literals preventing using as table, column, and further customized names.
https://www.postgresql.org/docs/8.1/sql-keywords-appendix.html
In this case user is preserved for PostgreSQL, SQL:2003, SQL:1999 and SQL-92.
Try Dropping the table from pg admin console (drop table schema_name.table_name)and make sure your entity class is proper annotated.For example #Table(name = "table_name", schema = "schema_name") on entity class
Use: #GeneratedValue(strategy = GenerationType.IDENTITY)
In your POJO class Id Field. This thing solved my error.
In addition to all previous correct answers, I'd like to say that the positioning of annotations #Column and #GeneratedValue also matters. You want to have both these annotations above either the specific field or getter method, but not separately (not one annotation above the getter and the other above the field). It worked for me at least.
I fixed the issue by altering the column name from nameLikeThis to name_like_this
ALTER TABLE table RENAME nameLikeThis to name_like_this;
Following is my DTO object of customer class. When i make some get query on hibernate i receive 1064 error
#Entity
#Table(name="customer")
public class Customer implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private int id;
#Column(name="code")
private String code;
#Column(name="address")
private String address;
#Column(name="phone1")
private String phone1;
#Column(name="phone2")
private String phone2;
#Column(name="credit_limit")
private BigDecimal creditLimit;
#Column(name="current_credit")
private BigDecimal currentCredit;
#OneToMany(fetch=FetchType.EAGER)
#JoinColumn(name="customer_id")
private Set<Order> orders;}
then i call following method
public List<Order> allOrders(){
return orderDao.findAll();
}
this is the error i receive.
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 'order orders0_ where orders0_.customer_id=5' at line 1
Hibernate:
select
customer0_.id as id1_0_,
customer0_.address as address2_0_,
customer0_.code as code3_0_,
customer0_.credit_limit as credit_l4_0_,
customer0_.current_credit as current_5_0_,
customer0_.phone1 as phone6_0_,
customer0_.phone2 as phone7_0_
from
customer customer0_
Hibernate:
select
orders0_.customer_id as customer2_0_0_,
orders0_.id as id1_1_0_,
orders0_.id as id1_1_1_,
orders0_.customer_id as customer2_1_1_
from
order orders0_ where
orders0_.customer_id=?
Can you please tell what i'm doing wrong here
The problem is your Order entity: order is a reserved word in sql. Best is to change the table name to something else, e.g. #Table(name = "orders") - with an s.
Alternatively, see this answer:
If you are using Hibernate 3.5+, try
hibernate.globally_quoted_identifiers=true to quote all database
identifiers (this is something they added for JPA 2.0, see the secion
2.13 Naming of Database Objects of the spec for the JPA way to activate this if you are using JPA).