Posts Tagged ‘ Java

Custom Hibernate Sequence and JPA ID generator

หัวข้อนี้ก็ยังหนีไม่พ้น Hibernate อีกตามเคยจากหัวข้อที่แล้ว เรื่องการสร้าง custom order by หัวข้อนี้กลับมาดูเรื่องการ Custom การสร้าง ID และ Sequence แบบฉบับตามใจคนใช้งาน
เริ่มแรกก็ขอคววามช่วยเหลือจาก google ตามเคย หลังจากสอบถามก็ได้ข้อมูลมาสองที่ดังนี้
Customize IdGenerator in JPA, gap between Hibernate and JPA annotations และ
Custom Hibernate Sequence Generator for id field
เมื่อได้แหล่งข้อมูลตัวอย่างแล้วก็นำมาปรับแก้ดังนี้

/*
 * ----------------------------------------------------------------------------
 * Copyright © 2014 by http://www.secondknow.com. All rights reserved.
 * ----------------------------------------------------------------------------
 */
package com.secondknow.hibernate.seq;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.exception.JDBCExceptionHelper;
import org.hibernate.id.SequenceGenerator;
import org.hibernate.type.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Supot Saelao
 * @version 1.0
 */
public class OracleSeqWithDateFormatGenerator extends SequenceGenerator {
	private static final Logger logger = LoggerFactory.getLogger(OracleSeqWithDateFormatGenerator.class);
	
	private String sql;
	
	@Override
	public void configure(Type type, Properties params, Dialect dialect)
			throws MappingException {
		super.configure(type, params, dialect);
		
		String sequenceName = " TO_CHAR(SYSDATE,'YYYYMMDDHH24MISS') || " + getSequenceName();
		sql = dialect.getSequenceNextValString(sequenceName);
	}

	@Override
	public Serializable generate(SessionImplementor session, Object obj)
			throws HibernateException {
		return generateSeqStr(session);
	}
	
	private String generateSeqStr(SessionImplementor session) {
		try {
			logger.info("Seq statement : {}", sql);
			
			PreparedStatement st = session.getBatcher().prepareSelectStatement(sql);
			try {
				ResultSet rs = st.executeQuery();
				try {
					rs.next();
					String seqStr = rs.getString(1);
					logger.info("Seq value : {}", seqStr);
					
					return seqStr;
				} finally {
					rs.close();
				}
			} finally {
				session.getBatcher().closeStatement(st);
			}
		} catch (SQLException sqle) {
			throw JDBCExceptionHelper.convert(session.getFactory()
					.getSQLExceptionConverter(), sqle, "could not get next sequence value", sql);
		}
	}	
}

Read more

How to create custom order by in hibernate Criteria API

สืบเนื่องจากการใช้งาน Hibernate กับฐานข้อมูล oracle แล้วเจอปัญหาเวลาทำการเรียงลำดับข้อมูล (Order by) แล้วภาษาไทยจะเรียงไม่ถูกใจผู้ใช้งาน เช่น
การเรียงข้อมูลจังหวัด

กรุงเทพ
เชียงราย
เชียงใหม่
พะเยา
อ่างทอง

ข้อมูลจะถูกเรียงลำดับดังนี้

กรุงเทพ
พะเยา
อ่างทอง
เชียงราย
เชียงใหม่

ถ้าต้องการเรียงให้ถูกต้องตามหลักภาษาไทย (ภาษาไทยเทพเกิน) ต้องเพิ่ม function NLSSORT เข้าไปด้วยดังนี้

SELECT PROVINCE_NAME 
FROM CONF_PROVINCE
ORDER BY NLSSORT(PROVINCE_NAME, 'NLS_SORT = THAI_DICTIONARY');

ทุกอย่างเหมือนไม่มีอะไรซับซ้อนแต่ปัญหาก็คือ ระบบใช้ Hibernate และก็ Qurey ผ่าน Hibernate Criteria หลังจากสอบถาม google ก็ไปเจอกับแนวทางดังนี้
Read more

True Wifi Auto Login (โปรแกรมสำหรับ Login เข้าใช้ True Wifi หลังหมดเวลา)

ได้มีโอกาสอันดีในการมาสมัครใช้ True Wifi แบบรายเดือนเพราะสถานที่ทำงานไม่มี อินเตอร์เน็ตให้ใช้ ดังนั้นเลยต้องไป ซื้อ SIM True และจัดการสมัคร package 100 ใช้ Wifi แบบไม่จำกัด แต่ปัญหาที่เจอก็คือ ระบบจะตัดสัญญาเมื่อครบกำหนด 90 นาที และต้องทำการ Login ใหม่ซึ่งถ้าใช้งานทั่วไปก็คงไม่มีปัญหาอะไรแต่ถ้าเกิดกำลังดูหนังฟังเพลงกันอยู่ จะทำให้อารมณ์ค้างไม่ใช่เล่น ก็เลยเกิดความคิดจะเขียน script หรือ โปรแกรมอะไรขึ้นมาเพื่อทำให้ทำการต่ออินเตอร์เน็ตเองหลังจากใกล้หมดเวลา ประมาณ นาทีที่ 88, 89 พอลองค้นหาจาก Google ดูแล้วเหมือนไม่เจอโปรแกรมลักษณะนี้ เจอแต่ script ที่ต้องเปิดหน้า browserเอาไว้ (หรือหาไม่เป็นก็ไม่รู้ เลยไม่เจอ) ก็เลยคิดว่า เอาแบบนั้นก็เขียนเองละกัน

1. ขั้นแรกไปศึกษาการ ส่งค่าไปให้ระบบสำหรับทำการ login ก่อน พบว่า จากหน้าจอ login ข้อมูลที่ถูกส่งไปคือ

username==ชื่อผู้ใช้งาน
password=รหัสผ่าน
param=ค่า MAC Address +"|"+ปี+เดือน+วัน+ชั่วโมง+นาที ที่ทำการเข้าระบบ ทำการ เข้ารหัสด้วย base64

ตัวอย่างค่า param

MDA6Mjc6MTA6OTk6YjI6NmN8MjAxMzEwMTgxMzA3Cg==

จากนั้นมาลองเขียน Java โดยทำการ post ค่าดังกล่าวไปในส่วนของการ Login แต่จนแล้วจนรอดก็ไม่ได้ ทั้งๆที่การเข้าหรัสถูกต้องหมด แต่ถ้าเอาค่าจากใน browser มาทดสอบส่งไปสามารถ login ได้ปกติ เลยคิดเอาเองว่า ในขั้นตอนการ generate param นั้นอาจมีการเก็บค่าเพื่อทำการอ้างอิงและตรวจสอบกับค่าที่ส่งจากหน้าจอ ถ้าไม่ตรงกันก็ไม่สามารถเข้าระบบได้

พอสันนิษฐานดังกล่าวก็เปลี่ยนแนวคิด ดังนี้
1. เปิด Connection ไปที่ http://www.google.com ก่อน รอให้ระบบส่งเราเข้าไปยังหน้า Login เอง
2. จากนั้นก็ไป Copy ค่า param จากหน้าจอดังกล่าวมา โดยไม่ต้อง generate เอง
3. ส่งข้อมูลไปทำการ Login

หลังจากทดสอบก็สามารถทำงานได้ตามปกติ ไม่มีเวลามากเลยทดสอบเล็กๆน้อยๆ และเขียนออกมาแค่นี้ หากมีเวลาค่อยไปไล่ปรับปรุง เพิ่มเติม

Download
Java Version (Windows)
.NET Windows Form version (Update 20/10/2013)

Note: ติดปัญหา และต้องการแนะนำเพิ่มเติม ติดต่อได้ที่หน้า Readme

RichFaces : Component ID _viewRoot:status has already been found in the view.

ทดลองใช้ RichFaces แล้วต้องการให้แสดงสถานะแจ้งว่ากำลังทำงานอยู่ (Process Dialog) ซึ่งใน RichFaces นั้นมี Tag

<a4j:status />

ให้ใช้งานอยู่แล้ว เลยทดลองดังนี้

<a4j:status onstart="{Richfaces.showModalPanel('process-wait')};"
    onstop="{Richfaces.hideModalPanel('process-wait')};" />
<rich:modalPanel id="process-wait" width="250" height="100">
	<f:facet name="header">
		<h:outputText value="Processing" />
	</f:facet>
	<div align="center">
		<h:outputText value="Processing please wait..." /> <br>
	</div>
</rich:modalPanel>

พอทดลองรันแล้วเกิด Error ดังนี้

java.lang.IllegalStateException: Component ID _viewRoot:status has already been found in the view.  
	at com.sun.faces.util.Util.checkIdUniqueness(Util.java:724)
	at com.sun.faces.util.Util.checkIdUniqueness(Util.java:708)
	at com.sun.faces.application.StateManagerImpl.saveView(StateManagerImpl.java:129)
	at javax.faces.application.StateManager.saveSerializedView(StateManager.java:183)
	-
	-

Error ที่เกิดขึ้นเนื่องจากในหน้าจอ หรือ page นี้มีการใช้ tag จะไม่สามารถใช้ได้อีก วิธีการแก้ไข
1. ลบ Tag ที่ซ้ำออกแล้วสามารถใช้งานได้เลย
2. manual เปิด dialog เองในปุ่มที่ต้องการให้แสดง เช่น

<a4j:commandButton id="bt-action"
	value="Save" action="#{example.save}"
	onclick="{Richfaces.showModalPanel('process-wait')};"
	oncomplete="{Richfaces.hideModalPanel('process-wait')};" />

Update : 08/05/2012 สามารถใช้ tag หลายๆตัวในหน้าเดียวกันอ้างอิงจาก RichFaces Status โดยระบุ attribute “for” และกำหนด tag ครอบส่วนที่ต้องการให้เรียกใช้ เช่น

<a4j:region id="xx-yy">
     <a4j:commandButton id="bt-action"
	 value="Save" action="#{example.save}" />
</a4j:region>

<a4j:status for="xx-yy" onstart="{Richfaces.showModalPanel('id-here')};"
	onstop="{Richfaces.hideModalPanel('id-here')};" />

[Java]MessageFormat : insert values between single quotes

หลังจากได้แนะนำการใช้ การจัด Format Message ก็ใช้งานมาเรื่อยจนวันนี้ ติดปัญหาเล็กน้อยในการ ใช้ MessageFormat สำหรับ format ข้อความที่มีเครื่องหมาย ” (single quotes) เนื่องจากหลังจาก format แล้วเครื่องหมาย ” จะหายไปโดยปริยาย เช่น

String msg = "Hi, '{0}' this is {1}";
String val = MessageFormat.format(msg, "test", "xx");
System.out.println(val);

ข้อความที่ได้จะกลายเป็น Hi, test this is xx
ด้วยความช่วยเหลือจาก อ.google ก็พบวิธีแก้ไขดังนี้

String msg = "Hi, ''{0}'' this is {1}";
String val = MessageFormat.format(msg, "test", "xx");
System.out.println(val);

โดยการเพิ่ม ” ครอบ ” อีกทีหนึ่ง

ลอกมาจาก
Java MessageFormat – How can I insert values between single quotes?