How to retrive a generated primary key when a new row is inserted in the oracle database using Spring JDBC? - spring

Below is the code I am using to save a record in the database and then get the generated primary key.
public void save(User user) {
// TODO Auto-generated method stub
Object[] args = { user.getFirstname(), user.getLastname(),
user.getEmail() };
int[] types = { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR };
SqlUpdate su = new SqlUpdate();
su.setJdbcTemplate(getJdbcTemplate());
su.setSql(QUERY_SAVE);
setSqlTypes(su, types);
su.setReturnGeneratedKeys(true);
su.compile();
KeyHolder keyHolder = new GeneratedKeyHolder();
su.update(args, keyHolder);
int id = keyHolder.getKey().intValue();
if (su.isReturnGeneratedKeys()) {
user.setId(id);
} else {
throw new RuntimeException("No key generated for insert statement");
}
}
But its not working, It gives me following error.
The generated key is not of a supported numeric type. Unable to cast [oracle.sql.ROWID] to [java.lang.Number]
The row is being inserted in the database properly. As well I could get the generataed primary key when using MS SQL database but the same code is not working with the ORACLE 11G.
Please help.

As in the comment, oracle rowid's are alpha numerical so can't be cast to an int.
Besides that, you should not use the generated rowid anywhere in your code. This is not the primary key that you defined on the table.
MS SQL has the option to declare a column as a primary key which auto-increments. This is a functionality that does not work in oracle.
What I always do (regardless if the db supports auto-increment) is the following:
select sequenceName.nextval from dual
The value returned by the previous statement is used as the primary key for the insert statement.
insert into something (pk, ...) values (:pk,:.....)
That way we always have the pk after the insert.

Related

Why can JPQLs modifying queries only return void or int?

When i want to modify the database via JPQL i have to mark the query as Transactional and Modiyfing. If i do so, the return type of the method representing the query has to be either void or int(representing the number of edited rows i think). Why are only the two return types allowed? If i do a HTTP-PUT request and update the object with an own JPQL query, i would like to return the updated object again. Whats the best way to do it if the return type of the query has to be void or int? Do i have to do a seperate query/request again which selects the object after it was updated?
EDIT:
Thats how i call the query:
if (inactivityListDTO.getProjectIds().size() > 0) {
projectRepository.updateProjectsIsArchivedByProjectIds(inactivityListDTO.getProjectIds(), inactivityListDTO.getIsArchived());
}
Thats the query:
#Transactional
#Modifying
#Query("UPDATE Project project SET project.isArchived = :isArchived,
project.archivedDate = current_date " +
"WHERE project.id IN :ids")
void updateProjectsIsArchivedByProjectIds(#Param("ids") List<Long> ids, #Param("isArchived") boolean isArchived);
Because it finally boils down to execute a standard UPDATE SQL in the DB , and the UPDATE in standard SQL only returns the number of records being updated and does not return a result set.
And yes , if you need get a record 's value after update , you have to query it again. Alternatively , you should consider using a JPA way to update a record , which first query the object , then update it by changing its state . Something like below (Assume you are using spring #Transactional to manage the transactional boundary):
#Transactional
public void changeEmployeeSalary(Integer employeeId , Integer salary){
Employee employee = entityManager.find(Employee.class , employeeId);
employee.setSalary(salary);
}
In this way , you do not need to query the record again after it is updated and you also do not need to manually write a UPDATE SQL.

Query to check if the record exists in Spring Jdbc Template

I am fairly new to spring ,I am looking to check if a certain email id exists in database or not , using Spring Jdbc Template ,I looked here but could'nt find the proper answer .I am looking something like ,SELECT count(*) from table where email=?
Any help will be appreciated.
You can do something as below if you are using jdbctemplate and new version of spring
private boolean isEmailIdExists(String email) {
String sql = "SELECT count(*) FROM table WHERE email = ?";
int count = jdbcTemplate.queryForObject(sql, new Object[] { email }, Integer.class);
return count > 0;
}
queryForObject method of jdbcTemplate accepts the sql query as the first parameter, second argument is an array of objects for the sql query place holders and the third argument is the expected return value from the sql query.
In this case we only have one place holder and hence I gave the second argument as new Object[] { email } and the result we are expecting is a count which is a Integer and hence I gave it as Integer.class
I kind of got this answer from https://www.mkyong.com/spring/jdbctemplate-queryforint-is-deprecated/
You can go through it if you are interested.
private boolean isEmailIdExists(String email) {
return jdbcTemplate.queryForObject("SELECT EXISTS(SELECT FROM table WHERE email = ?)", Boolean.class, email);
}
http://www.postgresqltutorial.com/postgresql-exists/

MVC - Adding data into linker tables

I have a registration form that allows a school to register. In addition to the obvious login and general details the school can pick from a list of facilities and accreditations that they have.
My data is displayed lovely and binded correctly.
Problem Entering the data into the linker tables does not work it throws an error in both the different ways that I have tried:
Method1:
MembershipUser membershipUser = null;
if (schoolRegisterModel != null)
{
if (null != DB)
{
school SchoolUser = new school();
SchoolUser.username = schoolRegisterModel.UserName;
SchoolUser.email = schoolRegisterModel.Email;
string sPassowrdSalt = Security.Instance().CreateSalt();
SchoolUser.password = Security.Instance().CreatePasswordHash(schoolRegisterModel.Password, sPassowrdSalt);
SchoolUser.password_salt = sPassowrdSalt;
..More data etc..
foreach (var item in schoolRegisterModel.Facilities)
{
if (item.#checked)
{
school_facility sf = new school_facility();
sf.facility_id = item.facility_id;
SchoolUser.school_facility.Add(sf);
}
}
foreach (var item in schoolRegisterModel.Accreditations)
{
if (item.#checked)
{
school_accreditation sa = new school_accreditation();
sa.accreditation_id = item.accreditation_id;
SchoolUser.school_accreditation.Add(sa);
}
}
DB.schools.Add(SchoolUser);
DB.SaveChanges();
Error: {"The INSERT statement conflicted with the FOREIGN KEY constraint \"FK_school_facility_facility\". The conflict occurred in database \"MYDB\", table \"dbo.facility\", column 'facility_id'.\r\nThe statement has been terminated."}
Also - Do I need to manually retrieve the soon to be school ID that will be generated based on this insert. This method avoids entering data directly into the linker tables using only the primary table (school).
Method2:
Same code again apart from trying to update the primary tables (school) accreditation and facilities collection directly, I manually update the linker tables seperately using the latest primary key generated by the previous query, code for this is as follows:
MembershipUser membershipUser = null;
if (schoolRegisterModel != null)
{
if (null != DB)
{
school SchoolUser = new school();
SchoolUser.username = schoolRegisterModel.UserName;
SchoolUser.email = schoolRegisterModel.Email;
string sPassowrdSalt = Security.Instance().CreateSalt();
SchoolUser.password = Security.Instance().CreatePasswordHash(schoolRegisterModel.Password, sPassowrdSalt);
SchoolUser.password_salt = sPassowrdSalt;
..More data etc..
// Linker data for facilities and accreditations.
// Facilities
foreach (var item in schoolRegisterModel.Facilities)
{
if (item.#checked)
{
school_facility sf = new school_facility();
sf.facility_id = item.facility_id;
sf.school_id = SchoolUser.school_id;
DB.school_facility.Add(sf);
}
}
// Accreditations
foreach (var item in schoolRegisterModel.Accreditations)
{
if (item.#checked)
{
school_accreditation sa = new school_accreditation();
sa.accreditation_id = item.accreditation_id;
sa.school_id = SchoolUser.school_id;
DB.school_accreditation.Add(sa);
}
}
m_DB.SaveChanges();
Error: {"The INSERT statement conflicted with the FOREIGN KEY constraint \"FK_school_facility_facility\". The conflict occurred in database \"MYDB\", table \"dbo.facility\", column 'facility_id'.\r\nThe statement has been terminated."}
If you guys have any idea where I am going wrong then please do let me know. There seem to be examples of updating linker table date (which I will need at some point anyway) but can't find an example of my problem...
Thanks in advance.
Looks like I have found the answer:
MVC: The INSERT statement conflicted with the FOREIGN KEY constraint
My data being pulled through was basically not containing the correct foreign key value (0 - which didn't exist) and quite rightly my DB was throwing the error. Sorry for wasting time to whoever read and thanks for your time. I hope this can help somebody else.
Joe

Guid values in Oracle with fluentnhibernate

I've only been using fluent nhibernate a few days and its been going fine until trying to deal with guid values and Oracle. I have read a good few posts on the subject but none that help me solve the problem I am seeing.
I am using Oracle 10g express edition.
I have a simple test table in oracle
CREATE TABLE test (Field RAW(16));
I have a simple class and interface for mapping to the table
public class Test : ITest
{
public virtual Guid Field { get; set; }
}
public interface ITest
{
Guid Field { get; set; }
}
Class map is simple
public class TestMap : ClassMap<Test>
{
public TestMap()
{
Id(x => x.Field);
}
}
I start trying to insert a simple easily recognised guid value
00112233445566778899AABBCCDDEEFF
Heres the code
var test = new Test {Field = new Guid("00112233445566778899AABBCCDDEEFF")};
// test.Field == 00112233445566778899AABBCCDDEEFF here.
session.Save(test);
// after save guid is changed, test.Field == 09a3f4eefebc4cdb8c239f5300edfd82
// this value is different for each run so I pressume nhibernate is assigning
// a value internally.
transaction.Commit();
IQuery query = session.CreateQuery("from Test");
// or
// IQuery query = session.CreateSQLQuery("select * from Test").AddEntity(typeof(Test));
var t in query.List<Test>().Single();
// t.Field == 8ef8a3b10e704e4dae5d9f5300e77098
// this value never changes between runs.
The value actually stored in the database differs each time also, for the run above it was
EEF4A309BCFEDB4C8C239F5300EDFD82
Truly confused....
Any help much appreciated.
EDIT: I always delete data from the table before each test run. Also using ADO directly works no problem.
EDIT: OK, my first problem was that even though I thought I was dropping the data from the table via SQL command line for oracle when I viewed the table via oracle UI it still had data and the first guid was as I should have expected 8ef8a3b10e704e4dae5d9f5300e77098.
Fnhibernate still appears to be altering the guid value on save. it alters it to the value it stores in the database but I'm still not sure why it is doing this or how\if I can control it.
If you intend on assigning the id yourself you will need to use a different id generator than the default which is Guid.comb. You should be using assigned instead. So your mapping would look something like this:
Id(x => x.Field).GeneratedBy.Assigned();
You can read more about id generators in the nhibernate documentation here:
http://www.nhforge.org/doc/nh/en/index.html#mapping-declaration-id-generator

Auditing in Entity Framework

After going through Entity Framework I have a couple of questions on implementing auditing in Entity Framework.
I want to store each column values that is created or updated to a different audit table.
Right now I am calling SaveChanges(false) to save the records in the DB(still the changes in context is not reset). Then get the added | modified records and loop through the GetObjectStateEntries. But don't know how to get the values of the columns where their values are filled by stored proc. ie, createdate, modifieddate etc.
Below is the sample code I am working on it.
// Get the changed entires( ie, records)
IEnumerable<ObjectStateEntry> changes = context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
// Iterate each ObjectStateEntry( for each record in the update/modified collection)
foreach (ObjectStateEntry entry in changes)
{
// Iterate the columns in each record and get thier old and new value respectively
foreach (var columnName in entry.GetModifiedProperties())
{
string oldValue = entry.OriginalValues[columnName].ToString();
string newValue = entry.CurrentValues[columnName].ToString();
// Do Some Auditing by sending entityname, columnname, oldvalue, newvalue
}
}
changes = context.ObjectStateManager.GetObjectStateEntries(EntityState.Added);
foreach (ObjectStateEntry entry in changes)
{
if (entry.IsRelationship) continue;
var columnNames = (from p in entry.EntitySet.ElementType.Members
select p.Name).ToList();
foreach (var columnName in columnNames)
{
string newValue = entry.CurrentValues[columnName].ToString();
// Do Some Auditing by sending entityname, columnname, value
}
}
Here you have two basic options:
Do it at the database level
Do it in the c# code
Doing it at the data base level, means using triggers. In that case there is no difference if you are using enterprise library or another data access technology.
To do it in the C# code you would add a log table to your datamodel, and write the changes to the log table. When you do a save changes both the changes to the data and the information which you wrote to the log table would be saved.
Are you inserting the new record using a stored proc? If not (i.e. you are newing up an object, setting values, inserting on submit and then saving changes the new object id will be automatically loaded into the id property of the object you created. If you are using a stored proc to do the insert then you need to return the ##IDENTITY from the proc as a return value.
EX:
StoreDateContext db = new StoreDataContext(connString);
Product p = new Product();
p.Name = "Hello Kitty Back Scratcher";
p.CategoryId = 5;
db.Products.Add(p);
try
{
db.SaveChanges();
//p.Id is now set
return p.Id;
}
finally
{
db.Dispose;
}

Resources