Criteria ORDER BY date not working? - java

Can you please help me in solving this problem. I am trying to order the results of an criteria query by date, but I'm not getting the results I need.I saved date in String format,how can i order by date using criteria
The code I'm using is:
#Override
public List<Program> getListProgram() {
Session session=sessionFactory.openSession();
Criteria criteria=session.createCriteria(Program.class);
criteria.addOrder(Order.asc("createdDate"));
List<Program> programs=(List<Program>)criteria.list();
return programs;
}
Results are:
01/02/2009
03/01/2009
04/06/2009
05/03/2009
06/12/2008
07/02/2009
Results should be:
06/12/2008
03/01/2009
01/02/2009
07/02/2009
I need to select the date in the format above.
Your help is much appreciated.

You have to call criteria.addOrder(Order.asc("createdDate")); before executing the list method on criteria.
#Override
public List<Program> getListProgram() {
Session session=sessionFactory.openSession();
Criteria criteria=session.createCriteria(Program.class);
criteria.addOrder(Order.asc("createdDate"));
List<Program> programs=(List<Program>)criteria.list();
return programs;
}
EDIT
In your case, if you want to order by String dates, as i mentionned in the comments, this answer is not the proper you can get ( may be turning creationDate into a Date type is the best! for sure).
You can try some code like :
static final String DF = "DD/MM/YYYY";
static final SimpleDateFormat SDF = new SimpleDateFormat(DF);
#Override
public List<Program> getListProgram() {
Session session=sessionFactory.openSession();
Criteria criteria=session.createCriteria(Program.class);
List<Program> =(List<Program>)criteria.list();
boolean asc = true;
programs.sort((a, b) -> {
int comparison = 0;
try {
comparison = SDF.parse(a.getCreatedDate()).compareTo(SDF.parse(b.getCreatedDate()));
} catch (ParseException e) {
// handle it!!
}
return asc ? comparison : (0-comparison);
});
return programs;
}
EDIT 2
If you want to avoid using lambdas, try using this instead :
Collections.sort(programs, new Comparator<Main>() {
#Override
public int compare(Program a, Program b) {
int comparison = 0;
try {
comparison = SDF.parse(a.getCreatedDate()).compareTo(SDF.parse(b.getCreatedDate()));
} catch (ParseException e) {
// handle it!!
}
return asc ? comparison : (0-comparison);
}
});

This is how Implemented it
private List<Users> findUsers(boolean all, int maxResults, int firstResult, SchoolData data) {
EntityManager em = getEntityManager(data.getExternalId());
try {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery();
Root<Users> from = cq.from(Users.class);
cq.orderBy(cb.desc(from.get("dateCreated")));
cq.select(cq.from(Users.class));
Query q = em.createQuery(cq);
if (!all) {
q.setMaxResults(firstResult);
q.setFirstResult(maxResults);
}
return q.getResultList();
} finally {
em.close();
}
}

Criteria are passed to Hibernate and are translated to the certain language. They appear after where clause in the select so they all the limitations must be set before executing query. Although you don't see it, there is a query generated and executed when calling criteria.list().
Try the following:
#Override
public List<Program> getListProgram() {
Session session=sessionFactory.openSession();
Criteria criteria=session.createCriteria(Program.class);
criteria.addOrder(Order.asc("createdDate"));
List<Program> programs=(List<Program>)criteria.list();
return programs;
}

Firstly, dates should be stored as dates, not strings. If you are working with a legacy system, it may not be possible to change this, but I'd argue that it probably would make your life easier to bite the bullet and refactor.
As a string, the sort function is working because 05 comes before 06.
I'd suggest using annotation or mapping, as outlined in this question, to convert your string to a date in the pojo so that it is the correct type at least somewhere in your application.
public class StringToDateConverter implements AttributeConverter<Date, String> {
String convertToDatabaseColumn(Date attribute) { /* Conversion code */ }
Date convertToEntityAttribute(String dbData) { /* Conversion code */ }
}
and
public class MyPojo {
#javax.persistence.Convert(converter = StringToDateConverter.class)
public Date getCreateDate() {
}
}

Related

How to translate dynamic query from QueryDSL into JOOQ?

I use QueryDSL just for dynamic queries in Spring Boot 2+ with Spring Data JPA applications in the following manner:
#Override
public Iterable<Books> search(String bookTitle, String bookAuthor, String bookGenre) {
BooleanBuilder where = dynamicWhere(bookTitle, bookAuthor, bookGenre);
return booksRepository.findAll(where, orderByTitle());
}
public BooleanBuilder dynamicWhere(String bookTitle, String bookAuthor, String bookGenre) {
QBooks qBooks = QBooks.books;
BooleanBuilder where = new BooleanBuilder();
if (bookTitle != null) {
where.and(qBooks.title.equalsIgnoreCase(bookTitle));
}
if (bookAuthor!= null) {
where.and(qBooks.author.eq(bookAuthor));
}
if (bookGenre!= null) {
where.and(qBooks.genre.eq(bookGenre));
}
return where;
}
I want to use JOOQ in a similar transparent way, but I do not know how to do it elegantly. I also like that in JOOQ there isn't QBooks-like generated constructs, although I think JOOQ also generates some tables. Anyhow, I am confused and I couldn't find an answer online, so I am asking can it be done and how.
Thanks
jOOQ doesn't have a specific "builder" to construct your predicates. All the "building" API is located directly on the predicate type, i.e. the Condition
#Override
public Iterable<Books> search(String bookTitle, String bookAuthor, String bookGenre) {
Condition where = dynamicWhere(bookTitle, bookAuthor, bookGenre);
return dslContext.selectFrom(BOOKS)
.where(where)
.orderBy(BOOKS.TITLE)
.fetchInto(Books.class);
}
public Condition dynamicWhere(String bookTitle, String bookAuthor, String bookGenre) {
Condition where = DSL.noCondition();
if (bookTitle != null) {
where = where.and(BOOKS.TITLE.equalsIgnoreCase(bookTitle));
}
if (bookAuthor!= null) {
where = where.and(BOOKS.AUTHOR.eq(bookAuthor));
}
if (bookGenre!= null) {
where = where.and(BOOKS.GENRE.eq(bookGenre));
}
return where;
}
This is assuming:
1) That you have injected a properly configured DSLContext
2) That you're using jOOQ's code generator
For more information, see also https://www.jooq.org/doc/latest/manual/sql-building/dynamic-sql/

Sorting with SortableDataProvider and Hibernate

I have the following code:
PersonDao.java
#Repository
#Transactional
public class PersonDao implements PersonDaoIface {
Object property;
String order;
#Autowired
private SessionFactory sessionFactory;
public PersonDao() {
}
public PersonDao(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
#SuppressWarnings("unchecked")
#Override
public List<Person> getAll(long first, long count) {
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Person.class);
this.setPaging(criteria, first, count);
addSort(criteria);
return criteria.list();
}
#Override
public long getAllCount() {
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Person.class)
.setProjection(Projections.rowCount());
Long i = (Long) criteria.uniqueResult();
return i;
}
#Override
public List<Person> getByFilter(Person person, int first, int count) {
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Person.class);
criteria.add(Restrictions.eq("firstName", person.getFirstName()));
criteria.add(Restrictions.eq("lastName", person.getLastName()));
this.setPaging(criteria, first, count);
return criteria.list();
}
#Override
public long getByFilterCount(Person person) {
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Person.class);
criteria.add(Restrictions.eq("firstName", person.getFirstName()));
criteria.add(Restrictions.eq("lastName", person.getLastName()));
criteria.setProjection(Projections.rowCount());
Long result = (Long) criteria.uniqueResult();
return result;
}
private void setPaging(Criteria criteria, long first, long count) {
criteria.setFirstResult((int) first);
criteria.setMaxResults((int) count);
}
private void addSort(Criteria criteria) {
if (property != null) {
if (order.equalsIgnoreCase(SortOrder.ASCENDING.toString())) {
criteria.addOrder(Order.asc((String)property));
} else {
criteria.addOrder(Order.desc((String)property));
}
}
}
#Override
public void setSort(Object property, String order) {
this.property = property;
this.order = order;
}
}
SortableDataProvider
public class PersonSortableDataProvider extends SortableDataProvider {
private transient PersonDaoIface personDao;
public PersonSortableDataProvider(PersonDaoIface personDao) {
this.personDao = personDao;
}
public PersonSortableDataProvider() {
}
/**
*
*/
private static final long serialVersionUID = 1L;
#Override
public Iterator<Person> iterator(long first, long count) {
System.out.println(getSort());
return personDao.getAll(first, count).iterator();
}
#Override
public long size() {
long result = personDao.getAllCount();
return result;
}
#Override
public IModel<Person> model(final Object object) {
return new AbstractReadOnlyModel<Person>() {
#Override
public Person getObject() {
return (Person) object;
}
};
}
}
A panel with a data table using the sortable data provider
public DataDisplayPanel(String id) {
super(id);
List<IColumn> columns = new ArrayList<IColumn>();
columns.add(new PropertyColumn(new Model<String>("First Name"), "firstName"));
columns.add(new PropertyColumn(new Model<String>("Last Name"), "lastName"));
AjaxFallbackDefaultDataTable table = new AjaxFallbackDefaultDataTable("personData", columns,
personSortableDataProvider, 8);
table.addTopToolbar(new HeadersToolbar(table, personSortableDataProvider));
add(table);
}
I have paging done no problem but I am having trouble understanding how to get sorting working with hibernate, I can see how you could do the sorting from the java side of things but given that I could potentially get large data sets back I don't like this idea.
Given my code above does anyone have a way of getting the data table, on click of either first name or last name to then make the same query found in the iterator with the additional order by clause.
You are almost there. You just need an:
addOrder(Order.asc(columnName))
The doc is here.
To anyone that encounters this situation I have the following setup:
hibernate 4, spring 4 and wicket 6
I inject using Spring and it seems wicket and spring get confused if you inject within the SortableDataProvider.
I don't know what exactly happens; when i step over the project I will have a better idea but it appears setSort is not getting set correctly, when i move the Dao class out of sortable data provider and into the page and inject it there, then pass the dao instance into sortable data provider the sorting works correctly.

HibernateCallback returns value even though search criteria is not qualified

I am having a trouble with my codes. If I input correct criteria, let say 300 for the amount it returns correct result/s.But if I put not qualified search criteria it still return result/s which is incorrect.
#Override
public Page<ApPayment> searchPayment(final String searchCriteria,
final PageSetting pageSetting) {
HibernateCallback<Page<ApPayment>> paymentCallBack = new HibernateCallback<Page<ApPayment>>() {
#Override
public Page<ApPayment> doInHibernate(Session session)
throws HibernateException, SQLException {
Criteria criteria = session.createCriteria(ApPayment.class);
if(!searchCriteria.isEmpty() && StringFormatUtil.isNumeric(searchCriteria)) {
Integer paymentNo = Integer.valueOf(searchCriteria);
Double amount = Double.valueOf(searchCriteria);
criteria.add(Restrictions.or(Restrictions.like(ApPayment.FIELD.paymentNumber.name(), paymentNo), Restrictions.eq(ApPayment.FIELD.amount.name(), amount)));
}
Page<ApPayment> payments = getAll(criteria, pageSetting);
for (ApPayment payment: payments.getData()) {
--some codes--
}
return payments;
}
};
return getHibernateTemplate().execute(paymentCallBack);
}
Thanks in advance.

Java Data-Entity model: Constructing general types

I have had some trouble with using a general type in a static method.
All comments on the source code are welcome, especially ones that significantly improve the code. I am also currently not planning on using any external framework, apart from JDBC, to keep it still simple, please do not put too much emphasis on that.
My view on not using external frameworks is also supported by the fact that the operations I will be using on the database are very minimal:
Inserting data
Updating data
Retrieving all fields. (And simply by putting in a different SQL Query you could already select what fields to retrieve
I do not plan on making a full framework, so I know that it will not be supporting everything. The speed of retrieving all fields is neither a real issue, as this will be pretty much only done on server bootup, and if used at any other time it will be done in a background task for which I do not really care when it is finished.
Entity.java:
abstract public class Entity<KeyType, DataType> {
protected KeyType key;
protected List<Object> data;
public Entity() {
data = new ArrayList<>();
}
//abstract public static Map<KeyType, DataType> getAll();
protected List<Object> createData(final DataAction dataAction) {
List<Object> list = new ArrayList<>();
if (dataAction == DataAction.INSERT) {
list.add(key);
}
list.addAll(data);
if (dataAction == DataAction.UPDATE) {
list.add(key);
}
return list;
}
abstract public void insert();
abstract public void update();
protected static <KeyType, DataType> Map<KeyType, DataType> getData(final Class<DataType> dataTypeClass, final String query) {
Map<KeyType, DataType> map = new HashMap<>();
try {
PreparedStatement preparedStatement = DatabaseConnection.getConnection().prepareStatement(query);
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
KeyType key = (KeyType)resultSet.getObject(1);
int index = 2;
List<Object> dataList = new ArrayList<>();
while (resultSet.getObject(index) != null) {
dataList.add(resultSet.getObject(index));
index++;
}
DataType dataObject = null;
try {
dataObject = dataTypeClass.getConstructor(List.class).newInstance(dataList);
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException ex) {
Logger.getLogger(Entity.class.getName()).log(Level.SEVERE, null, ex);
}
map.put(key, dataObject);
}
} catch (SQLException ex) {
Logger.getLogger(Entity.class.getName()).log(Level.SEVERE, null, ex);
}
return map;
}
protected void executeQuery(final String query, final List<Object> data) {
try {
PreparedStatement preparedStatement = DatabaseConnection.getConnection().prepareStatement(query);
int dataIndex = 0;
for (Object dataObject : data) {
preparedStatement.setObject(dataIndex, dataObject);
dataIndex++;
}
preparedStatement.execute();
preparedStatement.close();
} catch (SQLException ex) {
Logger.getLogger(Entity.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
A concrete implementation, Account.java:
public class Account extends Entity<String, Account> {
private final static String SELECT_ALL_QUERY = "SELECT * FROM accounts";
private final static String INSERT_QUERY = "INSERT INTO accounts (username, password) VALUES(?, ?)";
private final static String UPDATE_QUERY = "UPDATE accounts SET password=? WHERE username=?";
private String username;
private String password;
public Account(final String username, final String password) {
this.username = username;
this.password = password;
key = username;
data.add(password);
}
public Account(final List<Object> data) {
this((String)data.get(0), (String)data.get(1));
}
public String getUsername() {
return username;
}
public void setUsername(final String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(final String password) {
this.password = password;
}
public static Map<String, Account> selectAll() {
return getData(Account.class, SELECT_ALL_QUERY);
}
#Override
public void insert() {
executeQuery(INSERT_QUERY, createData(DataAction.INSERT));
}
#Override
public void update() {
executeQuery(UPDATE_QUERY, createData(DataAction.UPDATE));
}
}
I am generally happy about the concrete implementation, it seems like I have managed to bring it down to a bare minimum, except public Account(final List<Object> data) does not seem that nice, but I can live with it.
However, as guessed, the getData() from Entity is definately not nice, and I would like to improve it if possible.
What I would like to use is something like DataType dataObject = new DataType(dataList), but it seems like Generic Type Arguments cannot be instantiated.
So are there any ways of optimizing my current code in my current view? And is it possible to decouple the concrete classes and abstract classes even more?
EDIT:
Added a relevant question (I don't think I should make a fully new question for this thing, right?):
Is there a way to move the static Strings (SQL Queries) and the insert() and update() out of the Account class, into the Entity class?
To avoid the use of reflection in your getData method you should accept a factory that given a ResultSet creates instances of the specific type. Your selectAll method would then be something like:
public static Map<String, Account> selectAll()
{
return getData(
new EntityFactory<Account>()
{
public Account newInstance(ResultSet resultSet) throws SQLException
{
return new Account(resultSet.getString(0), resultSet.getString(1));
}
},
SELECT_ALL_QUERY
);
}
The getData method then ends up something like:
protected static <K, T extends Entity<K>> Map<K, T> getData(EntityFactory<T> entityFactory, String query)
{
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try
{
connection = dataSource.getConnection();
preparedStatement = connection.prepareStatement(query);
resultSet = preparedStatement.executeQuery();
Map<K, T> entities = new HashMap<>();
while (resultSet.next())
{
Entity<K> entity = entityFactory.newInstance(resultSet);
entities.put(entity.getKey(), entity);
}
return entities;
}
finally
{
closeQuietly(resultSet);
closeQuietly(prepareStatement);
closeQuietly(connection);
}
}
And assumes the Entity looks like:
public interface Entity<K>
{
public K getKey();
}
This allows you to remove the reflection and keeps the code that understands the database structure in one place. You should also use a similar template pattern to map from the domain object to the prepared statement when doing inserts and updates.
Now you've asked for comments on the code in general.
First off, code like this violates the Single Responsibility Principal and Seperation Of Concerns. A domain class should be a domain class and not contain persistance logic. Look at patterns like the Data Access Object for how this should be done.
Second, while I'm all for keeping it simple, Hibernate solved this problem a long time ago and JPA standardized it - you need a very good reason not to use one or both of these APIs.
Finally, your use of database resources - if you are going to use JDBC directly you have to clean up properly. Database connections are expensive resources and should be handled as such, the basic template for any JDBC call should be:
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try
{
connection = //get connection from pool or single instance.
preparedStatement = connection.prepareStatement("SELECT * FROM table WHERE column = ?");
preparedStatement.setString(1, "some string");
resultSet = preparedStatement.executeQuery();
while (resultSet.next())
{
//logic goes here.
}
}
catch (SQLException e)
{
//Handle exceptions.
}
finally
{
closeQuietly(resultSet);
closeQuietly(prepareStatement);
closeQuietly(connection);
}
The closeQuietly method has to be overloaded but should take the general form:
try
{
if (resultSet != null)
{
resultSet.close();
}
}
catch (SQLException e)
{
//Log exceptions but don't re-throw.
}
Well, as Darwind and Nick Holt told you, in a normal situation, you should use JPA, which is the Java standard specification for object-relational mapping. You can use Hibernate, EclipseLink or any other framework behind. Their design is can manage connections, transactions. In addition, using standards rather than exotic frameworks means that you can get help more easily for the community.
Another option is using Spring JDBC, which is quite light and facilitates many things.
Anyway, I suppose you did this for learning purpose so let's try to go further.
First, I think you should separate the classes in charge or retrieving the data (call it manager or Data Access Object -DAO-) and the entites representing the data themselves.
For me, using the class to get all the data as you did isn't a problem in itself. The problem is the position of the key is hardcoded. This should not be determined directly a generic (I mean the same for all the Entity implementation). This makes queries subjects to bugs when the first field is not the key (are you sure a select * from... will ALWAYS return the key in the first position? ) or with a composite key.
I think a better solution is to crate a Mapper interface and to implement it for each entity.
public interface RecordMapper<KeyType, DataType extends Entity> {
public void appendToMap(ResultSet resultSet, Map<KeyType, DataType>) throws SQLException;
}
The implementation of the mapper should be in charge of instanciating your entity, retrieving the key from the resultset, populating your entity and putting it in the map you expect.
public class AccountMapper implement RecordMapper<String, Account>{
public void appendToMap(ResultSet resultSet, Map<String, Account> accounts) throws SQLException {
String user= resultSet.getString("userName");
String pwd= resultSet.getString("passWord");
Account account = new Account(user, pwd);
accounts.put(user, account);
}
}
As I told you should move your data access methods in a DAO:
public class DAO{
public <KeyType, DataType> Map<KeyType, DataType> getData(final RecordMapper<KeyType, DataType> mapper, final String query) {
Map<KeyType, DataType> map = new HashMap<>();
try {
PreparedStatement preparedStatement = DatabaseConnection.getConnection().prepareStatement(query);
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
mapper.appendToMap(resultSet, map);
}
} catch (SQLException ex) {
Logger.getLogger(Entity.class.getName()).log(Level.SEVERE, null, ex);
} finally {
if(resultSet != null){
try{resultSet.close();} catch (Exception e){}
}
if(preparedStatement!= null){
try{preparedStatement.close();} catch (Exception e){}
}
}
return map;
}
public void executeQuery(final String query, final List<Object> data) {
try {
PreparedStatement preparedStatement = DatabaseConnection.getConnection().prepareStatement(query);
int dataIndex = 0;
for (Object dataObject : data) {
preparedStatement.setObject(dataIndex, dataObject);
dataIndex++;
}
preparedStatement.execute();
} catch (SQLException ex) {
Logger.getLogger(Entity.class.getName()).log(Level.SEVERE, null, ex);
} finally {
if(resultSet != null){
try{resultSet.close();} catch (Exception e){}
}
if(preparedStatement!= null){
try{preparedStatement.close();} catch (Exception e){}
}
}
}
}
To answer your second quenstion, I think that putting your request string in the abstract parent instead of is certainly not a good idea. Each time you create new entity, you have to create a new query in the parent. Weird...unless I haven't understood properly your question.
Personnaly I think that the queries should be build dynamically and you should use reflection and annotations but the answer should be a bit long. Once again, you can get a look at JPA to see how creating an entity should look like. By the way, it should be even better if the entities didn't have to extend a parent Entity class.

Mapping a row from a SQL data to a Java object

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"}}
]

Categories

Resources