Spring Boot and Hibernate Dynamic Id Generator for invoice Number - spring

I am working on Spring boot Application with hibernate to create simple invoice.
I want to generate invoice number through hibernate in the following format
as below
YEAR/MONTH/Number(Will Increase)
The above sequence is dependent on invoice date.
Month and year values do change based on the current Date.
Once the year completed, let say after the completion of one financial
year the sequence again start from the beginning, like month/Year/number.
I have tried to Sequence Generator , table Generator . My Code snippet is as below :
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "year_gen")
#GenericGenerator(name = "year_gen", strategy = "com.example.generator.CustomGenerator")
#Column(name = "invoice_no")
private String invoiceno;
But I am not getting any idea that how to make it dependable on Invoice Date.
My Generator is below :
public class CustomGenerator implements IdentifierGenerator {
#Override
public Serializable generate(SharedSessionContractImplementor sessionImpl, Object data)
throws HibernateException {
Serializable result = null;
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
String prefix = "";
DateTimeFormatter newPattern = DateTimeFormatter.ofPattern("yyyyMM");
LocalDate ldObj = LocalDate.now();
String yyyymm = newPattern.format(ldObj).toString();
prefix = "INV/"+yyyymm+"/";
connection = sessionImpl.connection();
statement = connection.createStatement();
try {
resultSet = statement.executeQuery("select max(id)+1 from invoice");
} catch(Exception ex) {
}
if(resultSet.next()) {
int nextValue = resultSet.getInt(1);
String suffix = String.format("%05d", nextValue + 1);
result = prefix.concat(suffix);
System.out.println("Custom generated Sequence value : "+result);
} else
{
int nextValue = 1;
String suffix = String.format("%05d", nextValue + 1);
result = prefix.concat(suffix);
}
} catch (SQLException e) {
e.printStackTrace();
}
return result;
}
}

Related

How to export huge result set from database into several csv files and zip them on the fly?

I need to create a REST controller which extracts data from a database and write it into CSV files that will ultimately be zipped together. Each CSV file should contain exactly 10 lines. Eventually all CSV files should be zipped into a one zip file. I want everything to happen on the fly, meaning - saving files to a temporary location on the disk is not an option. Can someone provide me with an example?
I found a very nice code to export huge amount of rows from database into several csv files and zip it.
I think this is a nice code that can assist alot of developers.
I have tested the solution and you can find the entire example at : https://github.com/idaamit/stream-from-db/tree/master
The conroller is :
#GetMapping(value = "/employees/{employeeId}/cars") #ResponseStatus(HttpStatus.OK) public ResponseEntity<StreamingResponseBody> getEmployeeCars(#PathVariable int employeeId) {
log.info("Going to export cars for employee {}", employeeId);
String zipFileName = "Cars Of Employee - " + employeeId;
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_TYPE, "application/zip")
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + zipFileName + ".zip")
.body(
employee.getCars(dataSource, employeeId));
The employee class, first checks if we need to prepare more than one csv or not :
public class Employee {
public StreamingResponseBody getCars(BasicDataSource dataSource, int employeeId) {
StreamingResponseBody streamingResponseBody = new StreamingResponseBody() {
#Override
public void writeTo(OutputStream outputStream) throws IOException {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
String sqlQuery = "SELECT [Id], [employeeId], [type], [text1] " +
"FROM Cars " +
"WHERE EmployeeID=? ";
PreparedStatementSetter preparedStatementSetter = new PreparedStatementSetter() {
public void setValues(PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setInt(1, employeeId);
}
};
StreamingZipResultSetExtractor zipExtractor = new StreamingZipResultSetExtractor(outputStream, employeeId, isMoreThanOneFile(jdbcTemplate, employeeId));
Integer numberOfInteractionsSent = jdbcTemplate.query(sqlQuery, preparedStatementSetter, zipExtractor);
}
};
return streamingResponseBody;
}
private boolean isMoreThanOneFile(JdbcTemplate jdbcTemplate, int employeeId) {
Integer numberOfCars = getCount(jdbcTemplate, employeeId);
return numberOfCars >= StreamingZipResultSetExtractor.MAX_ROWS_IN_CSV;
}
private Integer getCount(JdbcTemplate jdbcTemplate, int employeeId) {
String sqlQuery = "SELECT count([Id]) " +
"FROM Cars " +
"WHERE EmployeeID=? ";
return jdbcTemplate.queryForObject(sqlQuery, new Object[] { employeeId }, Integer.class);
}
}
This class StreamingZipResultSetExtractor is responsible to split the csv streaming data into several files and zip it.
#Slf4j
public class StreamingZipResultSetExtractor implements ResultSetExtractor<Integer> {
private final static int CHUNK_SIZE = 100000;
public final static int MAX_ROWS_IN_CSV = 10;
private OutputStream outputStream;
private int employeeId;
private StreamingCsvResultSetExtractor streamingCsvResultSetExtractor;
private boolean isInteractionCountExceedsLimit;
private int fileCount = 0;
public StreamingZipResultSetExtractor(OutputStream outputStream, int employeeId, boolean isInteractionCountExceedsLimit) {
this.outputStream = outputStream;
this.employeeId = employeeId;
this.streamingCsvResultSetExtractor = new StreamingCsvResultSetExtractor(employeeId);
this.isInteractionCountExceedsLimit = isInteractionCountExceedsLimit;
}
#Override
#SneakyThrows
public Integer extractData(ResultSet resultSet) throws DataAccessException {
log.info("Creating thread to extract data as zip file for employeeId {}", employeeId);
int lineCount = 1; //+1 for header row
try (PipedOutputStream internalOutputStream = streamingCsvResultSetExtractor.extractData(resultSet);
PipedInputStream InputStream = new PipedInputStream(internalOutputStream);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(InputStream))) {
String currentLine;
String header = bufferedReader.readLine() + "\n";
try (ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream)) {
createFile(employeeId, zipOutputStream, header);
while ((currentLine = bufferedReader.readLine()) != null) {
if (lineCount % MAX_ROWS_IN_CSV == 0) {
zipOutputStream.closeEntry();
createFile(employeeId, zipOutputStream, header);
lineCount++;
}
lineCount++;
currentLine += "\n";
zipOutputStream.write(currentLine.getBytes());
if (lineCount % CHUNK_SIZE == 0) {
zipOutputStream.flush();
}
}
}
} catch (IOException e) {
log.error("Task {} could not zip search results", employeeId, e);
}
log.info("Finished zipping all lines to {} file\\s - total of {} lines of data for task {}", fileCount, lineCount - fileCount, employeeId);
return lineCount;
}
private void createFile(int employeeId, ZipOutputStream zipOutputStream, String header) {
String fileName = "Cars for Employee - " + employeeId;
if (isInteractionCountExceedsLimit) {
fileCount++;
fileName += " Part " + fileCount;
}
try {
zipOutputStream.putNextEntry(new ZipEntry(fileName + ".csv"));
zipOutputStream.write(header.getBytes());
} catch (IOException e) {
log.error("Could not create new zip entry for task {} ", employeeId, e);
}
}
}
The class StreamingCsvResultSetExtractor is responsible for transfer the data from the resultset into csv file. There is more work to do to handle special character set which are problematic in csv cell.
#Slf4j
public class StreamingCsvResultSetExtractor implements ResultSetExtractor<PipedOutputStream> {
private final static int CHUNK_SIZE = 100000;
private PipedOutputStream pipedOutputStream;
private final int employeeId;
public StreamingCsvResultSetExtractor(int employeeId) {
this.employeeId = employeeId;
}
#SneakyThrows
#Override
public PipedOutputStream extractData(ResultSet resultSet) throws DataAccessException {
log.info("Creating thread to extract data as csv and save to file for task {}", employeeId);
this.pipedOutputStream = new PipedOutputStream();
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
prepareCsv(resultSet);
});
return pipedOutputStream;
}
#SneakyThrows
private Integer prepareCsv(ResultSet resultSet) {
int interactionsSent = 1;
log.info("starting to extract data to csv lines");
streamHeaders(resultSet.getMetaData());
StringBuilder csvRowBuilder = new StringBuilder();
try {
int columnCount = resultSet.getMetaData().getColumnCount();
while (resultSet.next()) {
for (int i = 1; i < columnCount + 1; i++) {
if(resultSet.getString(i) != null && resultSet.getString(i).contains(",")){
String strToAppend = "\"" + resultSet.getString(i) + "\"";
csvRowBuilder.append(strToAppend);
} else {
csvRowBuilder.append(resultSet.getString(i));
}
csvRowBuilder.append(",");
}
int rowLength = csvRowBuilder.length();
csvRowBuilder.replace(rowLength - 1, rowLength, "\n");
pipedOutputStream.write(csvRowBuilder.toString().getBytes());
interactionsSent++;
csvRowBuilder.setLength(0);
if (interactionsSent % CHUNK_SIZE == 0) {
pipedOutputStream.flush();
}
}
} finally {
pipedOutputStream.flush();
pipedOutputStream.close();
}
log.debug("Created all csv lines for Task {} - total of {} rows", employeeId, interactionsSent);
return interactionsSent;
}
#SneakyThrows
private void streamHeaders(ResultSetMetaData resultSetMetaData) {
StringBuilder headersCsvBuilder = new StringBuilder();
for (int i = 1; i < resultSetMetaData.getColumnCount() + 1; i++) {
headersCsvBuilder.append(resultSetMetaData.getColumnLabel(i)).append(",");
}
int rowLength = headersCsvBuilder.length();
headersCsvBuilder.replace(rowLength - 1, rowLength, "\n");
pipedOutputStream.write(headersCsvBuilder.toString().getBytes());
}
}
In order to test this, you need to execute http://localhost:8080/stream-demo/employees/3/cars

Replace url premeters to path based prameter

I have the following controller which accepts an optional "page" parameter.
Right now my URL would look something like mysite.com/pittsburgh-listings?page=2
I want the urls to look like this instead, how can I achieve this?
mysite.com/pittsburgh-listings/2
mysite.com/pittsburgh-listings/3
etc..
My controller
#Controller
public class CityController {
private static final int BUTTONS_TO_SHOW = 5;
private static final int INITIAL_PAGE = 0;
private static final int INITIAL_PAGE_SIZE = 40;
private static final int[] PAGE_SIZES = { 5, 10, 20, 40 };
private AdService adService;
public CityController(AdService adService) {
this.adService = adService;
}
#RequestMapping(value = "/{city:[\\w'-]+}-listings", method = RequestMethod.GET)
public String city(#PathVariable("city") String city, Model model, #RequestParam("page") Optional<Integer> page) {
Database db = new Database();
model.addAttribute("city", city.replace("-", " "));
System.out.println(city.replace("-", " "));
// List<Ad> ads = adService.getPage(1, city.replace("-", " "));
// model.addAttribute("ads", ads);
int evalPageSize = INITIAL_PAGE_SIZE;
int evalPage = (page.orElse(0) < 1) ? INITIAL_PAGE : page.get() - 1;
Long cityId = null;
try {
cityId = db.getCityId(city.replace("-", " "));
} catch (Exception e) {
e.printStackTrace();
}
Page<Ad> ads = adService.findAllPageable(new PageRequest(evalPage, evalPageSize, Sort.Direction.DESC, "id"),
cityId);
System.out.println("Ads: " + ads.getSize());
Pager pager = new Pager(ads.getTotalPages(), ads.getNumber(), BUTTONS_TO_SHOW);
model.addAttribute("ads", ads);
model.addAttribute("selectedPageSize", evalPageSize);
model.addAttribute("pageSizes", PAGE_SIZES);
model.addAttribute("pager", pager);
return "city";
}
}
You can change method signature as follows:
#RequestMapping(value = {"/{city:[\\w'-]+}-listings", "/{city:[\\w'-]+}-listings/{page}"}, method = RequestMethod.GET)
public String city(#PathVariable("city") String city, #PathVariable Optional<Integer> page, Model model) {
// ...
}
In order to map two endpoints (with and without page) in the same controller method and using Java 8 Optional to get page value.

Why I get "java.sql.SQLException: Invalid column index "?

I try to pass number into PrepareStmt, but get this error. I can't understant my problem.
Query:
private static final String SQL_FIND_ALL_CALENDARS = "SELECT * FROM calendar WHERE idCall > '?';";
Function:
private List<Calendar> findAll(Connection con) throws SQLException {
List<Calendar> calendar = new ArrayList<Calendar>();
PreparedStatement prsmt = null;
ResultSet rs = null;
try {
prsmt = con.prepareStatement(SQL_FIND_ALL_CALENDARS);
prsmt.setInt(1, TestOracleJDBC.idCall);
rs = prsmt.executeQuery();
while (rs.next()) {
Calendar calendar2 = extractCalendar(rs);
calendar.add(calendar2);
}
} catch (SQLException ex) {
ex.printStackTrace();
} finally {
rs.close();
prsmt.close();
}
return calendar;
}
In other class my parameter:
public static int idCall = 1;
Single quotes (') denote string literals in SQL. When using bind variables you shouldn't surround the ? with quotes - that would change it into a string literal with a question mark, not a placeholder for binding values.
Just remove it and you should be OK:
private static final String SQL_FIND_ALL_CALENDARS =
"SELECT * FROM calendar WHERE idCall > ?";
// Here -------------------------------^

ResultSet next() is returning only one row

I am having this method
public List<Course> getCourses() throws InvalidCourseDataException {
ArrayList<Course> allCoursesFromDB = new ArrayList<>();
Connection dbConnection = null;
String getFromTableSQL = "SELECT * FROM courses";
try {
dbConnection = getDBConnection();
statement = dbConnection.createStatement();
resultSet = statement.executeQuery(getFromTableSQL);
while (resultSet.next()) {
String courseCategory = resultSet.getString("course_category");
String courseTitle = resultSet.getString("course_title");
int courseId = resultSet.getInt("course_id");
Date startDate = resultSet.getTimestamp("starting_date");
Date endDate = resultSet.getDate("ending_date");
String description = resultSet.getString("description");
int teacherId = resultSet.getInt("teacher_id");
Course course = new Course(courseCategory, courseTitle, startDate, endDate);
course.setCourseId(courseId);
course.setTeacherId(teacherId);
course.setDescription(description);
addParticipantsIdToCourse(course);
allCoursesFromDB.add(course);
}
The getDBConnection() method is
private static Connection getDBConnection() {
System.out.println("-------- MySQL JDBC Connection Testing ------------");
Connection dbConnection = null;
try {
Class.forName(DB_DRIVER);
} catch (ClassNotFoundException e) {
System.out.println(e.getMessage());
}
try {
dbConnection = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
return dbConnection;
} catch (SQLException e) {
System.out.println(e.getMessage());
}
return dbConnection;
}
My problem is that resultSet.next() is returning only the first row from DB. I'am sure that I have multiple rows. I saw JDBC ResultSet is giving only one row although there are many rows in table? that question but it really doesn't answer my :)
I am sorry for the question. I found my mistake. ResultSet next() method is working fine, but I've changed its value in my addParticipantsIdToCourse(course) method :)

JDBC connectivity issue

I'm using the NetBeans IDE(6.8). I have a DB class :
package garits;
import java.io.Serializable;
import java.sql.*;
import java.util.Properties;
public class DB implements Serializable{
private static final long serialVersionUID = 1L;
String dbURL = "jdbc:mysql:///team_project";
String user = "root";
String pwd = "arsenal";
String dbDriver = "com.mysql.jdbc.Driver";
private Connection dbCon;
private ResultSet r;
private Statement s;
public DB()
{}
public boolean connect() throws ClassNotFoundException,SQLException{
Class.forName(dbDriver);
Properties props = new Properties();
props.put(user, "root");
props.put(pwd, "arsenal");
props.put("charSet", "UTF-8");
props.put("lc_ctype", "UTF-8");
dbCon = DriverManager.getConnection(dbURL,props);
//dbCon = DriverManager.getConnection(dbURL,user,pwd);
return true;
}
public void close() throws SQLException{
dbCon.close();
if(r!=null)
r.close();
if(s!=null)
s.close();
}
public ResultSet execSQL(String sql) throws SQLException{
s = dbCon.createStatement();
r = s.executeQuery(sql);
return (r == null) ? null : r;
}
public int updateSQL(String sql) throws SQLException{
s = dbCon.createStatement();
int r = s.executeUpdate(sql);
return (r == 0) ? 0 : r;
}
public int updateSQL(String sql, String getID) throws SQLException{
s = dbCon.createStatement();
int autoIncValue = -1;
s.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);
ResultSet rs = s.getGeneratedKeys();
if (rs.next()) {
autoIncValue = rs.getInt(1);
}
return autoIncValue;
}
}
The jar file is im my library, but whenever I try to connect:
private void loginButtonActionPerformed(java.awt.event.ActionEvent evt) {
String result ="";
DB db = new DB();
try{
db.connect();
String query = "Select Role From User_Account Where Username=jTextField1.getText()AND Where Password=jTextField2.getText(); ";
ResultSet rs=db.execSQL(query);
while(rs.next())
{
result = rs.getString("Role");
}
if(result.equals(""))
{
JOptionPane.showMessageDialog(loginButton,"Access denied","Error Message",JOptionPane.ERROR_MESSAGE);
}
else if(result.equals("Administrator"))
{
MainPage_Admin admin = new MainPage_Admin();
}
}
catch(Exception e)
{
System.out.println("An error has occurred");
}
}
I get an error(the exception is caught)-the name of the database is "team_project" and password is "arsenal"-any ideas appreciated. I'm new to JDBC.
First step: use at least e.printStackTrace() in your catch-block to get some information from the exception. Otherwise you'll just be guessing.
MySQL database url connection property is wrong
jdbc:mysql://localhost:3306/<your database name>
instead of you are giving
jdbc:mysql:///team_project
modify and execute the program and better to handle the exception within the try/catch block instead of throws.

Resources