I have to insert data into two tables department and employee one by one through java code. Each table has one common column dept_id which is primary key in department table and foreign key in employee table and refers from dept_id column of department table. there is one sequence dept_sequence defined on dept_id in department table.
Now, my current approach to insert data into both these tables is as below,
I use dept_sequence.nextval and dept_sequence.currval respectively for dept_id column to insert data into both these tables.
Map<String, Object> deptData = ImmutableMap.builder()
.put("DEPT_NAME", "TEXTILE")
.put("LOCATION", "PARIS")
.build();
String insertToDeptSql = "INSERT INTO DEPARTMENT(DEPT_ID, DEPT_NAME, LOCATION)
VALUES(dept_sequence.nextval, :DEPT_NAME, :LOCATION)";
namedParameterJdbcTemplate.update(insertToDeptSql , deptData);
Map<String, Object> empData = ImmutableMap.builder()
.put("EMP_NAME", "John")
.put("AGE", 15)
.build();
String insertToEmpSql = "INSERT INTO EMPLOYEE(EMP_ID, DEPT_ID, EMP_NAME, AGE)
VALUES(emp_sequence.nextval, dept_sequence.currval, :EMP_NAME, :AGE)";
namedParameterJdbcTemplate.update(insertToEmpSql, empData);
It works perfectly fine when there is one single transaction at a time. both the tables have correct dept_id values but it breaks in multi-transactional environment. the employee table do not receive same value of dept_sequence which is inserted in department table for one transaction. before inserting record into employee table, dept_sequence value is increased by a different transaction(new record insert into department table) which might be happening in a different system and employee table receive some increased value of sequence.
How we can implement this in such a way that the dept_id value remains same for in both the tables for same transaction.
NOTE: Actual data models are different, employee and department are just for example purpose so don't suggest any changes in the models and primary key, foreign key constraints as I am not allowed to do anything with actual model.
String insertToDeptSql = "INSERT INTO DEPARTMENT(DEPT_ID, EMP_ID, EMP_NAME, DEPT_NAME, LOCATION)
VALUES(dept_sequence.nextval, emp_sequence.currval, :EMP_NAME, :DEPT_NAME, :LOCATION)"
If your primary key constraint is on (dept_id, emp_id), then theoretically, you could run a separate SQL query to get dept_sequence.nextval, then pass the same value into each insert. But that is a very... unconventional use of sequences and I think there are simpler approaches.
I recommend re-evaluating the data model.
Your department table isn't storing departments. It's storing relationships of employees to departments. If employee A and employee B are in the same department, you don't have 2 departments.
What I would suggest you do is put a dept_id column on the employee table and then on the department table, drop the employee-related columns.
You'd end up with something like this:
Map<String, Object> deptData = ImmutableMap.builder()
.put("DEPT_NAME", "MECH")
.put("LOCATION", "PARIS")
.build();
String insertToDeptSql = "INSERT INTO DEPARTMENT(DEPT_ID, DEPT_NAME, LOCATION)
VALUES(dept_sequence.nextval, :DEPT_NAME, :LOCATION)";
jdbcTemplate.update(connection -> {
PreparedStatement ps = connection
.prepareStatement(INSERT_MESSAGE_SQL);
ps.setString(1, message);
return ps;
}, keyHolder);
}
long departmentId = keyHolder.getKey().longValue();
Map<String, Object> empData = ImmutableMap.builder()
.put("EMP_NAME", "John")
.put("AGE", 15)
.put("DEPARTMENT", departmentId)
.build();
String insertToEmpSql = "INSERT INTO EMPLOYEE(EMP_ID, EMP_NAME, AGE, DEPARTMENT)
VALUES(emp_sequence.nextval, :EMP_NAME, :AGE, :DEPARTMENT)";
jdbcTemplate.update(insertToEmpSql, empData);
You can repeat the last section for each employee of a department and reuse the departmentId.
As Brandon has said, your data model isn't great. But to answer the question you're actually asking, basically, "How do I capture the just-inserted id value?" you use the RETURNING INTO clause for your first insert:
INSERT INTO EMPLOYEE(EMP_ID, EMP_NAME, AGE)
VALUES(emp_sequence.nextval, :EMP_NAME, :AGE)
RETURNING EMP_ID INTO :x
In PL/SQL this is pretty trivial. To do it through JDBC, this is captured slightly differently, though getGeneratedKeys(). See Oracle's RETURNING INTO usage in Java (JDBC, Prepared Statement)
Similar situation we handled as below
Get next sequence value of 'dept_sequence' into a variable, for example X
Use X in parent table as well as child table insert statements. Ensure both inserts are under single transaction scope
This will be clean and easy to manage parallel executions.
Related
I have a class of Employees
public class Employee {
int id;
String name;
}
And a corresponding table in postgres with autogenerated PK.
CREATE TABLE employee (
id INT GENERATED BY DEFAULT AS PRIMARY KEY,
name VARCHAR,
employee_group INT
);
I want to use Springs jdbcTemplate.batchUpdate() to insert many employees.
List<Employee> employees = List.of(new Employee("Carlos"), new Employee("Jane"), ...);
String sql = "insert into employee (name, employee_group) values (?, ?)";
int batchSize = 100;
jdbcTemplate.batchUpdate(sql, employees, batchSize, (ps, employee) -> {
ps.setString(1, employee.getName());
ps.setInt(2, 1);
});
Assuming that this table is brand new, and that generated ids start from 1, does batchUpdate (or postgres?) guarantee that inserted rows will get keys following the order of the given list?
I.e, will the resulting table look like this?:
id | name | employee_group
1 Carlos 1
2 Jane 1
...
The reason for my question is that I have a second table
CREATE TABLE employee_info (
employee_id REFERENCES employee(id),
phone VARCHAR
...
);
After I have batch-inserted the employees, I need to batch insert employee-info. For that I need the generated primary keys. If these are generated in the order of my list I know which ID belongs to which employee, by finding all ids with the given employee_group. Unfortunately jdbcTemplate.batchUpdate does not have a way of returning generated keys as far as I can find which would be the optimal solution.
I would like to know how to insert a row that contains a type, I was researching all over the internet but I could not find how to do it.
CREATE TYPE t_name
AS(name char(50),
surname1 char(50),
surname2 char(50));
CREATE TABLE Employees (
id int PRIMARY KEY NOT NULL,
name t_name,
departament int,
salary real);
I would like to insert a row but I don't find how.
Example:
INSERT INTO Employees(id, name, departament, salary)
VALUES(1,t_nom['name1','surname1','surname2']),10,20000.00);
(The above code is not working).
I'm using postgreSQL.
Based on the documentation composite types are created using the ROW() function. Therefore your INSERT statement should be
INSERT INTO Employees (id, name, departament, salary)
VALUES (1, ROW('name1', 'surname1', 'surname2'), 10, 20000.00);
I am trying to access a data in a table using a sub query.
The table 1 contains a foreign key to table 2 , which means i can use that key to access the data in table 2.
My problem is after i return the array list from the below shown method , the arraylist is null.
This is what i have done:
LogEntry logBookDates;
List<LogEntry> bookList =new ArrayList();
try{
PreparedStatement getSummaryStmt=con.prepareStatement("SELECT * FROM LOGENTRYTABLE WHERE DIARYCODE =(SELECT Diarycode FROM LOGBOOKTABLE WHERE STUDENTUSERNAME=? OR SUPERVISORUSERNAME=? AND PROJECT_APPROVE_STATUS=?)");
//the above statment is the sub query which i have created, i get the diary code from log book table and then access the log entry table.
getSummaryStmt.setString(1,userName);
getSummaryStmt.setString(2,userName);
getSummaryStmt.setString(3,"Accepted");
ResultSet rs=getSummaryStmt.executeQuery();
while(rs.next())
{
logBookDates=new LogEntry(rs.getString("STUDENTUSERNAME"),rs.getString("SupervisorUsername"),rs.getString("projecttitle"),rs.getString("projectDescription"),rs.getDate("startDate"),rs.getDate("enddate"),rs.getString("project_approve_status"),rs.getString("diarycode"),rs.getString("projectcode"),rs.getInt("Index"),rs.getString("log_Entry"),rs.getDate("logentry_date"),rs.getString("supervisor_comment"),rs.getString("project_progress"));
bookList.add(logBookDates);
}
}catch(Exception e){}
return bookList;
}
I have not used sub queries before and this is the first time am using them.
What seems to be the problem here ?
Thank you for your time.
Edit : Sample data of logbook table
Sample Data of logentry table
Expected output:
I don't have a screen shot of that but what i need is just to iterate through the arraylist which will be returned from the above method.
Here is the problem, the LOGENTRYTABLE table doesn't contain a column with STUDENTUSERNAME, SupervisorUsername, projecttitle, projectDescription, startDate, etc...
rs.getString("STUDENTUSERNAME"), rs.getString("SupervisorUsername"), etc...
probably, you need JOIN query
"SELECT * FROM LOGENTRYTABLE LT
INNER JOIN LOGBOOKTABLE LB ON LT.DIARYCODE=LB.DIARYCODE
WHERE LT.DIARYCODE =
(SELECT DIARYCODE FROM LOGBOOKTABLE
WHERE (STUDENTUSERNAME=? OR SUPERVISORUSERNAME=?)
AND PROJECT_APPROVE_STATUS=?)"
I have two tables linked by another table like this:
ROLES(RoleID, RoleName)
EMPLOYEES(EmployeeID, E_Name, Address)
ROLE_EMPLOYEES(RoleID#,EmployeeID#).
I want a query that retrieves all from EMPLOYEES and RoleID from ROLES and displays on Java form.
I have tried this but does not work:
rs=st.executeQuery("SELECT EMPLOYEES.*, ROLES.* FROM EMPLOYEES JOIN ROLES");
while(rs.next()){
//MOVE THE CURSOR TO THE FIRST RECORD AND GET DATA
int employeeid=rs.getInt("EmployeeID");
String id=Integer.toString(employeeid);
String name=rs.getString("E_Name");
String addr=rs.getString("Address");
String s = rs.getString("RoleID");
jComboBox1.addItem(s.trim());
//DISPLAY THE FIRST RECORD IN THE TEXT FIELD
txtEmpNumber.setText(id);
txtEmpName.setText(name);
txtEmpAddress.setText(addr);
jComboBox1.setSelectedItem(s);
}
You may try this:
SELECT
EM.*, RL.*
FROM
EMPLOYEES EM
INNER JOIN
ROLE_EMPLOYEES REM ON REM.EmployeeID = EM.EmployeeID
INNER JOIN
ROLES RL ON RL.RoleID = REM.RoleID
Just by writing the keyword JOIN the db-engine does not know in which way it should join the data of the tables; unless you want to retrieve a cartesian product (that's not your case), you need to explicitly set the criteria by using the ON clause.
I have a mySql schema named Contacts that contains 4 tables: Contacts, Phone, Email, and Addresses. The Contacts table contains basic information about a person such as an id number, first name, and last name. The other tables all contain a foreign key that links it to the Contacts table so for example, John Doe in the Contacts table can have multiple phone numbers in the Phones table that are all searchable by using John Doe's id number.
My question is how do I query this schema and return all data for a single (or multiple) users. Can it be done with one SQL statement, or do I need to contact the database for each individual table based on the fact that the amount of results returned will not match for each row returned from the Contacts table. For example, I have some basic search functionality that searches the Contacts table for one or more rows based on search criteria:
public class ContactsListDAO {
//Constants
private static final String SQL_FIND_BY_SEARCH_CRITERIA = "SELECT * FROM Contacts.Contacts WHERE Id LIKE :searchString OR FirstName LIKE :searchString OR LastName LIKE :searchString";
//Variables
private DAOFactory daoFactory;
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
//Constructors
public ContactsListDAO(DAOFactory daoFactory) {
this.daoFactory = daoFactory;
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(daoFactory.getDataSource());
}
public List<Contact> findSearchResults(String searchCriteria) {
Map<String, String> namedParameters = Collections.singletonMap("searchString", searchCriteria);
RowMapper<Contact> mapper = new RowMapper<Contact>() {
#Override
public Contact mapRow(ResultSet resultSet, int row) throws SQLException {
Contact contact = new Contact(
resultSet.getInt("Id"),
resultSet.getString("FirstName"),
resultSet.getString("LastName")
);
return contact;
}
};
return namedParameterJdbcTemplate.query(SQL_FIND_BY_SEARCH_CRITERIA, namedParameters, mapper);
}
}
I am using spring to query and map the results back to a Contact bean. How would I go about modifying this SQL statement and mapping functionality to search the contacts table, get the data for each row and then based on the id of each returned row, also query the phone, email, and address tables and then map those to a List object stored in the bean? The problem is that row 1's id might find 8 phone numbers rows that match the id, but row 2's id might only find 3 phone numbers. How is this going to be stored in a ResultSet? Or will I have to query the Contacts table first and then perform a separate query for each other table (for each row returned from the first) and add that data to the bean case by case? If the first query returns 100 results, and I have to perform a query for each of those on 3 tables, I am looking at 301 trips to the database and back.
Is it possible to use one query and just return 1 result from each of the phone, email, address tables for each result found in the Contacts table? Maybe I can add a primary column or something so it only returns 1 result and then if the user clicks something to request more information about the result it can perform the other queries and gather all the info about that user.
The query i've come up with uses LEFT JOIN to search the tables:
SELECT * FROM Contacts.Contacts LEFT JOIN Contacts.Phone ON Contacts.Id = Phone.ContactId AND Phone.Primary = 1 LEFT JOIN Contacts.Email ON Contacts.Id = Email.ContactId AND Email.Primary = 1 WHERE Contacts.Id LIKE :searchString OR Contacts.FirstName LIKE :searchString OR Contacts.LastName LIKE :searchString AND Contacts.OrganizationId = :organizationId
I created a column in the Phone, Email, and Address database called Primary that contains a boolean so that on my initial query I will only return 1 result for each Contact in the database. So far this is doing what I need. Not sure if it the proper way to go about something like this?