Can anyone give me short description about his spring class
org.springframework.jdbc.core.BatchPreparedStatementSetter
(JavaDoc API Link)
It's used for bulk insertion of many rows at once.
This code will illustrate how it's used.
Take a good look at importEmployees method, and everything should become clear.
batchUpdate can be done using JdbcTemplate batchUpdate method as follows..
public int[] batchUpdate(final List<Actor> actors) {
int[] updateCounts = jdbcTemplate.batchUpdate("update t_actor set first_name = ?, " +
"last_name = ? where id = ?",
new BatchPreparedStatementSetter() {
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setString(1, actors.get(i).getFirstName());
ps.setString(2, actors.get(i).getLastName());
ps.setLong(3, actors.get(i).getId().longValue());
}
public int getBatchSize() {
return actors.size();
}
});
return updateCounts;
}
Related
I am building a Java application that uses a database and I'm using a DAO design pattern: in my code, all objects classes have an associated DAO class that implements an interface with get, save and update methods.
For instance, for a User object, I will have the following class (ConnectionDB implements the connection to the database):
public class UserDAO implements Dao<User, String> {
private final static String TABLE_NAME = "users";
private final static UserDAO instance = new UserDAO();
public static UserDAO getInstance() {
return instance;
}
private UserDAO() {
}
#Override
public User get(String username) throws SQLException {
String query = "SELECT * FROM " + TABLE_NAME + " WHERE username = ?";
PreparedStatement stmt = ConnectionDB.getInstance().prepareStatement(query);
stmt.setString(1, username);
ResultSet result = stmt.executeQuery();
if (!result.next())
return null;
User user = new User(
result.getInt("id"),
username,
);
stmt.close();
result.close();
return user;
}
/* same thing for save and update */
}
Here is the Dao interface for reference:
public interface Dao<T, S> {
T get(S id) throws SQLException;
ArrayList<T> getAll() throws SQLException;
void save(T t) throws SQLException;
void update(T t) throws SQLException;
}
This way works pretty fine but as I have more and more classes in my application, and a DAO class for each one of them, I have a lot of repetitive code. For instance, the only difference between the get implementation on different objects is the name and type of the primary key and the call to the constructor.
In order to make the code more generic, I tried to implement a fetchItem method in the ConnectionDB class that would be able to query an item from the database:
public <T> HashMap<String, Object> fetchItem(String table_name, String pk, T id) throws SQLException {
String query = "SELECT * FROM " + table_name + " WHERE " + pk + " = ?";
PreparedStatement stmt = prepareStatement(query);
stmt.setObject(1, id);
ResultSet result = stmt.executeQuery();
if (!result.next())
return null;
HashMap<String, Object> values = buildObject(result);
stmt.close();
result.close();
return values;
}
public HashMap<String, Object> buildObject(ResultSet result) throws SQLException {
ResultSetMetaData metadata = result.getMetaData();
int columnCount = metadata.getColumnCount();
HashMap<String, Object> values = new HashMap<>();
for (int i = 1; i <= columnCount; i++) {
values.put(metadata.getColumnName(i), result.getObject(i));
}
return values;
}
With this implementation, I can now replace my first get method in the UserDAO class by the following simplified code:
public User get(String username) throws SQLException {
HashMap<String, Object> values = ConnectionDB.getInstance()
.fetchItem(TABLE_NAME, "username", username);
if (values == null || values.isEmpty())
return null;
return new User(
id,
(String) values.get("String")
);
}
While this new implementation is simpler and allows the get methods to only do what they're supposed to do (here, create a User object with the right parameters from the DB), I find it a bit dangerous as I'll have to make a lot of casts; as I have a lot of Object variables in my code I'm not sure whether it'll be easy to debug the code if something fails in any of these function calls.
So here's my question: which implementation is better, easier to maintain and safer?
Connection DB is a very bad place to define such implementation. It is just a link with a specific database thats all. You violate single responsibility rule. Better to implement base generic class for all DAO's and place common logic there.
Also if you will use Hibernate framework, you will not need to work with query strings and Object variables casts.
I have a class that uses multiple get methods the returns
public int getCurrNum(String Name) {
// query clearances table to return an int that represents the clearance level
String sql = "SELECT number FROM clearances WHERE '" + Name + "' = name;";
//String.format("SELECT number FROM clearances WHERE '%s' = name;",clearanceName);
return jdbcTemplate.queryForObject(sql, Integer.class);
}
which I understand I can use Mockito with a statement like Mockito.when(jdbcTemplate.queryForObject(Mockito.anyString(), Mockito.eq(Integer.class))).thenReturn(1);
But that is not really testing values that are being passed in because I am just telling it to return a value I want. I want to make sure all these methods return what the parameters are specially passing in. We have a user token with user details being sent in. We also have more JdbcTemplates such as
public List<String> getCurr(String currCountry) {
// query alliances table to return a list of alliance tags that countain the
// user's country tag
String sql = "SELECT * FROM user WHERE '" + currCountry + "' = ANY(access_tags) ;";
return jdbcTemplate.query(sql, new RowMapper<String>() {
#Override
public String mapRow(ResultSet rs, int rownumber) throws SQLException {
return rs.getString(1);
}
});
Then the last methods combines everything and has this return statement that uses all these setters from another class.
// return list of appropriately filtered missions
return jdbcTemplate.query(sql, new RowMapper<Mission>() {
#Override
public OtherClass mapRow(ResultSet rs, int rownumber) throws SQLException {
OtherClass m = new OtherClass();
m.setNumber(rs.getInt(1));
m.setName(rs.getString(2));
m.setLastCheckinDate(rs.getString(3));
m.setLocation(rs.getString(4));
m.setCurrLocation(rs.getString(5));
m.setFinalLocation(rs.getString(6));
m.setTags(rs.getString(7));
return m;
}
});
}
But I am not sure how to test these statements. Without using the Mockito.when commands, they always turn out null. We have this information in a database .xml file for the column and rows so it knows what information to grab to fill in the lists. Do I need to mock a mock db or something to test this?
I cannot find a tutorial on this, and I find the documentation scant. How can I do batch insert using ibatis annotations?
public interface MyTableMapper {
#Insert("insert into MyTable(col1) values (#{valueOfCol1})")
void insert(MyRecordClass obj);
}
public class MyTransactionalClass {
#Transactional
public void insert(MyRecordClass obj) {
myTableMapperInst.insert(obj);
}
}
I did this naive implementation (surprisingly without success :-):
public class MyTransactionalClass {
#Transactional(executorType = ExecutorType.BATCH)
public void insert(MyRecordClass obj) {
myTableMapperInst.insert(obj);
}
}
This is without annotation, according to the docs, your way seems correct.
try {
sqlMap.startTransaction()
List list = (Employee) sqlMap.queryForList("getFiredEmployees", null);
sqlMap.startBatch ();
for (int i=0, n=list.size(); i < n; i++) {
sqlMap.delete ("deleteEmployee", list.get(i));
}
sqlMap.executeBatch();
sqlMap.commitTransaction();
} finally {
sqlMap.endTransaction();
}
iBatis annotation insert bulk record you can do like this
#Insert({"<script>",
"insert into user_master (first_name,last_name) values ",
"<foreach collection='userList' item='user' index='index' open='(' separator = '),(' close=')' >#{user.first_name},#{user.last_name}</foreach>",
"</script>"})
int insertUserList(#Param("userList") List<UserNew> userList);
It's work for me and i inserted bulk record in my PostgreSQL database using above single insert.
I am trying to create a "select from insert" within my Spring JdbcDaoSupport class and am having trouble figuring out how to get the data from the select statement and return it.
My EventJdbcTemplate (my DaoImpl):
#Service
public class EventJdbcTemplate extends JdbcDaoSupport implements EventDao {
private static final Logger LOGGER = Logger.getLogger(EventJdbcTemplate.class);
private static final String SQL_INSERT_EVENT = "SELECT EVENT_ID FROM FINAL TABLE " +
"(INSERT INTO EBT10DBB.SB0401T0 (EVENT_NAME, HOST_NAME, USER_ID) " +
"VALUES(?, ?, \'EMP0321\'))";
#Autowired
public EventJdbcTemplate(DataSource pDataSource) {
super.setDataSource(pDataSource);
}
#Override
public Integer createEvent(EventBean pEventBean) { //(Integer id, String eventName)
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Entering create(Event event) of EventJDBCTemplate.");
}
// This SQL works, but is for an INSERT only.
/*this.getJdbcTemplate().query(SQL_INSERT_EVENT, new Object[]{
pEventBean.getEventName(),
pEventBean.getHostName()
});*/
final List eventList = this.getJdbcTemplate().query(SQL_INSERT_EVENT, new Object[]{
pEventBean.getEventName(),
pEventBean.getHostName()
}, new EventRowMapper()
);
Event event = null;
for (int i = 0; i < eventList.size(); i++) {
event = (Event)eventList.get(i);
}
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Exiting create(Event event) of EventJDBCTemplate.");
}
//return statement -- should return either the entire "pEventBean", or
//just the unique key, "EVENT_ID".
return event.getId();
}
EventRowMapper class (Not sure is I'll need this for the select or not):
public class EventRowMapper implements RowMapper<Event> {
#Override
public Event mapRow(ResultSet rs, int rowNum) throws SQLException {
final EventBuilder event = new EventImpl.EventBuilder();
event.setId(rs.getInt("EVENT_ID"));
event.setEventName("EVENT_NAME");
event.setHostName("HOST_NAME");
return event.build();
}
}
So my goal is to return an Integer value that would be the unique key (EVENT_ID) that is created from the INSERT SQL.
You can use SimpleJdbcInsert provided by Spring to get back generated keys, see following documentation provided by Spring section
13.5.2 Retrieving auto-generated keys using SimpleJdbcInsert
Here is the Link
I have a Java class with instance fields (and matching setter methods) that match the column names of a SQL database table. I would like to elegantly fetch a row from the table (into a ResultSet) and map it to an instance of this class.
For example:
I have a "Student" class with instance fields "FNAME", "LNAME", "GRADE" and appropriate getter and setter methods for each.
I also have a SQL table with three columns of the same name.
Right now I am doing something like this:
rs = statement.executeQuery(query);
Student student = new Student();
student.setFNAME(rs.getString("FNAME"));
student.setLNAME(rs.getString("LNAME"));
student.setGRADE(rs.getString("GRADE"));
There has to be a less verbose way of doing this, right? As I add columns this might get really annoying and messy.
I recommend using Spring JDBC. You don't need to use the rest of Spring to use their JDBC library. It will manage connections for you (no more closing Connection, Statement, or ResultSet) and has many conveniences, including row mapping.
We've retrofitted legacy code with Spring JDBC with little trouble.
Here is a presentation (PDF) of an overview of Spring JDBC. It's a few years old but it still works essentially the same, even without letting Spring inject the dependencies.
Spring JDBC Presentation PDF
You can do it generically by doing the following simple methods:
Interface to use as a method pointer:
public interface I_DBtoJavaObjectConvertable<T>
{
public T createFromDB(ResultSet i_rs) throws SQLException;
}
Generic class to handle every mapping from SQL to java Object:
public class DBManager
{
static volatile Connection conn;
//set here a static c'tor to handle the connection to the database
//The General generic method:
public static <T> List<T> GetObjectsFromDB(String i_Query, I_DBtoJavaObjectConvertable i_Converter)
{
List<T> ResList = new ArrayList<>();
try
{
Statement st = conn.createStatement();
for (ResultSet rs = st.executeQuery(i_Query); rs.next();)
{
ResList.add((T) i_Converter.createFromDB(rs));
}
}
catch (SQLException ex)
{
_LOG_ERROR(ex.getMessage());
}
return ResList;
}
}
Now By using Lanbda expression use can easlly convert an sql row to object, by given your convertion method, for example:
public static User FetchUserFromDB(ResultSet i_rs)
{
User userToCreate = null;
try
{
String FirstName = i_rs.getString("FirstName");
String LastName = i_rs.getString("LastName");
String Password = i_rs.getString("Password");
userToCreate = new User(FirstName, LastName, Password);
}
catch (SQLException ex)
{
_LOG_ERROR("Error in fetching user from DB: \n" + ex.getMessage());
}
return userToCreate;
}
And now you can use this this method to bring any Users you want:
public static List<User> GetAllUsersFromDB() throws SQLException
{
String Query = "select * "
+ "from UsersTable";
return DBManager.GetObjectsFromDB(Query, rs -> FetchUserFromDB(rs));
}
Or:
public static List<String> GetAllNamesFromDB() throws SQLException
{
String Query = "select FirstName "
+ "from UsersTable";
return DBManager.GetObjectsFromDB(Query, rs -> rs.getString("FirstName"));
}
You could use an ORM like one of the JPA providers e.g. Hibernate. This lets you set up mappings between your objects and your tables.
If you use JDBC that is how it works. If you want to avoid adding columns like this in Java, you may consider using some ORM frameworks.
A slightly less verbose way would be to give Student a constructor that accepts 3 strings. Then you could do this:
Student student = new Student(rs.getString("FNAME"), rs.getString("LNAME"), rs.getString("GRADE"));
The other way to do it is to use an ORM like Hibernate but Hibernate only becomes worth the massive setup effort for really big projects dealing with lots of tables.
There are many ORM libraries that simplify or eliminate the JDBC drudgery. See Source Forge ORM for some examples. I like my library, sormula, since it can be used with minimal configuration.
If you do not want to use any other framework, you can create standard mapping method and use it after every Result.
public class CurrencyDAO(){
public Currency findById(int id) {
String sql = "SELECT * FROM CCR.CURRENCY WHERE id = ?";
Currency currency = null;
Connection c = null;
try {
c = DBConnection.getConnection();
PreparedStatement ps = c.prepareStatement(sql);
ps.setInt(1, id);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
currency = processRow(rs);
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
} finally {
DBConnection.close(c);
}
return currency;
}
protected Currency processRow(ResultSet rs) throws SQLException {
Currency currency = new Currency();
currency.setId(rs.getInt("id"));
currency.setEUR(rs.getString("EUR"));
currency.setUSD(rs.getString("USD"));
currency.setRate(rs.getString("rate"));
return currency;
}
}
Give q2o a try. It is a JPA based object mapper which helps you with many of the tedious SQL and JDBC ResultSet related tasks, but without all the complexity an ORM framework comes with.
Bind the Student class to its corresponding table:
#Table(name = "STUDENTS")
public class Student (
private String FNAME;
private String LNAME;
private String GRADE;
...
)
Select some students by their grade:
List<Student> students = Q2ObjList.fromClause(Student.class, "GRADE = ?", grade);
Change a student's grade and persist the change to the database:
student.setGRADE(grade);
Q2obj.update(student);
q2o is helpful even when you depend on Spring JDBC:
jdbcTemplate.queryForObject("...", new RowMapper<Student>() {
#Override
public Student mapRow(final ResultSet rs, final int rowNum) throws SQLException {
return Q2Obj.fromResultSet(rs, Student.class);
}
});
It is pretty easy, isn't it? Find more about q2o here.
When you execute a query you can get metadata from the ResultSet. You have access to the columns from this. Here's an example:
#RestController
public class MyController {
#GetMapping("/characters")
public List<Payload> characters() {
List<Payload> results = new ArrayList<>();
try (Connection conn = new Connection()) {
conn.makeConnection();
Statement stmt = conn.createStatement();
ResultSet result = stmt.executeQuery("SELECT * FROM public.hello;");
ResultSetMetaData resultMetaData = result.getMetaData();
Set<String> columns = new HashSet<>();
for (int i = 1; i <= resultMetaData.getColumnCount(); i++) {
columns.add(resultMetaData.getColumnName(i));
}
while (result.next()) {
results.add(new Data(result, columns));
}
} catch (Exception e) {
results.add(new Fail("404", e.getMessage()));
}
return results;
}
}
public class Data implements Payload {
private final Map<String, Object> data = new HashMap<>();
public Data(ResultSet result, Set<String> columns) throws SQLException {
for (String column : columns) {
data.put(column, result.getString(column));
}
}
public Map<String, Object> getData() {
return data;
}
}
Now you can have one class object that parses out the columns and data for any table. You never really care what columns there are. The down side is that all of your info is now stored in a data field. So the payload would look something like:
[
{"data":{"id":"1","name":"Rick Sanchez"}},
{"data":{"id":"2","name":"Morty Smith"}},
{"data":{"id":"3","message":"Summer Smith"}}
]