I'm trying to make a Spring MVC application using Spring Security. I have a problem with JPA - I have a code like this (part of register action):
userDao.addUser(user);
UserDetailsAdapter userDetails = new UserDetailsAdapter(user);
String password = userDetails.getPassword();
Object salt = saltSource.getSalt(userDetails);
user.setPassword(passwordEncoder.encodePassword(password, salt));
After last line there should be a call to update the password field in database (addUser is JPA'a entity manager's persist), but I don't know how to achieve this without making additional selects.
My user table looks like this:
CREATE TABLE `user` (
`user_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(30) NOT NULL,
`password` varchar(64) NOT NULL,
`rank_id` tinyint(4) unsigned DEFAULT '1',
`email` varchar(45) NOT NULL,
`registration_date` datetime NOT NULL,
`last_login_date` datetime DEFAULT NULL,
`admin` bit(1) DEFAULT b'0',
`active` bit(1) DEFAULT b'1',
PRIMARY KEY (`user_id`),
KEY `users_rank_id_pk` (`rank_id`)
) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8$$
The problem is that before calling addUser the user object doesn't have the default values set (rank_id, active, admin, last_login_date). If I call the merge on that object I get those values nullified because they don't get set default.
If I select the User object again using
User test = userDao.findUserById(user.getUserId());
test.setPassword(passwordEncoder.encodePassword(password, salt));
userDao.updateUser(test);
The whole thing works but it makes few more database queries. Is there another way around? I tried setting default values in mapped classes directly, but it didn't work out. I'm using spring's HibernateJpaVendorAdapter and #Transactional annotation on my DAO classes
Regards,
Marcin
Related
I've got two tables - Appointment and User. Appointments can be linked to two different Users - a student and a member of staff. The Appointment table contains two foreign keys: StaffUsername and ExternalID. These reference columns in the User table named Username (the User table's PK) and ExternalID (a UNIQUE index). Here are the table definitions:
CREATE TABLE [dbo].[Appointment]
(
[ID] INT NOT NULL IDENTITY(1,1),
[AppointmentTypeID] INT NOT NULL,
[StartTime] DATETIME NOT NULL,
[EndTime] DATETIME NOT NULL,
[AppointmentSlotID] INT NULL,
[StaffUsername] NVARCHAR(200) NOT NULL,
[ExternalID] NVARCHAR(10) NULL,
[BookedBy] NVARCHAR(200) NOT NULL,
[BookedTimestamp] DATETIME NOT NULL,
[ReminderEmailSentTimestamp] DATETIME NULL,
[CancelledBy] NVARCHAR(200) NULL,
[CancelledTimestamp] DATETIME NULL,
[StudentDidNotAttend] BIT NULL,
[LastModifiedTimestamp] DATETIME NOT NULL,
[LastModifiedBy] NVARCHAR(200) NOT NULL,
CONSTRAINT [PK_Appointment] PRIMARY KEY CLUSTERED ([ID] ASC),
CONSTRAINT [FK_Appointment_AppointmentType] FOREIGN KEY ([AppointmentTypeID]) REFERENCES [dbo].[AppointmentType]([ID]),
CONSTRAINT [FK_Appointment_AppointmentSlot] FOREIGN KEY ([AppointmentSlotID]) REFERENCES [dbo].[AppointmentSlot]([ID]),
CONSTRAINT [FK_Appointment_User_StaffUsername] FOREIGN KEY ([StaffUsername]) REFERENCES [dbo].[User]([Username]),
CONSTRAINT [FK_Appointment_User_ExternalID] FOREIGN KEY ([ExternalID]) REFERENCES [dbo].[User]([ExternalID])
)
CREATE TABLE [dbo].[User]
(
[Username] NVARCHAR(200) NOT NULL,
[FirstName] NVARCHAR(200) NULL,
[LastName] NVARCHAR(200) NULL,
[EmailAddress] NVARCHAR(200) NULL,
[IsStaff] BIT NOT NULL DEFAULT 0,
[ExternalID] NVARCHAR(10) NOT NULL,
[LastLogin] DATETIME NULL,
CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED ([Username] ASC),
CONSTRAINT [UQ_ExternalID] UNIQUE ([ExternalID])
)
Unfortunately, when I use the Update model from database option in the EDMX model designer, it will not pick up the foreign key on the ExternalID columns. It remains looking like this (highlighted in green are the properties relating to the relationship which is modelled correctly, in yellow are the properties which should relate to a second relationship but are being ignored):
I know from experience that the EDMX designer can be quirky at times, especially when detecting changes to objects, so I've tried all the usual tricks. I've checked in Web.config that my connection string is pointing to the correct database. I've deleted the Appointment and User tables in the designer completely and run the Update command again. I've tried that with a save and restart of Visual Studio between deletion and update, too.
To check the relationship is correct in the database I've created a database diagram in SSMS which shows the troublesome relationship correctly:
I've also created a brand new project and added a new Entity Data Model pointing to the same database with the same credentials, just in case the issue was related to the fact that I'm updating an existing model, but no dice. Even in the new project, the relationship isn't detected.
I also tried to create the Navigation Property manually, but as you can see from this screenshot, the foreign key I'd need to select isn't available in the dropdown list:
I don't know if the issue somehow relates to the fact that the ExternalID column isn't the primary key of the User table, or maybe its NVARCHAR(10) data type. I've no idea, to be honest.
Any suggestions as to why this foreign key isn't being detected? And how I can fix it? My project targets .NET Framework 4.6 and I'm using EF6. Obviously I'm using Database First.
In EF6 an Entity only has one key, and so all Navigation Properties must use a Foreign Key that references the same key. EF Core supports Alternate Keys, and supports a Database-First workflow with Reverse Engineering.
I'm developping an API using Springboot and PostgreSQL 12.
I created a user table with hibernate and the entity field that is problematic is the following:
#Column(columnDefinition = "BOOLEAN NOT NULL DEFAULT FALSE")
private Boolean emailVerificationStatus = false;
My table seems to be correctly generated :
create table users (
id int8 generated by default as identity,
email varchar(120) not null,
email_verification_status boolean default false not null,
email_verification_token varchar(255),
encrypted_password varchar(255) not null,
first_name varchar(50) not null,
last_name varchar(50) not null,
user_id varchar(255) not null,
primary key (id)
)
My issue is while running the application I receive an error message :
org.hibernate.PropertyValueException: not-null property references a null or transient value : fr.mycompany.app.io.entity.UserEntity.emailVerificationStatus
By default, when I create the user, it sends a null value. I can solve this issue by setting the value to false in my service layer but it's not very elegant. Also, When I remove the option nullable = false, it works fine but the value is set to null instead of getting the default value. It seems that postgresql doesn't take the default value (false) as value when I try to send a user to my API. I want to force changing null value to false by default.
According to the hibernate documentation you can try to use the following approach:
#Entity
#DynamicInsert
public class YourEntity {
#ColumnDefault("false")
private Boolean emailVerificationStatus;
// ...
}
Please note that the entity above is annotated with the #DynamicInsert annotation so that the INSERT statement does not include any entity attribute that does not contain a value. So, this is allow you to avoid case described in the #MikeOrganek's answer.
You need to solve this in your service layer.
PostgreSQL is following the SQL standard. The default clause value is in effect only if the column is omitted from the column list or if default is specified in the value list. When Hibernate runs the insert, the emailVerificationStatus is explicitly included as null.
Please see: http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt
The section "<insert statement>," "General Rules," 4b, on page 389 defines insert behavior.
I'm trying to store an entity in my postgresql database. This entity has a List in it, so I'd like to use postgresql type TEXT[]. But everytime I'm trying I get a SQL error, I have no idea why.
I don't get the syntax error, really. I'm sure it's a dumb issue but can you help me?
Thank you
I tried some alternatives, creating it directly from h2 console but I always get the same error
The script I use with flyway for creating the table
CREATE TABLE discrimination(
id SERIAL PRIMARY KEY NOT NULL ,
location VARCHAR(255) NOT NULL,
criteria TEXT[] NOT NULL,
domain VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
name_organ VARCHAR(55) NOT NULL,
function_disc VARCHAR(55) NOT NULL
);
my application config for h2 & flyway
h2:
console:
enabled: true
path: /h2
datasource:
url: jdbc:h2:mem:formation-iris;MODE=PostgreSQL
username: test
password: test
driver-class-name: org.h2.Driver
flyway:
locations: classpath:db/migration
enabled: true
And the error I get
Syntax error in SQL statement "CREATE TABLE DISCRIMINATION(
ID SERIAL PRIMARY KEY NOT NULL ,
LOCATION VARCHAR(255) NOT NULL,
CRITERIA TEXT[[*]] NOT NULL,
DOMAIN VARCHAR(255) NOT NULL,
DESCRIPTION TEXT NOT NULL,
NAME_ORGAN VARCHAR(55) NOT NULL,
FUNCTION_DISC VARCHAR(55) NOT NULL
) "; expected "(, FOR, UNSIGNED, INVISIBLE, VISIBLE, NOT, NULL, AS, DEFAULT, GENERATED, ON, NOT, NULL, AUTO_INCREMENT, BIGSERIAL, SERIAL, IDENTITY, NULL_TO_DEFAULT, SEQUENCE, SELECTIVITY, COMMENT, CONSTRAINT, PRIMARY, UNIQUE, NOT, NULL, CHECK, REFERENCES, ,, )"; SQL statement:
From H2 documentation:
Compatibility Modes
For certain features, this database can emulate
the behavior of specific databases. However, only a small subset of
the differences between databases are implemented in this way.
Which means that H2 can emulate certain DB-specific behaviours, but it won't be fully compatible with the selected DB.
That's especially true for SQL syntax.
So, if you want to use arrays in H2, then you should use the H2 syntax ARRAY instead of TEXT[]
Which also means that you will need a separate SQL script for production (PostgreSQL) and for tests (H2). Luckily, flyway supports that. It can load the vendor-specific scripts from different folders. Extend the flyway configuration this way:
spring.flyway.locations=classpath:db/migration/{vendor}
and add the vendor-specific SQL scripts under the /h2 and /postgresql folders respectively.
I know there are dozens of tutorials for how to do this across just as many websites, but this is my first time trying to connect a database table to a UI, so when the version of Spring Boot/MyBatis/Vaadin, for example, are different than the one I'm working with, or they use JPA or JDBC instead of MyBatis, I have no idea how to change it to work with my specific situation.
When people say "it's no different than any other method of doing it with " that doesn't help AT ALL, since, as I stated earlier, I've never done it before. Annotations and classes in the code examples of a tutorial get removed and deprecated with every new version with no clear explanation of how to change it to work with the newer version. I've been researching the various APIs (Spring Boot, Vaadin, MyBatis) for about a month and have a vague understanding of what each one does but not how they work together to achieve the desired result of making a UI for a database. I'm just getting really frustrated at how a single deprecated annotation or class in a tutorial can bring the whole thing crashing down. I know that was long-winded but I just wanted you all to understand where I'm coming from. I'm not particularly attached to any single API, just whatever is easiest.
My current dependencies are:
- Maven : 4.0.0
- Spring Boot: 2.1.2.RELEASE
- Vaadin: 12.0.4
- MyBatis Spring Boot Starter: 2.0.0
I got the starter package from Spring Initializr and added the MyBatis dependency later.
I have a PostgreSQL 10.5 database with 17 tables that will eventually be a UI for a store manager to use for things like looking at received inventory shipments, the hours an employee worked, and other tasks.
My database is named 'store', user: 'store', password: 'store' (if it matters).
For example, these are a few of my tables:
CREATE TABLE IF NOT EXISTS supplier (
id SERIAL,
brand VARCHAR(30) NOT NULL,
phone VARCHAR(15) NOT NULL,
address VARCHAR(100) NOT NULL,
CONSTRAINT pk_supplier PRIMARY KEY (id)
);
CREATE TABLE IF NOT EXISTS shipment (
id SERIAL,
shipdate DATE NOT NULL,
shiptime TIME NOT NULL,
status VARCHAR(10) DEFAULT 'arrived' NOT NULL,
sid INT NOT NULL,
CONSTRAINT pk_shipment PRIMARY KEY (id),
CONSTRAINT fk_shipment_supplier FOREIGN KEY (sid)
REFERENCES supplier(id)
);
CREATE TABLE IF NOT EXISTS shipmentcontains (
shipid INT NOT NULL,
iid INT NOT NULL,
quantity INT NOT NULL,
price DEC(6,2) NOT NULL,
CONSTRAINT pk_shipmentcontains PRIMARY KEY (shipid, iid),
CONSTRAINT fk_shipmentcontains_shipment FOREIGN KEY (shipid)
REFERENCES shipment(id),
CONSTRAINT fk_shipmentcontains_item FOREIGN KEY (iid)
REFERENCES item(id)
);
CREATE TABLE IF NOT EXISTS item (
id SERIAL,
itemtype VARCHAR(25) NOT NULL,
itemsize VARCHAR(10) NOT NULL,
price DEC(5,2) NOT NULL,
sid INT NOT NULL,
CONSTRAINT pk_item PRIMARY KEY (id),
CONSTRAINT fk_item_supplier FOREIGN KEY (sid)
REFERENCES supplier(id)
);
CREATE TABLE IF NOT EXISTS employee (
id SERIAL,
lastname VARCHAR(40) NOT NULL,
firstname VARCHAR(40) NOT NULL,
hourlywage DEC(4,2),
manager BOOLEAN DEFAULT false NOT NULL,
CONSTRAINT pk_employee PRIMARY KEY (id)
);
If someone can give me a code example of how to just get one of those to show in a Grid, I'm sure I can figure out how to do the rest of it. I have the connection details in my application.properties file, but I've seen that with newer versions of MyBatis this isn't needed and annotations such as #Update can be used on the SQL statements to replace that. Also, in plain English, what the heck is a Spring Bean? I hope that wasn't too long..or not long enough.
EDIT: Current version of Vaadin 12 is 12.0.4
You are asking quite a lot, so I will try to touch everything a little and nothing too detailed. I hope this helps you getting the ball rolling.
First off, you will need a java class with all fields that you have in the supplier table, annotated with #Entity. The #Table annotation lets you define the Db table name, and it is not necessary if the table is called the same as the class (case insensitive):
#Entity // javax.persistence
#Table(name = "supplier") // javax.persistence
public class Supplier {
#Id // javax.persistence
private Long id;
private String brand;
private String phone;
private String address;
public Supplier(){
}
// public getters and setters for all fields
// omitted for brevity
}
Now that you have a class for your table, you can start with creating a Vaadin Grid for it. This can be done the easiest with Grid<Supplier> supplierGrid = new Grid<Supplier>(Supplier.class);.
Now to fill the grid with items (suppliers). This is done with supplierGrid.setItems(allSuppliers);. But where do allSuppliers come from you ask?
They can be fetched using a Repository. Because the repository will be annotated with #Repository, its a spring component that can be automatically generated by spring and can be Injected/Autowired (i.e. in your view) using #Inject/#Autowired.
Then you simply call List<Supplier> allSuppliers = supplierRepository.findAll() and you have a list of all suppliers from your DB, that you now can put into the grid with the aforementioned supplierGrid.setItems(allSuppliers);
Any class where an instance of it can be injected by spring is a spring-bean, this includes classes annotated with either #Component, #Serivce or #Repository. Entities like Supplier can not automatically be injected by Spring, unless you define this is your #Configuration class:
/* Do this only if you want to inject a Supplier somewhere. */
#Bean
public Supplier supplier(){
/* define here how a default Supplier should look like */
return new Supplier();
}
I have two tables Employee and Address having one-to-one relationship.
CREATE TABLE EMPLOYEE(
ID BIGINT PRIMARY KEY NOT NULL,
EMP_NAME VARCHAR(50) NOT NULL,
PHONE_ID BIGINT,
DELETED BOOLEAN NOT NULL DEFAULT FALSE,
CONSTRAINT CONSTRAINT1 FOREIGN KEY (PHONE_ID)
REFERENCES PHONE (ID)
)
CREATE TABLE PHONE(
ID BIGINT PRIMARY KEY NOT NULL,
PH_NUMBER VARCHAR(20) NOT NULL,
DELETED BOOLEAN NOT NULL DEFAULT FALSE,
)
I am using Spring Data REST.
Q1. I want to expose a single data rest repository method to update DELETED column for both EMPLOYEE and `PHONE.
Something like below:
TestRepository implements CrudRepository{
#Query(value="update both table query", native=false)
public void updateBoth();
}
Q2. Is doing so even possible using Spring data REST.
PLEASE NOTE: I do not want to use native query, i.e. #Query(value="", native="true")
You have to find the balance between using the framework properly and overusing it.
Spring Data REST is to expose your repositories to HTTP but you can't solve everything with it.
The proper way here is to create a custom Controller and implement the functionality you want with proper transaction management to have the data integrity you need.