I have a web application in which a user must log into. I am using Hibernate to save objects to the MySQL database. In the database i have triggers which insert new records into audit tables when ever an insert/update/delete is done on an object
DELIMITER $$
CREATE TRIGGER `trg_insert_Book` BEFORE INSERT ON Book FOR EACH ROW
BEGIN
INSERT INTO audit_book (AUDITTYPE,AUDITDATE,bookId, bookName, pages) VALUES ('I',NOW(), NEW.bookId, NEW.bookName, NEW.pages);
END; $$
DELIMITER ;
As you can see from the trigger above i am inserting a new record into the audit_book table when ever an insert is done on the Book table. The insert is done using the following code
public void addNewBook(Book book) {
Session session = sessionFactory.getCurrentSession();
session.save(book);
}
I have a method which returns the user that is currently using the system
#ModelAttribute("username")
public String getPerson(Principal principal) {
return principal.getName();
}
My question is how can i pass this username to the database so that the username is added to the audit table without adding the username to the Book object?
No, you can't do it that way, because the database is not aware of the logic about identifying application users.
You could consider using Hibernate Envers instead of database triggers for auditing purposes. In the linked example you can see how to accomplish your goal in the section 4. Add username information to each revision.
Related
im trying to use the createwithparm method programmatically in adf
to insert new record in the database but it doesnt works
i have db table with 2 generated values with before insert triggers
and i will pass 2 values
and this is my code
OperationBinding operation = ADFUtils.findOperation("CreateWithParams");
Object result = operation.execute();
and from the edit action binding I've referenced the 2 values i want to pass
{pageFlowScope.userBean.investorNumber}
{pageFlowScope.userBean.tempCode}
but nothing is inserted in the database
and there is nothing in the log
Given that you said "nothing is inserted into the database", I have to ask: Do you understand how ADF BC(EO, VO, AM) works? When you submit a page, for example with createwithparam, it updates the EO and VOs in the ADF BC middle tier model, in memory. Nothing is written to the database. You must issue a COMMIT through the enclosing Application Module to get the data written to the db.
This might help.
I think this question is similar to Data base pessimistic locks with Spring data JPA (Hibernate under the hood) but thought I would ask separately as not exactly the same.
I have a multi threaded/node springboot application on top of a mariadb database with a table like
CREATE TABLE job (
id INT PRIMARY KEY AUTO_INCREMENT,
owner VARCHAR(50),
status VARCHAR(10) );
Have a Job domain class as you'd expect.
Have a JobRepository interface which extends CrudRepository<Job,Integer> and a service class.
The application rule is we cannot insert a new job if same owner and set of status values. e.g. If this was old school native sql I would just:
START TRANSACTION;
INSERT INTO job (owner, status)
SELECT 'fred', 'init' FROM DUAL
WHERE NOT EXISTS
( SELECT 1 FROM job
WHERE owner = 'fred' AND status IN ('init', 'running')
);
COMMIT;
But how to I do this in JPA/CrudRepository.
I split into DB operations. Defined a repository method:
#Lock(LockModeType.READ)
long countByOwnerAndStatusIn(String owner, List<String> status);
And then had a service method like:
#Transactional
public Job createJob(Job job) {
if (jobRepository.countByOwnerAndStatusIn(job.getOwner(), Job.PROGRESS_STATUS) == 0) {
// Sleeps just to ensure conflicts
Thread.sleep(1000);
Job newJob = jobRepository.save(job);
Thread.sleep(1000);
return newJob
}
else {
return null;
}
}
But with this I do not get the desired effect.
LockModeType of READ and WRITE allow duplicates to be created.
LockModeType of PESSIMISTIC_READ and PESSIMISTIC_WRITE can result in deadlock errors.
So I guess I am after one of two options:
Is there a way to make get the INSERT...SELECT WHERE NOT EXISTS
into a JPA/CrudRepository method?
Is there a way to get the serivce
method to effectively wrap the count check and the insert in the same
lock?
If there is no way to do either I guess I'll try and get access to the underlying jdbc connection and explicity run a LOCK TABLE statement (or the insert...select, but I don't like the idea of that, keeping it JPA like is probably better).
Hope I have explained myself properly. Thanks in advance for your help.
Searching the web for a solution to create a database for my spring project when it doesn't exist, I found this topic here in stackoverflow:
Simulate CREATE DATABASE IF NOT EXISTS for PostgreSQL?
with this stored procedure to acomplish that:
DO
$do$
BEGIN
IF EXISTS (SELECT 1 FROM pg_database WHERE datname = 'mydb') THEN
RAISE NOTICE 'Database already exists';
ELSE
PERFORM dblink_exec('dbname=' || current_database() -- current db
, $$CREATE DATABASE mydb$$);
END IF;
END
$do$
I want run this procedure from my Java code. My initial idea was include this code as a String atribute in this Service class:
#Service
public class InstallService {
private String query = "";
public boolean create_database(String maquina, String usuario, String senha) {
return false;
}
public boolean create_user(String usuario, String senha, String email) {
return false;
}
}
But I just find out this can't be done. Anyone have any sugestion of how to do that inside this class?
I would recommend calling the stored procedure via whatever method you're currently using to connect to Postgres from Spring. (i.e. Spring JDBC: http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/jdbc.html; or perhaps MyBatis, etc.)
You could define the stored procedure in, say, the template database, and then connect to it via Spring JDBC or whatever method you're employing, with a query of the form:
SELECT stored_proc_name(dbname)
(You would need to have the stored procedure take the DB name as an argument, also.)
Edit:
In response to the comment below, inquiring if this can be done without adding anything to a database, the answer is yes:
You could connect to the template1 DB, run the SELECT query against pg_catalog to see if the DB exists (you'll get an empty set back if it doesn't), and if it doesn't exist, run another query on the connection to the template1 db to create the DB.
Basically, it'd be deconstructing the stored procedure into its constituent parts and calling them directly from Java using JDBC or similar.
my question is how to insert values into DB in sql. i have a USER who can have multiple emails. how can i insert multiple emails into one object? i dont want to add a completely new user object into a new row. i just want to update and append new email into email field of an existing user in db.
i did this:
JPA.em().createQuery
("insert into User (email) select (email) from User where USERNAME=? VALUES (?)")
.setParameter(1, username).setParameter(2, email).executeUpdate();
but it is not working, thanks for help !!
Get the user from the database, concatenate its existing email with the new value, and save the user.
JPA uses entities and generates SQL queries for you. You tyically use queries only to get entities from the database. And those queries are JPQL queries, not SQL queries.
And it looks like your schema is not normalized correctly. One User entity should have many Email entities (OneToMany associations), rather than stuffing all the emails in a single CLOB field of the user. This is how you could search and get individual emails from the database, and get users without all their emails if you don't need them.
I don't know JPA but query should be
JPA.em().createQuery
("insert into User (email) select (email) from User where USERNAME=? VALUES (?)")
.setParameter(1, username).setParameter(2, email).executeUpdate();
should be
JPA.em().createQuery("UPDATE User
SET email =?
WHERE USERNAME=? ")
.setParameter(1,email )
.setParameter(2,username)
.executeUpdate();
I have a table xxx with id (id_xxx int AUTO_INCREMENT ) and name (name_xxx varchar (50)),
When I insert a new row in the table I made:
INSERT INTO xxx VALUES ("name for test");
and the result (int=1) of insertion is returned, then I display in my java interface a message "succseed!", until now it's a very basic and simple operation...
BUT,
when I want to return the inserted id_xxx,I have to do another query to the database:
INSERT INTO xxx VALUES ("name for test");
//after the insert response I made:
SELECT MAX (id_xxx) FROM xxx;
and I display in my java interface "succseed $$$ is your id_xxx "....
the second version can easily cause a serious error during concurrent access to multiple users:
imagine a case when a user1 makes an insert... and then H2DB interrupt operations of this user then executes the insert of user2.
when user1 executes a select max (id_xxx) the H2DB return A FALSE id_xxx...
(I hope that my example is clear otherwise I will schematize this problem).
how to solve this problem?
You should be able to retrieve keys generated by insert query, see 5.1.4 Retrieving Automatically Generated Keys.