Mapping ResultSet to Pojo Objects - java

Well that's really embarrassing I have made a standard pojo class and its dao class for data retrieval purpose. I am having a difficulty to understand a basic procedure to how to handle a customized query data to Pojo class.
let's say my User class is
public class User{
private int userId;
private String username;
private int addressId;
}
public class Address{
private int addressId;
private String zip;
}
public class UserDAO{
public void getUserDetails(){
String getSql = select u.userId, u.username, a.zipcode from user u, address a where u.addressId = a.addressId;
//no pojo class is now specific to the resultset returned. so we can't map result to pojo object
}
}
now how I should model this with my pojo class as if using String to manage this then concept of object oriented vanishes, also complexity would increase in the future as well. kindly guide!
Update for Further Explanation
We know that we can map same table objects with same pojo class, but when the query is customized and there is a data returned which doesn't map to any specific class then what would be the procedure? i.e. should we make another class? or should we throw that data in a String variable? kindly give some example as well.

For this purpose you can use one of implementation of JPA. But as you want to do it manually I will give you small example.
UPD:
public class User {
private int userId;
private String username;
private Address address; // USE POJO not ID
}
public class Address{
private int addressId;
private String zip;
List<User> users;
}
public User getUserById(Connection con, long userId) {
PreparedStatement stmt;
String query = "select u.user_id, u.user_name, a.id, a.zip from user u, address a where a.address_id = u.id and u.id = ?";
User user = new User();
Address address = new Address;
try {
stmt = con.prepareStatement(query);
stmt.setLong(1, userId);
ResultSet rs = stmt.executeQuery();
address.setId(rs.getInt("id"));
address.setZip(rs.getString("zip");
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("user_name"));
user.setAddressId(rs.getInt("address_id"));
user.setAddress(address); // look here
} catch (SQLException e) {
if (con != null) {
try {
System.err.print("Transaction is being rolled back");
con.rollback();
} catch (SQLException excep) {
}
}
} finally {
if (stmt != null) {
stmt.close();
}
}
return user;
}
You shouldn't do new POJO for that query, you should write normal query. And remember - your object model is main, tables in DB is just a way to save data of your application.

We know that we can map same table objects with same pojo class, but when the query is customized and there is a data returned which doesn't map to any specific class then what would be the procedure? i.e. should we make another class?
JPA dynamic instantiation allows you to define a query with a POJO whose constructor specifies only the fields and types you want from the database.
This will perform a JPA selection which will return a List.
If you need to change the query later and the columns are unchanged, your POJO will still work.
If you change the columns, then also change the POJO accordingly.
NOTE:
You must specify fully qualified package and constructor arguments.
Type User must be a JPA-mapped or JPA-annotated entity class.
The entityManager is in JPA EntityManagerFactory.
TypedQuery<User> q;
String sql = "select new com.stuff.User(
int u.userId, String u.username, String a.zipcode)
from User u, Address a where u.addressId = a.addressId";
List<User> list = entityManager.createQuery(sql).getResultList();
for(User u : list) {
doStuff(u);
}
Dynamic instantiation is also handy when you want to select specified columns, but avoid those columns with large data, such as BLOB types.
For example, maybe you want a list of proxy POJO's which represent the fully populated thing, but are themselves not fully populated.
You present the proxy list, and when the user selects one, then you do another query to get the fully populated object.
Your mileage may vary.

There's many ORM frameworks that can do this including Hibernate, myBatis, JPA and spring-JDBC
spring-jdbc and myBatis give you granular control over the SQL whereas with JPA and Hibernate you are usually abstracted away from the SQL.
I suggest you do some reading and figure out which one you like before rolling your own solution.

Your question:
We know that we can map same table objects with same pojo class,
but when the query is customized and there is a data returned
which doesn't map to any specific class then what would be the procedure?
If you have 100 kinds of SQL which returns different combination of columns, could it be to create 100 different POJOs? The answer is "NO, stop using POJO".
This library qood is designed to solve this problem, you can try it.

Related

How to use inner join with Spring MVC

Hello I wrote inner join between the foreign key and the linked tables, but I can not print the data in the 2nd table. I get an error in Javascript, probably the data in the 2nd table is not coming there. I need user_name in User table but I can not access.
BookController
#Autowired
private BooksService booksService;
#Autowired
private BookCommentsService bookCommentsService;
#RequestMapping(value = "/bookdetail")
public ModelAndView showBookDetailForm(#RequestParam long book_id)
{
ModelAndView mav= new ModelAndView("bookdetail");
List<Books> book=booksService.bookGetWithId(book_id);
List<BookComments> listBookComments= bookCommentsService.listAllDetailComments(book_id);
mav.addObject("listBook",book);
mav.addObject("listBookComments", listBookComments);
return mav;
}
BookCommentsService
#Service
public class BookCommentsService {
#Autowired
private BookCommentsRepository repository;
public List<BookComments> listAllDetailComments(long book_id)
{
return repository.listAllDetailComments(book_id);
}
}
BookCommentsRepository
public interface BookCommentsRepository extends CrudRepository<BookComments, Long> {
#Query(value = "Select b From BookComments b inner join Users u on b.comment_user_id = u.user_id where b.comment_book_id= :book_id")
public List<BookComments> listAllDetailComments(#Param("book_id") long book_id);
}
BookDetail.js
<c:forEach items="${listBookComments}" var="bookcomments">
(No problem here) ${bookcomments.book_comment}
(The error is here) <h4>${bookcomments.user_name}</h4>
</c:forEach>
You should use comment_user_id and close the braces, ${bookcomments.comment_user_id} should work fine.
To solve your problem, you must use another object (known as a Data Transfer Object; DTO) which will contain the details from BookComments and the username.
An example would be:
package com.myapp.book;
public class BookCommentsDto{
private String bookComment;
private String username;
// You can add any other attributes that you need from the table book_comments
// add them in the constructor as well
public BookCommentsDto(String bookComment, String username){
this.bookComment = bookComment;
this.username = username;
}
}
I will refer to the class BookCommentsDto as dto. The above dto is the simple dto you might need. You can add other details you need from book comment in the dto and make sure to add them in the constructor as well. I have included a dummy package name on the top because we need to mention the full classpath of the dto in the query we will write. In the query below, I am creating the dto from the result of the sql. In the query, I am using the constructor of BookCommentsDto to create the object which will be returned.
#Query(value = "Select new com.myapp.book.BookCommentsDto(b.book_comment, u.user_name) From BookComments b inner join Users u on b.comment_user_id = u.user_id where b.comment_book_id= :book_id")
public List<BookCommentsDto> listAllDetailComments(#Param("book_id") long book_id);
Update return type in the service BookCommentsService
#Service
public class BookCommentsService {
#Autowired
private BookCommentsRepository repository;
public List<BookCommentsDto> listAllDetailComments(long book_id)
{
return repository.listAllDetailComments(book_id);
}
}
Update controller
#RequestMapping(value = "/bookdetail")
public ModelAndView showBookDetailForm(#RequestParam long book_id)
{
ModelAndView mav= new ModelAndView("bookdetail");
List<Books> book=booksService.bookGetWithId(book_id);
List<BookCommentsDto> listBookCommentsDto= bookCommentsService.listAllDetailComments(book_id);
mav.addObject("listBook",book);
mav.addObject("listBookCommentsDto", listBookCommentsDto);
return mav;
}
Finally update js
<c:forEach items="${listBookCommentsDto}" var="bookcommentsDto">
${bookcommentsDto.bookComment}
<h4>${bookcommentsDto.username}</h4>
</c:forEach>
if you do the above changes, the code should work just fine.
I have a few comments for you if you want to improve your code and get better at coding.
When using Hibernate, we must think in terms of entities instead of tables found in the DB. Therefore we do not do inner joins like we do in native sql, instead we use hibernate relationship to define the relationship between the table and we join the entities instead of the tables. You can read on OneToMany, ManyToOne and ManyToMany relationships when it comes to joining entities.
List<Books> book=booksService.bookGetWithId(book_id);
A book_id is returning a list of Books? book_id should be unique per book if ids are used correctly and even if it is returning a list of books, the generic of the list should be Book, not Books; List<Book>, not List<Books>.
When hibernate is implemented correctly, you should be able to retrieve all the data at once and it would be something like:
This is the type of code is which you should be able to achieve after working with hibernate for a while (appx 1 year), however there is still a lot that can be improved about it.
In book's repository
#Query("from book b inner join b.comments c inner join c.users where b.book_id = :book_id")
Book getBook(#Param("book_id") long book_id); // bookService.getBook will call this method
Controller level
Book book = bookService.getBook(bookId); // only this one is querying the DB
List<BookComment> bookComments = book.getComments();
List<User> users = book.getComments().getUsers();
Advance topics to read on (read in same order as posted):
HQL - Hibernate Query Language
N+1 issue with Hibernate
JPA Specifications (for creating dynamic queries)
QueryDsl - an improvement of JPA Specifications

How to implement one to many with jdbc

I'm trying to write a program to manage some clients. Each client has reservations, and each reservation has items.I know that I have one to many relationship, but I cannot figure out how to do that.
I'm not using Spring, only JDBC.
Without a database I did it like that:
public class Client {
private String _fullName;
private String _address;
private String _email;
private String _phoneNumber;
private List<Reservation> _reservations;
}
public class Reservation {
private List<Item> _items;
private int _totalSum;
private boolean _toDeliver;
private String _date;
}
public class Item {
//primary key.
private int _id;
private int _price;
private String _name;
}
Do I have to declare these Lists? Is it possible to do that without Spring, Hibernate and other stuff? I just want to know how design my program.
Yes, you can do everything just with a JDBC database driver.
The first thing you have to do is to design your database, that is your tables and constraints and etc.
Then you go back to Java and query your database using the JDBC.
A common approach to this is creating a DAO class, where you can decouple your database query from your code. It goes something like this:
public String getUserName(String userId) throws SQLException {
String sql = "SELECT name FROM user WHERE user_id = ?";
try (Connection connection = getConnection();
PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setString(1, userId);
try (ResultSet rs = stmt.executeQuery()) {
String name = rs.getString("name");
return name;
}
}
}
This is just a simple example, there's no error handling here, neither dealing with empty query results, there are better examples online to illustrate how to make a DAO and create a connection. Depending on where you are running this code, you would prefer using a Connection Pool for efficiency (another thing to read more about).
In your use case, you would have the DAO get the data to make your Item, create a new Item and return it from the DAO. There are plenty of examples of this online, if you have further questions, don't hesitate to ask.

Retrieve data as custom object Hibernate Criteria

The task is to retrieve data from the database for certain list of columns and return as custom already existing class.
I tried to resolve this task with following code:
public List<EntityOne> getServiceProviders(EntityTwo EntityTwo) {
Criteria criteria = createCriteria(EntityOne.class);
criteria.add(Restrictions.eq("EntityTwo", EntityTwo));
criteria.createAlias("spid", "two");
criteria.addOrder(Order.asc("two.entityName"));
criteria.setProjection(Projections.projectionList()
.add(Projections.property("entityId"), "entityId")
.add(Projections.property("publishStatus"), "publishStatus")
.add(Projections.property("two.entityName"), "two.entityName")
.add(Projections.property("two.entityId"), "two.entityId")
);
return criteria.list();
}
But I receive a list of data that is not grouped in class as I wanted.
Your question is not very clear especially where you are attempting to use Restrictions.eq("EntityTwo", EntityTwo) as this will not give proper results. Hibernate however provides a means to return EntityOne as an Object from the Columns selected using Hibernate Transformers class. In your case, you will need to write a custom class with getter setter of the columns you are returning. Note that is is important that the variables be named exactly like the alias columns.
Since your example is not clear, let me illustrate with a simple example: Say I need all purchase OrderAmount grouped by OrderDate and OrderNumber
public static List<YourCustomEntity> getAggregateOrders(){
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = null;
List<YourCustomEntity> list = new ArrayList<YourCustomEntity>();
try {
tx = session.beginTransaction();
Criteria cr = session.createCriteria(PurchaseOrders.class);
cr.setProjection(Projections.projectionList()
.add(Projections.sum("orderAmount").as("sumOrderAmount"))
.add(Projections.groupProperty("orderNumber").as("agOrderNumber"))
.add(Projections.groupProperty("orderDate").as("agOrderDate")));
cr.setResultTransformer(Transformers.aliasToBean(YourCustomEntity.class));
list = (List<YourCustomEntity>) cr.list();
}catch (Exception asd) {
System.out.println(asd.getMessage());
if (tx != null) {
tx.rollback();
}
} finally {
session.close();
}
return list;
}
In this regard, you will need your customEntity to have the three columns returned above. For example:
public class YourCustomEntity {
private double sumOrderAmount;
private String agOrderNumber;
private Date agOrderDate;
//And then the getters and setters
NB: Notice the naming of the variables is the same as the column aliases.
You should use Projections.groupProperty(propertyName);
criteria.setProjection(Projections.groupProperty("propertyName"));

How can I reduce code duplication in my database access layer?

I am new to Java and I'm trying to implement a basic database access layer.
I'm using Apache DBUtils to reduce JDBC boilerplate code and this is working really well.
The problem is that my implementation uses a separate class for CRUD for each table in my database and it feels wrong to be duplicating so much functionality.
Is this an acceptable design and if not what can I do to reduce code duplication?
Could I refactor my solution to use generics in some fashion?
I realize I could use an ORM (myBatis, Hibernate etc) as a solution but I would like to try to stick with DBUtils and plain JDBC if I can help it.
Just for clarification:
Lets say I have 2 tables...
---------------------
User | File
---------------------
userId | fileId
name | path
age | size
---------------------
In my current solution I would create 2 classes (UserStore, FileStore) and
each class would implement similar basic CRUD methods:
protected boolean Create(User newUser)
{
QueryRunner run = new QueryRunner(dataSource);
try
{
run.update("INSERT INTO User (name, age) " +
"VALUES (?, ?)", newUser.getName(), newUser.getAge());
}
catch (SQLException ex)
{
Log.logException(ex);
return false;
}
return true;
}
protected User Read(int userId)
{
try
{
User user = run.query("SELECT * FROM User WHERE userId = ? ", userId);
return user;
}
catch (SQLException ex)
{
Log.logException(ex);
return null;
}
}
protected update(User user)
{
... perform database query etc
}
protected delete(int userId)
{
... perform database query etc
}
You asked how i would do this with Template method. Here is an example how you could do it:
public class AbstractDAO<T> {
private String table;
private String id_field;
public AbstractDAO(String table, String id_field){
this.table = table;
...
}
public T read(int id){
try
{
T user = run.query("SELECT * FROM "+ table + " WHERE "+id_field +" = ? ", id);
return user;
}
catch (SQLException ex)
{
Log.logException(ex);
return null;
}
}
This one looks easy, how about Create?
public boolean Create(T user){
QueryRunner run = new QueryRunner(dataSource);
try
{
run.update("INSERT INTO "+table+ getFields() +
"VALUES " + getParameters(user));
}
catch (SQLException ex)
{
Log.logException(ex);
return false;
}
return true;
}
protected abstract String getFields();
protected abstract String getParameters(T user);
Ugly, and insecure, but okay for transmitting the idea.
It looks like I can give you few simple suggestions for your question.
1)
Instead of managing queries inside DAOs like what you are doing, make a factory class that has list of queries for your needs.
like
class QueryFactory {
static String INSERT_BOOK = "BLAH";
static String DELETE_BOOK = "BLAH";
}
This will separate queries from DAO code and make it easier to manage.
2)
Implement a generic DAO
http://www.codeproject.com/Articles/251166/The-Generic-DAO-pattern-in-Java-with-Spring-3-and
3) As you have mentioned above, use ORM to help yourself binding beans to Database and many more features.
Using DBUtils you have already abstracted away a lot of boilerplate code. What remains is mostly the work that differs between entities : The right sql statement, transformation of entity objects into UPDATE parameters and vice versa with SELECTs, exception handling.
Unfortunately it is not easy to create a general abstraction that is flexible enough for these remaining tasks. That's what ORM mappers are all about. I would still suggest to look into one of these. If you stick to the JPA API, you are still in standards land and able to switch the ORM provider more easily (although there is always some coupling).
I was impressed by SpringData's Repository abstraction. In simple use cases they give you zero code DAO's. If you are already using Spring and just want to persist your object model you should definitely look into it.
Alternatively I made some good experiences with jooq. It can also create DTO's and corresponding DAO's based on the tables in your schema. In contrast to ORM mappers it is closer to the relational schema, which may be an advantage or a disadvantage.
mybatis allows you to return a resulttype as hashmap:
<select id="mCount" resultType="hashmap">
select managerName, count(reportees) AS count
from mgr_employee
group by managerName;
</select>
So you can effectively write out in a workflow like this:
1) Develop an interface
2a) Use mybatis annotation to define the query required OR
2b) Link the interface to a xml and write the query
Take note that this will not be involving any DAO and other boilerplates involve as above

How to use Spring jdbc templates (jdbcTemplate or namedParameterJDBCTem) to retrieve values from database

Few days into Spring now. Integrating Spring-JDBC into my web application. I was successfully able to preform CRUD operations on my DB, impressed with boiler-plate code reduction. But I am failing to use the query*() methods provided in NamedParameterJDBCTemplate. Most of the examples on the internet provide the usage of either RowMapper or ResultSetExtractor. Though both uses are fine, it forces me to create classes which have to implement these interfaces. I have to create bean for every type of data I am loading for the DB (or maybe I am mistaken).
Problem arises in code section where I have used something like this:
String query="select username, password from usertable where username=?"
ps=conn.prepareStatement(query);
ps.setString(username);
rs=ps.executeQuery();
if(rs.next()){
String username=rs.getString("username");
String password=rs.getString("password")
//Performs operation on them
}
As these values are not stored in any bean and used directly, I am not able to integrate jdbcTemplate in these kind of situations.
Another situation arises when I am extracting only part of properties present in bean from my database.
Example:
public class MangaBean{
private String author;
private String title;
private String isbn;
private String releaseDate;
private String rating;
//getters and setters
}
Mapper:
public class MangaBeanMapper implements RowMapper<MangaBean>{
#Override
public MangaBean mapRow(ResultSet rs, int arg1) throws SQLException {
MangaBean mb=new MangaBean();
mb.setAuthor(rs.getString("author"));
mb.setTitle(rs.getString("title"));
mb.setIsbn(rs.getString("isbn"));
mb.setReleaseDate(rs.getString("releaseDate"));
mb.setRating(rs.getString("rating"));
return mb;
}
}
The above arrangement runs fine like this:
String query="select * from manga_data where isbn=:isbn"
Map<String, String> paramMap=new HashMap<String, String>();
paramMap.put("isbn", someBean.getIsbn());
return template.query(query, paramMap, new MangaBeanMapper());
However, if I only want to retrieve two/three values from my db, I cannot use the above pattern as it generates a BadSqlGrammarException: releaseDate does not exist in ResultSet . Example :
String query="select title, author where isbn=:isbn"
Map<String, String> paramMap=new HashMap<String, String>();
paramMap.put("isbn", someBean.getIsbn());
return template.query(query, paramMap, new MangaBeanMapper());
Template is an instance of NamedParameterJDBCTemplate. Please advice me solutions for these situations.
The other answers are sensible: you should create a DTO bean, or use the BeanPropertyRowMapper.
But if you want to be able to have more control than the BeanPropertyRowMapper, (or reflection makes it too slow), you can use the
queryForMap
method, which will return you a list of Maps (one per row) with the returned columns as keys. Because you can call get(/* key that is not there */) on a Map without throwing an exception (it will just return null), you can use the same code to populate your object irrespective of which columns you selected.
You don't even need to write your own RowMapper, just use the BeanPropertyRowMapper that spring provides. The way it works is it matches the column names returned to the properties of your bean. Your query has columns that match your bean exactly, if it didn't you would use an as in your select as follows...
-- This query matches a property named matchingName in the bean
select my_column_that doesnt_match as matching_name from mytable;
The BeanPropertyRowMapper should work with both queries you listed.
Typically, yes : for most queries you would create a bean or object to transform the result into. I would suggest that more most cases, that's want you want to do.
However, you can create a RowMapper that maps a result set to a map, instead of a bean, like this. Downside would be be losing the type management of beans, and you'd be relying on your jdbc driver to return the correct type for each column.
As #NimChimpskey has just posted, it's best to create a tiny bean object : but if you really don't want to do that, this is another option.
class SimpleRowMapper implements RowMapper<Map<String, Object>> {
String[] columns;
SimpleRowMapper(String[] columns) {
this.columns = columns;
}
#Override
public Map<String, Object> mapRow(ResultSet resultSet, int i) throws SQLException {
Map<String, Object> rowAsMap = new HashMap<String, Object>();
for (String column : columns) {
rowAsMap.put(column, resultSet.getObject(column));
}
return rowAsMap;
}
}
In yr first example I would just create a DTO Bean/Value object to store them. There is a reason its a commonly implemented pattern, it takes minutes to code and provides many long term benefits.
In your second example, create a second implementation of rowmapper where you don;t set the fields, or supply a null/subsitute value to mangabean where necessary :
#Override
public MangaBean mapRow(ResultSet rs, int arg1) throws SQLException {
MangaBean mb=new MangaBean();
mb.setAuthor(rs.getString("author"));
mb.setTitle(rs.getString("title"));
/* mb.setIsbn("unknown");*/
mb.setReleaseDate("unknown");
mb.setRating(null);
return mb;
}

Categories