HIbernate batch insert or update not working in spring boot - mysql

I need to do Bulk inserts(nearly 10000) in my MySQL database. I am using JPA/hibernate and spring boot. I read performing bulk insert/update in hibernate from hibernate documentation, I think my code is not working as it is sequentially inserting the hibernate queries instead of performing them in a batch insert. Below is my code. Am I missing something?
Here is my DataSource configuration.
#Component
public class Datasource {
#Autowired
EnvConfiguration configuration;
private static final Logger logger = LoggerFactory.getLogger(Datasource.class);
#Bean
public DataSource dataSource(){
logger.info("DataSource Bean creation...");
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(configuration.getDBDriver());
dataSource.setUrl("jdbc:mysql://"+configuration.getDBIp()+":"+configuration.getDBPort()+"/"+configuration.getDBName()+"?autoReconnect=true&useSSL=false");
dataSource.setUsername(configuration.getDBUser());
dataSource.setPassword(configuration.getDBPass().trim());
return dataSource;
}
#Bean
public HibernateJpaSessionFactoryBean sessionFactory() {
return new HibernateJpaSessionFactoryBean();
}
}
Code for my Role domain
//Role.java
#Entity
#Table(name = "Role",uniqueConstraints = #UniqueConstraint(
columnNames = { "roleName"}))
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long roleId;
#NotNull
private String roleName;
public Role(){}
public Role(String roleName){
this.roleName = roleName;
}
public Long getRoleId() {
return roleId;
}
public void setRoleId(Long roleId) {
this.roleId = roleId;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
}
Below is my service code. Here I am manually flushing the session. I have added a sleep function in order to find whether insert query executed one by one or they are executing in batch of 10 as happen in JDBC batch.
#Service
public class RoleService{
#Autowired
private SessionFactory factory;
#Autowired
private DataSource source;
private static final Logger logger = LoggerFactory.getLogger(WalletService.class);
public void insertRole(Collection<RegisterWallet> walletMetaCollection){
if(factory==null){
System.out.println("factory is null");
}else{
System.out.println("factory is working");
Session session = factory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Role role=new Role(""+i);
session.persist(role);
System.out.println("this is the role id "+role.getRoleId());
try {
Thread.sleep(1000);
} catch (InterruptedException e){
// TODO Auto-generated catch block
e.printStackTrace();
}
if ( i % 10 == 0 ) { //20, same as the JDBC batch size
//flush a batch of inserts and release memory:
session.flush();
session.clear();
}
}
tx.commit();
session.close();
}
}
}
As per my understanding over batch operation, 10 roles should be inserted at once, decreasing number of jdbc round trips used. But output of the above code is quiet unexpected. It is executing one insert per session.persist(..) call.
//This is log of the above code.
Hibernate: insert into role (active, role_description, role_name) values (?, ?, ?)
this is the role id 14
Hibernate: insert into role (active, role_description, role_name) values (?, ?, ?)
this is the role id 15
Hibernate: insert into role (active, role_description, role_name) values (?, ?, ?)
this is the role id 16
Hibernate: insert into role (active, role_description, role_name) values (?, ?, ?)
this is the role id 17
Hibernate: insert into role (active, role_description, role_name) values (?, ?, ?)
this is the role id 18
Hibernate: insert into role (active, role_description, role_name) values (?, ?, ?)
this is the role id 19
Hibernate: insert into role (active, role_description, role_name) values (?, ?, ?)
this is the role id 20
Hibernate: insert into role (active, role_description, role_name) values (?, ?, ?)
this is the role id 21
Hibernate: insert into role (active, role_description, role_name) values (?, ?, ?)
this is the role id 22
Hibernate: insert into role (active, role_description, role_name) values (?, ?, ?)
this is the role id 23
Hibernate: insert into role (active, role_description, role_name) values (?, ?, ?)
this is the role id 24
Hibernate: insert into role (active, role_description, role_name) values (?, ?, ?)
this is the role id 25
Hibernate: insert into role (active, role_description, role_name) values (?, ?, ?)
this is the role id 26
Hibernate: insert into role (active, role_description, role_name) values (?, ?, ?)
this is the role id 27
Hibernate: insert into role (active, role_description, role_name) values (?, ?, ?)
this is the role id 28
Hibernate: insert into role (active, role_description, role_name) values (?, ?, ?)
this is the role id 29
Hibernate: insert into role (active, role_description, role_name) values (?, ?, ?)
this is the role id 30
Hibernate: insert into role (active, role_description, role_name) values (?, ?, ?)
this is the role id 31
</pre>
-------------------------------------------------------------
Following is my application.properties configuration
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate4.SpringSessionContext
spring.jpa.properties.hibernate.jdbc.batch_size=10
Am I missing something?
Please help.

You are't missing anything.Your output is quite normal.You can get more info on this link: [1]http://www.dineshonjava.com/2012/06/hibernate-batch-processing_10.html

Related

Rollback transaction not working properly

In database manipulation command such as insert, update or delete can sometime throws exception due to invalid data. To protect the integrity of application data we must make sure when we a transaction was failed we must rollback
PreparedStatement ps = null;
Connection conn = null;
try {
conn = DriverManager.getConnection( URL, USERNAME, PASSWORD );
String query = "INSERT INTO tbl1(id, username) " +
"VALUES (?, ?)";
ps = conn.prepareStatement( query );
ps.setString( 1, "javaduke" );
ps.execute();
query = "INSERT INTO tbl2 (id, tbl1_id, " +
"quantity, price) VALUES (?, ?, ?, ?)";
ps = conn.prepareStatement( query );
ps.setInt( 1, id );
ps.setInt( 2, tbl_id );
ps.setInt( 3, 10 );
ps.setDouble( 4, 29.99 );
ps.execute();
}
catch ( SQLException e )
{
conn.rollback()
e.printStackTrace();
}
I guess this is Java.
Right after you get your connection object, turn off autocommit, like so.
conn = DriverManager.getConnection( URL, USERNAME, PASSWORD );
conn.setAutoCommit(false);
Right after your last execute() do this.
conn.commit();
Then the rollback() in your exception handler should do what you expect.
This should extend the scope of your transaction to beyond a single SQL query.

prevent null value insert in parent table using OneToOne ralationship in spring hibernate

I have two table User and ProfilePic having OneToOne relationship bidirectional mapping. When uploading profile picture for User it create new User in database having null value. I need to prevent new user being inserted and want to insert value in ProfilePic table only.
please do not mark as duplicate or down-vote I am new to hibernate.
Here is related Code:
User Class:
#Entity
#Table(name = "user_details")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int userId;
private String firstName;
private String lastName;
private String gender;
#Temporal(TemporalType.DATE)
#DateTimeFormat(pattern = "yyyy-MM-dd")
private Date dob;
private String email;
private String address;
private String username;
private String password;
private String phoneNumber;
private Boolean isActive;
private String role;
#OneToOne(mappedBy = "user")
private ProfilePic profilePic;
#OneToMany(mappedBy = "user")
private List<Login> login = new ArrayList<>();
-----getter setter---
}
PrfilePic Class:
#Entity
public class ProfilePic {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int imageId;
private String image_url;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "userId", nullable = false)
private User user;
---getter setter---
}
Repository class:
#Override
public void saveProfilePic(ProfilePic profilePic) {
HibernateUtil.getSession(sessionFactory).save(profilePic);
}
Controller class:
#RequestMapping(value = "/uploadProfileimage", method = RequestMethod.POST)
public String uploadProfileImage(#ModelAttribute User user, #ModelAttribute ProfilePic profilePic,
#RequestParam("image") CommonsMultipartFile file, Model model) {
String imageUrl = "";
if (!file.getOriginalFilename().isEmpty()) {
imageUrl = ImageUtil.writeImageToFile(file);
profilePic.setImage_url(imageUrl);
profilePic.setUser(user);
pictures.add(profilePic);
userService.saveProfilePic(profilePic);
}
return "redirect:/getProfile?userId=" + user.getUserId();
}
log:
Hibernate: select next_val as id_val from hibernate_sequence for update
Hibernate: update hibernate_sequence set next_val= ? where next_val=?
Hibernate: select next_val as id_val from hibernate_sequence for update
Hibernate: update hibernate_sequence set next_val= ? where next_val=?
Hibernate: insert into user_details (address, dob, email, firstName, gender, isActive, lastName, password, phoneNumber, role, username, userId) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into ProfilePic (image_url, userId, imageId) values (?, ?, ?)
Hibernate: select user0_.userId as userId1_7_0_, user0_.address as address2_7_0_, user0_.dob as dob3_7_0_, user0_.email as email4_7_0_, user0_.firstName as firstNam5_7_0_, user0_.gender as gender6_7_0_, user0_.isActive as isActive7_7_0_, user0_.lastName as lastName8_7_0_, user0_.password as password9_7_0_, user0_.phoneNumber as phoneNu10_7_0_, user0_.role as role11_7_0_, user0_.username as usernam12_7_0_, profilepic1_.imageId as imageId1_6_1_, profilepic1_.image_url as image_ur2_6_1_, profilepic1_.userId as userId3_6_1_ from user_details user0_ left outer join ProfilePic profilepic1_ on user0_.userId=profilepic1_.userId where user0_.userId=?
Expected result : insert data only in ProfilePic table with associated user id
Actual result: data inserted in both table that is User and ProfilePic. New user created having null values in all column.
Help will be highly appreciated. I am stuck in it.
The thing is, it's not a problem to insert data only in the ProfilePic if the user id is null, so no user is associated with this picture.
However if you want to insert data into ProfilePic WITH the user id, you also need to insert the associated user into the user table, otherwise the user id won't be available.
So, for example in MYSQL if you run this query:
INSERT INTO profile_pic (image_id, image_url, user_id) VALUES ('1', "/image", '1');
you will run into an error ( Cannot add or update a child row: a foreign key constraint fails (stackoverflow.profile_pic, CONSTRAINT user_id FOREIGN KEY (user_id) REFERENCES user (user_id)))
So basically there is no user with id 1.
The solution is to use unidirectional mapping, in other words, user will reference profile_pic, but profile_pic won't.
You should change your model into something like:
User class:
#Entity
#Table(name = "user_details")
public class User {
#OneToOne
private ProfilePic profilePic;
// other fields, getters and setters
}
ProfilePic class:
#Entity
public class ProfilePic {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int imageId;
private String image_url;
// getters and setters
}
Then you'll be able to insert only into ProfilePic.
I hope this works for you!

Entity inheritance an error while refreshing with EntityManager

When using entity inheritance hierarchy witch #DiscriminatorColumn storing DiscriminatorType.STRING values in MySQL ENUM. Below code example:
#Entity
#DiscriminatorColumn(name ="account_type", discriminatorType = DiscriminatorType.STRING,
columnDefinition = "ENUM('natural_person', 'firm', 'provider', 'employee', 'administrator', 'moderator', 'user')")
#DiscriminatorValue("user")
public class User implements Serializable { ... }
and inherited entity:
#Entity
#DiscriminatorValue("firm")
public class Firm extends User { ... }
when I create Firm object or removes everything works ok, even when I find it with EntityManager, but if I make EnityManager.refresh(firm) than there is an error complaining about:
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'firm0_.account_type' in 'field list'
UPDATE:
When I changed this columnDefinition of #DescriminatorColumn by deleting it and storing strings "firm", "user", ERROR ALSO OCCURES!
UPDATE 2:
I have a little suggestion that as Firm extends UserAccount this discriminator column account_type should be in user_account table. So error firm0.account_type seems stupid as it search this column in user_account.account_type!
I have another subclass Person and it persists and than saves OK, but Firm only persists, removes but DON'T REFRESH!
UPDATE 3:
Found sql log like this:
Hibernate:
insert
into
user_account
(activation_code, email, last_failed_login, last_logged, login, password, registration_date, account_type)
values
(?, ?, ?, ?, ?, ?, ?, 'firm')
Hibernate:
insert
into
firm
(address_city, address_country, address_office_no, address_building_no, address_state, address_street, address_zip_code, client_id, company_number, name, phone_number, skype_name, statistic_number, vatin, user_id)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate:
select
firm0_.user_id as user_id2_14_0_,
firm0_1_.activation_code as activati3_14_0_,
firm0_1_.email as email4_14_0_,
firm0_1_.last_failed_login as last_fai5_14_0_,
firm0_1_.last_logged as last_log6_14_0_,
firm0_1_.login as login7_14_0_,
firm0_1_.password as password8_14_0_,
firm0_1_.registration_date as registra9_14_0_,
firm0_.address_city as address_1_3_0_,
firm0_.address_country as address_2_3_0_,
firm0_.address_office_no as address_3_3_0_,
firm0_.address_building_no as address_4_3_0_,
firm0_.address_state as address_5_3_0_,
firm0_.address_zip_code as address_7_3_0_,
firm0_.client_id as client_15_3_0_,
firm0_.company_number as company_8_3_0_,
firm0_.name as name9_3_0_,
firm0_.skype_name as skype_n11_3_0_,
firm0_.statistic_number as statist12_3_0_,
firm0_.vatin as vatin13_3_0_,
firm0_2_.corporation_id as corporat5_8_0_,
firm0_2_.description as descript1_8_0_,
firm0_2_.name as name2_8_0_,
firm0_2_.type as type3_8_0_,
firm0_.account_type as account_1_14_0_
from
firm firm0_
inner join
user_account firm0_1_
on firm0_.user_id=firm0_1_.user_id
left outer join
provider firm0_2_
on firm0_.user_id=firm0_2_.provider_id
where
firm0_.user_id=?
Jun 10, 2015 5:37:16 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
WARN: SQL Error: 1054, SQLState: 42S22
Jun 10, 2015 5:37:16 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
ERROR: Unknown column 'firm0_.account_type' in 'field list'
Jun 10, 2015 5:37:16 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop
INFO: HHH000030: Cleaning up connection pool
I'm guessing that when JPA generates the DDL it is using the columnDefinition string as a literal for the column definition. Thus, it should be "account_type ENUM('natural_person', 'firm', 'provider', 'employee', 'administrator', 'moderator', 'user')"
Ok It seems I found solution but it is odd, and I will be testes it later.
private String accountType;
#Column(name = "account_type", insertable = false, updatable = false)
public String getAccountType() {
return accountType;
}
public void setAccountType(String accountType) {
this.accountType = accountType;
}
I have added this code into UserAccount (superclass - root of inheritance hierarchy), where discriminator column is defined... BUT it seems odd behaviour to define explicite discriminator column in entity! I read that it is anit-pattern, not recommanded to add, and moreover to use such discriminator column in code...

Prepared Statement INSERT JDBC MySQL

I am getting an error on doing ' mvn tomcat:run " . The error I am getting is:
exception
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [INSERT INTO ibstechc_dev.device (key, ip_address, type, name) VALUES (?, ?, ?, ?)]; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: 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 'key, ip_address, type, name) VALUES ('abcd', 'abcd', 1234, 'abcd')' at line 1
root cause
org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [INSERT INTO ibstechc_dev.device (key, ip_address, type, name) VALUES (?, ?, ?, ?)]; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: 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 'key, ip_address, type, name) VALUES ('abcd', 'abcd', 1234, 'abcd')' at line 1
org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:237)
org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72
My code segment is:
List<Device> devices = this.jdbcTemplate.query(
"select * from xyz.device a,xyz.user_device b "
+ "where b.user_id = ? and a.device_id = b.device_id and "
+ "a.type = ?",
new Object[]{userId,type},
new RowMapper<Device>() {
public Device mapRow(ResultSet rs, int rowNum) throws SQLException {
Device device = new Device();
device.setId(Long.valueOf(rs.getInt(1)));
device.setKey(rs.getString(2));
device.setIPAddress(rs.getString(3));
device.setType(rs.getInt(4));
device.setName(rs.getString(5));
return device;
}
});
System.out.println("Found for user..." + userId);
return devices;
}
public void create(Device device) {
this.jdbcTemplate.update("INSERT INTO xyz.device (key, ip_address, type, name) VALUES (?, ?, ?, ?)",
new Object[]{device.getKey(), device.getIPAddress(), device.getType(), device.getName()});
}
public void delete(Device device) {
this.jdbcTemplate.update("DELETE FROM xyz.device WHERE device_id = ?", new Object[] {device.getId()});
}
public void update(Device device) {
this.jdbcTemplate.update(
"UPDATE xyz.device SET key = ?, ip_address = ?, type = ?, name =? WHERE device_id = ?", new Object[]{device.getId(),device.getKey(), device.getIPAddress(), device.getType(), device.getName()});
And my Debug.java code is:
public String getNavBarData(){
Device device = new Device();
device.setKey("abcd");
device.setIPAddress("abcd");
device.setType(1234);
device.setName("abcd");
deviceDao.create(device);
return "";
The MySQL table has the same columns as in my code above with NOT NULL for each field. I have used the same code for a different functionality and it works there. Why am I getting this error for this one? Pls. Help.
KEY is a reserved word in Mysql. Therefore you either rename the column (which is better in a long run) or use back ticks around it.
That being said you insert statement should look like this
INSERT INTO xyz.device (`key`, ip_address, type, name) VALUES (?, ?, ?, ?)
^ ^
The same goes to your update statement
UPDATE xyz.device SET `key` = ?, ip_address = ?, type = ?, name =? WHERE device_id = ?
^ ^

Insert a row to MySQL table with jsp

I want to insert rows to mysql database with an jsp code, but I can´t.
Here is my jsp code without the html form:
<%
String login = request.getParameter("login");
String password = request.getParameter("password");
String full_name = request.getParameter("full_name");
String ulevel = request.getParameter("ulevel");
String team_id = request.getParameter("team_id");
Connection connection = null;
PreparedStatement pstatement = null;
Class.forName("com.mysql.jdbc.Driver").newInstance();
int updateQuery = 0;
if(login!=null && password!=null && full_name!=null && ulevel!=null && team_id!=null){
if(login!="" && password!="" && full_name!="" && ulevel!="" && team_id!="") {
try {
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/android","root","root");
String queryString = "INSERT INTO users(user_id,login,password,full_name,ulevel,team_id) VALUES (null, ?, ?, ?, ?, ?)";
pstatement = connection.prepareStatement(queryString);
pstatement.setString(2, login);
pstatement.setString(3, password);
pstatement.setString(4, full_name);
pstatement.setString(5, ulevel);
pstatement.setString(6, team_id);
updateQuery = pstatement.executeUpdate();
if (updateQuery != 0) { %>
When I press SUBMIT, the webpage shows me this:
type Status report message descriptionThe requested resource () is
not available.
p.s.: column user_id is set to autoincrement in mysql table, so I use null.
But I dont know, that´s the right way...
I run the code from netbeans 7.0.1
since ID is auto-incremented, get rid of it from the list, preparedstatement is looking for index 1 on its parameters.
String queryString = "INSERT INTO users(login,password,full_name,ulevel,team_id) VALUES (?, ?, ?, ?, ?)";
pstatement = connection.prepareStatement(queryString);
pstatement.setString(1, login);
pstatement.setString(2, password);
pstatement.setString(3, full_name);
pstatement.setString(4, ulevel);
pstatement.setString(5, team_id);
this one below is not tested
just start you parameter with 1 because there are only 5 parameters to be defined.
String queryString = "INSERT INTO users(user_id,login,password,full_name,ulevel,team_id) VALUES (null, ?, ?, ?, ?, ?)";
pstatement = connection.prepareStatement(queryString);
pstatement.setString(1, login);
pstatement.setString(2, password);
pstatement.setString(3, full_name);
pstatement.setString(4, ulevel);
pstatement.setString(5, team_id);
I found the problem. The error was in html form, at attribute action, where i had an source file, which i never had before. :/
String queryString = "INSERT INTO users(user_id,login,password,full_name,ulevel,team_id) VALUES (?, ?, ?, ?, ?)";
You should not use anything not even null for query!