Java in der Datenbank, für was es gut sein kann

und wie einfach es geht ...

Java in der Oracle Datenbank ist ja eigentlich ein alter Hut, das gibt es seit Oracle 8i aus dem Jahre 1999. Bisher habe ich es wenig benötigt, aber jetzt hatte ich wieder einen Anwendungsfall. Der Kunde benutzt für sein Berichtswesen nicht die normale ISO Kalenderwoche, welche am Montag beginnt, nein die Kalenderwoche hat partout am Sonntag zu beginnen. Der Blick in die Oracle Dokumentation bringt zutage, dass die Steuerung des ersten Wochentages einer Woche der NLS Einstellung NLS_TERRITORY (siehe hier) obliegt. Ich will aber nicht die ganzen anderen von diesem Parameter beeinflussten Werte mit umstellen, also musste eine andere Lösung gefunden werden, da to_char(t.datumsfeld, 'IW.IYYY') aus den oben genannten Grü nicht passt. Beim Grübeln fiel mir dann java.util.Calendar ein, da gibt es ja setMinimalDaysInFirstWeek( int value ) und auch setFirstDayOfWeek( int value ) das könnte ja möglicherweise passen. Also probierte ich es einfach einmal aus, erst einmal nur in Java.
package de.hehu.kw;

import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Calendar;
import java.util.GregorianCalendar;

public class CustKw {

	public static void main(String[] args) {
		// Jahreswechsel 2012/2013 probieren
		SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy");
		Date d = null;
		try {
			d = sdf.parse("28.12.2012");
			printCustKw_Kw(d);
			d = sdf.parse("29.12.2012");
			printCustKw_Kw(d);
			d = sdf.parse("30.12.2012");
			printCustKw_Kw(d);
			d = sdf.parse("31.12.2012");
			printCustKw_Kw(d);
			d = sdf.parse("01.01.2013");
			printCustKw_Kw(d);
			d = sdf.parse("02.01.2013");
			printCustKw_Kw(d);
			d = sdf.parse("03.01.2013");
			printCustKw_Kw(d);
			d = sdf.parse("04.01.2013");
			printCustKw_Kw(d);
			d = sdf.parse("05.01.2013");
			printCustKw_Kw(d);
			d = sdf.parse("06.01.2013");
			printCustKw_Kw(d);
			d = sdf.parse("07.01.2013");
			printCustKw_Kw(d);
		} catch (Exception ex) {
			ex.printStackTrace();
		}		
	}

	public static void printCustKw_Kw(Date inDate) {
		DecimalFormat df = new DecimalFormat("00");
		SimpleDateFormat sdf = new SimpleDateFormat("E dd.MM.yyyy");
		if (inDate != null) {
			try {
				GregorianCalendar cal_start = new GregorianCalendar();
				cal_start.setMinimalDaysInFirstWeek(4);
				cal_start.setFirstDayOfWeek(GregorianCalendar.SUNDAY);
				cal_start.setTime(inDate);
				// welche KW haben wir denn?
				int week = cal_start.get(Calendar.WEEK_OF_YEAR);
				int year = cal_start.get(Calendar.YEAR);
				// wenn bereits die Woche 1 aber der Monat noch auf Dezember
				// steht
				if (week == 1 && cal_start.get(Calendar.MONTH) == 11) {
					// das Jahr eins weiterzaehlen
					year = year + 1;
				}
				System.out.println("Datum " + sdf.format(inDate)
						+ " ist nach Kundenlogik in KW / Year "
						+ df.format(week) + " / " + year);
			} catch (Exception ex) {
				ex.printStackTrace();
			}
		}
	}

}
      			
Der Test mit dem Jahreswechsel 2012/2013 zeigt, es scheint zu funktionieren. Jetzt muss der Java Code nur noch in die Datenbank hinein und aus dem SQL heraus ansprechbar gemacht werden. Das ist jetzt ganz einfach. Erst einmal wird die Java Klasse in der Datenbank angelegt.
CREATE OR REPLACE AND COMPILE JAVA SOURCE NAMED "de.hehu.kw.ToCharKW" AS

package de.hehu.kw;

import java.text.DecimalFormat;
import java.util.Date;
import java.util.Calendar;
import java.util.GregorianCalendar;

// die Klasse muss in die Oracle-DB geladen werden
// nur so kann die kundeneigene KW Logik umgesetzt werden

public class ToCharKW {
	// einmal fuer die Kalenderwoche
	public static String getCustKw_Kw(java.sql.Date inDate) {
		DecimalFormat df = new DecimalFormat("00");
		String ret = "n/a";
		if (inDate != null) {
			try {
				// Datumsvariable erstellen
				Date now = new Date(inDate.getTime());
				GregorianCalendar cal_start = new GregorianCalendar();
				cal_start.setMinimalDaysInFirstWeek(4);
				cal_start.setFirstDayOfWeek(GregorianCalendar.SUNDAY);
				cal_start.setTime(now);

				int week = cal_start.get(Calendar.WEEK_OF_YEAR);
				ret = df.format(week);
			} catch (Exception ex) {
				ex.printStackTrace();
			}
		}
		return ret;
	}
	
	// einmal fuer das Jahr
	public static String getCustKw_Year(java.sql.Date inDate) {
		DecimalFormat df = new DecimalFormat("0000");
		String ret = "n/a";
		if (inDate != null) {
			try {
				// Datumsvariable erstellen
				Date now = new Date(inDate.getTime());
				GregorianCalendar cal_start = new GregorianCalendar();
				cal_start.setMinimalDaysInFirstWeek(4);
				cal_start.setFirstDayOfWeek(GregorianCalendar.SUNDAY);
				cal_start.setTime(now);

				int week = cal_start.get(Calendar.WEEK_OF_YEAR);
				int year = cal_start.get(Calendar.YEAR);
				// wenn bereits erste Woche aber der Monat auf Dezember steht
				if (week == 1 && cal_start.get(Calendar.MONTH) == 11) {
					// das Jahr eins weiterzaehlen
					year = year + 1;
				}
				ret = df.format(year);
			} catch (Exception ex) {
				ex.printStackTrace();
			}
		}
		return ret;
	}
}		
/
      		
Jetzt fehlen noch die PL/SQL Wrapper Funktionen, welche den Java Code aus SQL heraus ansprechbar machen
CREATE OR REPLACE FUNCTION cust_to_char_kw(p_datum IN DATE) RETURN VARCHAR2 AS
LANGUAGE JAVA NAME 'de.hehu.kw.ToCharKW.getCustKw_Kw(java.sql.Date) return java.lang.String';
/

CREATE OR REPLACE FUNCTION cust_to_char_year(p_datum IN DATE) RETURN VARCHAR2 AS
LANGUAGE JAVA NAME 'de.hehu.kw.ToCharKW.getCustKw_Year(java.sql.Date) return java.lang.String';
/      
      		
Ein Test in SQL-Plus liefert dann die endlich die gewünschten Ergebnisse.
select cust_to_char_kw(to_date('29.12.2012','DD.MM.YYYY')) cust_kw, cust_to_char_year(to_date('29.12.2012','DD.MM.YYYY')) cust_year from dual;
select cust_to_char_kw(to_date('30.12.2012','DD.MM.YYYY')) cust_kw, cust_to_char_year(to_date('30.12.2012','DD.MM.YYYY')) cust_year from dual;
select cust_to_char_kw(to_date('31.12.2012','DD.MM.YYYY')) cust_kw, cust_to_char_year(to_date('31.12.2012','DD.MM.YYYY')) cust_year from dual;			
				

10.03.2013