2015年10月27日 星期二

Google Compute Engine 試用感想(更新)


更新: google cloud 兩個月到期了XD...

google cloud 到試用到期(60天)後,會自動把VM shutdown。然後 release  你的IP...
但VM內容資料是有被保留下來的。
你要在事先先升級帳號(準備付銀彈),應該就會保留下來~

優點是:不會自動跟你收錢。
缺點是:IP被搶走了Orz...還得跑去把domain name 重定一輪就是...若是原本就是上線的系統,要注意....IP跑了可是非常麻煩~~

至於EC2是,試用到期(一年),並不會自動幫你關VM...反正就是直接開始扣錢~
所以想要一毛不拔,要記得自己上去關機VM。

至於目前,打算就先付費用google cloud一陣子吧。

-------------------------------------------------------------------------------------
Google Compute Engine 就差不多相對於 Amazon EC2...

跑去試用了一下下~就說一下簡單的感想~

帳號:(兩邊差不多)
Google 那邊需要一個 Google 帳號~ EC2 是要一個 email 帳號~
兩邊都要 key 信用卡才可以啟用~


介面:(Google勝)
Google 介面 中文化程度算高的(有八成以上吧)!當然文件還是英文的~
另外Google 介面我覺得順蠻多的~還有做網頁link到 ssl,console,還蠻新鮮的體驗~~
除了網頁可以access,還有gcloud這種東西~(但我覺得好難用=.=a,最簡單的就是在網頁上的 ssl sudo處裡好後,之後用putty看起來比較親切)


免費試用:(看人)
Google 免費試用目前是~免費 兩個月(部份vm,服務)裡面可用額度最高 300美元~
EC2 還是那個最迷你的vm,免費一年,還有一些服務試用~
(因為我主要都是用vm,iaas的服務,其它的沒在試)

如果是有點想要租的人,可以用google,可以先試用到不少功能(阿就兩個月而已),可以讓你先估計,如果你想要租xx加xx,一個月會花多少錢~現在大概搞懂為啥是兩個月了~因為至少會有一個 足月~

如果還是希望完全免費的話,就還是用慢慢的EC2 吧,有一年時間。
(但是真的很慢阿Orz...)


費用:(看人吧,我只用免費的)
這邊不得不要說一下,amazon好像有簡體的費用說明~google 連到的還是英文的~
原則兩邊都有不同的地區(美國/歐洲/亞洲,兩邊都有),價格也都不同~
但是 amazon的地區 比Google多了一些點~

Google 有個特別的 最高減免30% 計算,就是一整個月的第一周沒優惠,連開到第二~四周優惠x,y,z%,總之就是如果「Server 連開一整個月」可以得到最多的優惠。(希望你開了就不要停機降子)

Amazon 之前看比較優惠的就是 預存實例,比較像「預付卡」那樣,先買多少時數(by機型),然後在 一年/三年 內用完。

當然如果沒有搞到優惠的情況下,個人覺得挺貴的一u一a...但在有優惠的時後~就看實際情況了~

另外~頻寬、固定IP、加掛硬碟等等,都是額外計費的~只是因為個人搞不到這種大事業~所以沒認真比過就是O_oa...


VM Image:(Google有純正的centos耶)
EC2標榜他們有幾百個instance 的image,不過~老實說,當年在那邊翻簡直是大海撈針Orz...
另外有要版權的VM...兩邊都會收額外的費用~

話說,不知道是不是我的錯覺,Google上搞 MS 的OS,貴得有點離譜...不知道是不是故意的@@a...(雖說因為租不起高級server,現在也都不選MS那種吃效能的怪物就是)
EC2上之前讓偶不太高興的是,有redhat,卻沒有centos,是怕自己人打自己人嗎=_=...但他自己特別提供的linex vm..我覺得lib不是很齊~網路資源沒centos多...
所以看到Google 上寫著好好的 Centos 6...就開起來用了XD...

Google 比較特別是,他有挑一些特定用途的配套VM+Image+硬碟,讓一般人在選擇時比較容易入手。不過他們家推的底層是 那幾間 不認識的合作廠商~(試開了svn 配套vm,裡面掛的是 debian+Bitnami,開是很方便沒錯啦,但是完全不熟設定運作。而且資訊是散在那個論壇裡,不好入門,最後還是覺得專心搞centos就好)


結論就是~總之先用了再說:)...

2015年10月18日 星期日

SimpleDateFormat parse wrong ??!!

過了這麼多年,第一次發現原來 SimpleDateFormat 是會表人的XD...
先說愛用是 jdk 1.6.0_33...

總之寫個範例就知道了Orz...

        SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd");
       
        try  {
            System.out.println(sf.parse("20151010"));
            System.out.println(sf.parse("2015-10-10"));
            System.out.println(sf.parse("20-15-10-10"));
        }  catch(Exception e)  {
            e.printStackTrace();
        }

結果居然...
Sat Oct 10 00:00:00 CST 2015
Fri Oct 31 00:00:00 CST 2014
Sun Nov 05 00:00:00 CST 19

只有第一個是對的,但...居然都沒進 Exception...
如果前端輸入沒擋好,後端就要GG了~

是說解法也還還好~但真說都沒想到過,就是~
sf.setLenient(false);

但是為啥 jdk 不讓他預設就是 false就沒事了Orz...

2015年10月17日 星期六

spring4 @ResponseBody json date format

預設的 @ResponseBody  的日期型態 在 json 時是回傳 時間long值~

然後想改一下,就突然發現事情沒有想得那麼簡單Orz...

先講最簡單也是最基礎的寫法如下~
使用 MappingJacksonHttpMessageConverter
但是~他不知道在那一版就被 @deprecated 掉~猜大概是 3.x吧~
然後要講清楚,spring 的 json 是用 Jackson 的,若是使用 MappingJacksonHttpMessageConverter,就要搭配 jackson 1.x 版使用...jar檔大概是 jackson-core-asl-1.8.0.jar, jackson-mapper-asl-1.8.0.jar 之類的...

    <!-- messageConverters beans -->   
    <beans:bean id="stringHttpMessageConverter"
        class="org.springframework.http.converter.StringHttpMessageConverter">
    </beans:bean>
   
    <!-- orgin for @ResponseBody json converter use jackson 1.x  -->
    <beans:bean id="mappingJacksonHttpMessageConverter"
        class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
    </beans:bean>
   
    <!--
        AnnotationMethodHandlerAdapter messageConverters for @ResponseBody
    -->
    <beans:bean
        class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <beans:property name="messageConverters">
            <beans:list>
                <beans:ref bean="stringHttpMessageConverter" />
                <beans:ref bean="mappingJacksonHttpMessageConverter" />
            </beans:list>
        </beans:property>
    </beans:bean>


為了趕上時代,所以....=.=b,就決定使用新的 converter...
MappingJackson2HttpMessageConverter
很微妙的多了個 2...Google網路上有許多寫法,但...貼上去都會死XD...最後總算試出一套可以用Orz...
在此前提,要記得先抓 Jackson 2.x 的 jar檔(好像release到2.6+了),然後他們官網居然不放 binary 的jar...就去jar檔網站下載了比較快Orz...其實2.x版很多喔~不過就第一眼google到是 2.2的就可以動...要注意的是~他 2.x 包的 jar 分類跟 1.x 不一樣了!有三個~
jackson-annotations-2.2.4.jar
jackson-core-2.2.2.jar
jackson-databind-2.2.4.jar

然後spring 的設定也要改囉~從網路上貼來的,再加一點點中式英文註解XD...
(這套的設定要 spring 3.2+ 才能的樣子, xsd 有不一樣)
這個的mvc 還多寫了好幾道,算是很方便@@~

    <!-- messageConverters beans -->   
    <beans:bean id="stringHttpMessageConverter"
        class="org.springframework.http.converter.StringHttpMessageConverter">
    </beans:bean>
    <!--
        AnnotationMethodHandlerAdapter messageConverters for @ResponseBody
    -->
    <beans:bean
        class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <beans:property name="messageConverters">
            <beans:list>
                <beans:ref bean="stringHttpMessageConverter" />
                <beans:ref bean="acmJacksonConverter" />
            </beans:list>
        </beans:property>
    </beans:bean>
   
    <!-- @ResponseBody json Date format needs jackson-core/databind/annoation.jar 2.x -->
    <!-- set JSON date format to ISO-8601 e.g. 1970-01-01T00:00:00.000+0000 -->
    <beans:bean id="sourceObjectMapper" class="com.fasterxml.jackson.databind.ObjectMapper"/>
    <beans:bean id="acmObjectMapper" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <beans:property name="targetObject" ref="sourceObjectMapper"/>
        <beans:property name="targetMethod" value="disable"/>
        <beans:property name="arguments" value="WRITE_DATES_AS_TIMESTAMPS"/>
    </beans:bean>
    <beans:bean id="acmJacksonConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
        <beans:property name="objectMapper" ref="acmObjectMapper"/>
    </beans:bean>
   
    <mvc:annotation-driven>
        <mvc:message-converters>
            <!-- We configure the Jackson mapper to output dates in ISO801 format. This requires adding our
            customized Jackson mapper to the list of Spring MVC message converters. But, if we just add our bean here
            all by itself, it will handle requests it should not handle, e.g. encoding strings.  So we need to add the
            other standard message converters here too, and make sure to put the customized Jackson converter AFTER the
            string converter. -->
   
            <beans:bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
            <beans:bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"/>
            <beans:bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
            <beans:bean class="org.springframework.http.converter.ResourceHttpMessageConverter"/>
            <beans:bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"/>
            <beans:ref bean="acmJacksonConverter"/>
            <beans:bean class="org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter"/>
            <!-- atom feed requires com.sun.syndication package ...   -->
            <!--<bean class="org.springframework.http.converter.feed.AtomFeedHttpMessageConverter"/>-->
            <beans:bean class="org.springframework.http.converter.BufferedImageHttpMessageConverter"/>
            <beans:bean class="org.springframework.http.converter.FormHttpMessageConverter"/>
            <beans:bean class="org.springframework.http.converter.xml.Jaxb2CollectionHttpMessageConverter"/>
            <!-- marshalling converter requires spring oxm -->
            <!--<bean class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter"/>-->
        </mvc:message-converters>
    </mvc:annotation-driven>

在此他 default 的 date format 就會變成 1970-01-01T00:00:00.000+0000...
若是需要 特別指定 某vo 裡面的 date指定~ 也很方便~只要直接加 annotation..
import com.fasterxml.jackson.annotation.JsonFormat;

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
    private Date birthDate;

雖然把這套件加進去有點麻煩~但對開發上來說還蠻好用的~~

2015年10月13日 星期二

java/tomcat find class file path

之前好像有寫過用 當下的 class 找他的實體檔案路徑在那~
不過當然這都在一個前提下~ 該 class/resource 沒有被包在 jar 檔裡~
(在jar檔裡的話~阿就通通zip在一起了,那來的各別路徑Orz...)

所以以下一律以散的class來講~
一般正常來說,取該 class/或是resource/甚至只是要個檔案路徑,可以用這種寫法~
String javaPath = ClassLoader.getSystemResource(".").getPath();

但是在 tomcat 下, SystemResource是被改寫過的(會null)~不過用另一招可以再拿到~
String libPath =  this.getClass().getClassLoader().getResource(".").getPath();
--> 這個會拿到 tomcat lib 的那個 path...WEB-INF/lib/

String javaPath =  this.getClass().getClassLoader().getResource("log4j.properties").getPath();
--> 是個爛解~不過這樣就可以拿到 WEB-INF/classes/ 的實體路徑
(你不會說你不用 log4j 吧!!)

String somewherePath =  this.getClass().getClassLoader().getResource("test/files").getPath();
--> 如果你有建個該建的資料夾在那是可以拿到  WEB-INF/classes/test/files/

老實說~用ClassLoader這個蠻容易被裱的...

不過通常大家若在 webapps 下,是可以用 ServletContext 來取得 webapps/xxx/ 的那個實體路徑...

但是我想通用於一般跟在 tomcat 下的情況~目前是這樣寫著來用~
          URL ul = ClassLoader.getSystemResource(MY_LOACTION);
               if(ul == null)  {
                    //tomcat or other webapps has no SystemResource
                    path = this.getClass().getClassLoader().getResource(MY_LOACTION).getPath();
                    //System.out.println(this.getClass().getClassLoader().getResource(".").getPath());
                    //System.out.println(this.getClass().getClassLoader().getResource("log4j.properties").getPath());
                }  else  {
                    path = ul.getPath();
                }


2015年10月8日 星期四

spring4 + mybatis3 sample (簡易版) - mybatis with mapper config

寫設定檔應該是比較多人早期的用法~
(ibatis也是寫設定檔,然後跟 mybatis ...長得很像,但有微妙的不同!!)

首先他的 mapper xml resource 會定在主要的 mybatis-config 裡
      <mappers>
          <mapper resource="xxx/xxx/xxx/xxx/sample/SampleConfigDao.xml"/>
      </mappers>

然後在其對應的路徑下就是要有這麼一個 xml。
namespace 要 對應到 其 interface 的 class name
/WEB-INF/classes/xxx/xxx/xxx/xxx/sample/SampleConfigDao.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="xxx.xxx.xxx.xxx.sample.SampleConfigDao" >
  
    <select id="selectSampleConfig" parameterType="java.lang.String" resultType="java.util.HashMap">
        SELECT * FROM aa where name = #{name}
    </select>
  
</mapper>


所以對應的dao(mapper)要跟 xml 對應到~ namesapce,method name,input/output
/WEB-INF/classes/xxx/xxx/xxx/xxx/sample/SampleConfigDao.java

import java.util.*;
import org.springframework.stereotype.Repository;

@Repository
public interface SampleConfigDao {
   
    public List<HashMap> selectSampleConfig(String name);
}


和 annotation 的差別在於, annoatation 可以直接把 xml的內容,寫在 java (interface) 內,省了兩個檔~
但是 annoation 有個問題就是,總體要看到這系統用了幾個檔,就不太容易看~只能靠search...
寫設定檔~是很囉唆~加個(改個)功能,要動到 3隻 以上的程式 (mybatis-config.xml,xxx ineterface,xxx.xml)

總之軟體就是沒有100分的解~這樣工程師才不會失業阿Orz...

spring4 + mybatis3 sample (簡易版) - mybatis annotation

因為現在 annotation 很紅~
所以就試了一下~真的超簡短~~~
不過重點在於前面的設定要搞好....

複習 在 spring bean 要設定:
<bean id="myBatisMapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
       <property name="basePackage" value="xxx.xxx.xxx.xxx"/>
       <property name="annotationClass" value="org.springframework.stereotype.Repository"/>
    </bean>

SampleDao

import java.util.*;
import org.springframework.stereotype.Repository;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

@Repository
public interface SampleDao {
   
    @Select("SELECT * FROM aa")
    public List<HashMap> selectSample();
}

spring4 + mybatis3 sample (簡易版) - spring controller/service

基本上還是 spring 的 controller 和 service...一般般

Controller/Action

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.apache.log4j.Logger;

//import javax.servlet.http.HttpServletRequest;

import java.util.*;

@Controller
public class SampleController {

    Logger logg = Logger.getLogger(this.getClass());
    String logId = "log";
    String logName = "log";

    @Autowired
    SampleService theService;

    @RequestMapping(value={"/test/list"}, method = RequestMethod.GET)
    public @ResponseBody List fn1(Model model){
        System.out.println("??enter controller");
        return theService.test1();
    }
   
    @RequestMapping(value={"/test/list2"}, method = RequestMethod.GET)
    public @ResponseBody List fn2(Model model){
        System.out.println("??enter list2");
        return theService.test2();
    }
}


Service

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service
public class SampleService {

    @Autowired
    SampleDao dao = null;
   
    @Autowired
    SampleConfigDao dao2 = null;

    @Transactional
    public List test1()  {
        System.out.println("??enter service.test1");
        return dao.selectSample();
    }
   
    @Transactional
    public List test2()  {
        System.out.println("??enter test2");
        return dao2.selectSampleConfig("ABC");
    }
}

spring4 + mybatis3 sample (簡易版) - 系統設定檔

想說 spring4 之前寫過,然後 ibatis 也寫了幾個~
那 spring4 + mybatis3 應該沒啥問題 (mybatis 以前是 ibatis)...
然後~就發現被表了Orz...

總之~還是湊了一個超簡易的版本~

jar lib,就是 spring4 的那一拖拉庫,
加上 mybatis-3-xx.jar(要找一下,他github是放 source,直接 build 好 release 的 jar 後來是在別的地方找到的)。
還有一個  mybatis-spring-xx.jar...
另外因為後端我習慣接 postgresql...但早期的 jdbc driver(8.4) 在此會發生 setQueryTimeOut method not implemented 的問題,總之~換成新的 driver(9.4) 就可以解掉(但是在那版才加的就不清楚)

首先講一下我的檔案位置

webapps/demo/WEB-INF/web.xml
                                         /classes
                                         /classes/mybatis/mybatis-config.xml     <-- mybatis 主要設定檔
                                         /classes/**/*Dao.xml        <-- mapper 內容的檔案
                                         /conf             <--  spring 設定檔區
                                         /lib

設定檔內容:
/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <display-name>anping</display-name>
    <!-- The definition of the Root Spring Container shared by all Servlets
        and Filters -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/conf/root-context.xml</param-value>
    </context-param>

    <!-- Sets the default profile to use in the absence of any profiles set
        at deployment time. -->
    <context-param>
        <param-name>spring.profile.default</param-name>
        <param-value>dev</param-value>
    </context-param>

    <!-- Creates the Spring Container shared by all Servlets and Filters -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

   
    <!-- Handles requests into the application -->   
    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/conf/servlet-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
   
       
    <security-constraint>
        <web-resource-collection>
            <url-pattern>/*</url-pattern>
            <http-method>PUT</http-method>
            <http-method>DELETE</http-method>
            <http-method>HEAD</http-method>
            <http-method>OPTIONS</http-method>
            <http-method>TRACE</http-method>
        </web-resource-collection>
        <auth-constraint></auth-constraint>
    </security-constraint>
</web-app>


Spring 主檔(application.xml)
/WEB-INF/root-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
                        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <!-- Root Context: defines shared resources accessible to all other web
        components -->

    <!-- Turn on support for @Annotation-based configuration e.g. @Inject -->
    <context:annotation-config />

    <!-- Loads application properties -->
    <import resource="properties.xml" />
   
    <!-- Loads controller/component -->
    <import resource="servlet-context.xml" />
   
    <!-- Loads model(db) -->
    <import resource="data.xml" />
       
</beans>



servlet-context.xml、properties.xml 就spring 用的一般般沒啥問題~
主要差在 與 DB model 的設定
/WEB-INF/conf/data.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util"
    xmlns:ctx="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

   
   <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName">
            <value>org.postgresql.Driver</value>           
        </property>
        <property name="url">           
            <value>jdbc:postgresql://127.0.0.1:5432/db1?charSet=utf-8</value>
        </property>
        <property name="username">
            <value>xxxxxxx</value>
        </property>
        <property name="password">
            <value>xxxxxxx</value>           
        </property>
        <property name="timeBetweenEvictionRunsMillis" value="300000" />
        <property name="numTestsPerEvictionRun" value="6" />
        <property name="minEvictableIdleTimeMillis" value="1800000" />
        <property name="initialSize" value="3" />
        <property name="maxActive" value="100" />
        <property name="maxIdle" value="10" />
        <property name="minIdle" value="1"/>
        <property name="maxWait" value="5000" />       
        <property name="poolPreparedStatements" value="true" />
        <property name="maxOpenPreparedStatements" value="100" />
    </bean>
   
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation" value="classpath:mybatis/mybatis-config.xml" />
    </bean>
   
    <bean id="myBatisMapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
       <property name="basePackage" value="xxx.xxx.xxx.xxx"/>
       <property name="annotationClass" value="org.springframework.stereotype.Repository"/>
    </bean>
   
    <tx:annotation-driven transaction-manager="transactionManager"/>

    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean> 
   
   
</beans>


重點在  sqlSessionFactory 要指向 dataSource 和  configLocation
然後因為我要利用 annoation 的寫法~所以有加 myBatisMapperScanner
basePackage 必填, 但他不同於 spring 可以 ** 的用法,好像~只要寫入 上幾層的 pacakge就行了(跟 log4j 的 package 制定法較像)
annotationClass ,一般通用就是 spring 的 Repository
總合來說,就是~ class 有  @Repository 且 在  basePackage下 的就會被自動掃進來~

mybatis 的主設定檔
是說原本想把他搬去跟 conf 同一層,但是google了一下,有人說~把這個檔放在外面是有什麼理由嗎!?
後來我想想也是~因為他裡面還是會因需要一直加 mapper resourse 進來~那些內容也得跟程式對應。在 src 底下是比較好維護~(conf一般都是指系統架構整體用的 config,的確是不要去搞一個客制的比較好)
/WEB-INF/classes/mybatis/mybatis-config.xml 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="cacheEnabled" value="true" />
        <setting name="lazyLoadingEnabled" value="true" />
        <setting name="multipleResultSetsEnabled" value="true" />
        <setting name="useColumnLabel" value="true" />
        <setting name="defaultExecutorType" value="REUSE" />
        <setting name="defaultStatementTimeout" value="25000" />
    </settings>

    <mappers>
          <mapper resource="xxx/xxx/xxx/xxx/sample/SampleConfigDao.xml"/>
      </mappers>
</configuration>


後續~


2015年10月2日 星期五

oracle function 日期 流水號

這次要用 oracle function 來實作 日期流水號~(每天從1開始跑)
以前都習慣用AP實作,不過因為這邊是多台又同時,會造成號碼重覆的問題~
聽說改了幾次寫法還是衝突,最後只好來試試...唯一的瓶頸-->就是那台DB...

我不太知道英文的關鍵字要怎麼下~中翻英去找,結果都蠻慘的~
不過用中文的話~打「oracle 日期 流水號」...就可以看到不少的參考~

不過我說是參考...因為照貼結果感覺都很慘就是XD...

這次的解法,是用DB 開 table 來記(不是做DB 的流水號)
因為覺得去reset seq 是一件很蠢的事情~感覺上也不符合seq的精神....

table,主要的欄位就是「日期」,跟「現在號碼」,為了通用,再多加個「流水號類別 」...
原理就是~指定類別,若日期內有號碼,就把號碼+1丟出。若table沒有那個日期,就丟1,insert一筆新的日期,和號碼1進去。
(理論上比較好的想法是降子,不過後來因為 ooxx的考量...我是不用insert,而是日期不同,就把原本的資料蓋成 【新的日期】,跟把號碼變成1。變形後可以減少筆數~當然是前端 AP的日期不能亂丟就是~若在跨日其實是會出事的XD,不過要是這事情不會發生就算了~)

----修正版~
後來想一想,還是改個寫法~先撈值出來看一下日期再做處理~

CREATE OR REPLACE FUNCTION FN_XXXX_SEQ(P_CTYPE IN VARCHAR2, P_DT IN VARCHAR2)
  RETURN VARCHAR2 IS V_SEQ VARCHAR2(10);
 
BEGIN
  BEGIN
    Select l.DT INTO P_DT From OWNER.TB_AAA l where l.TYPE = P_TYPE ;  
    if P_DT is not null and P_DT >= to_char(SYSDATE,'YYYYMMDD') then 
        UPDATE OWNER.TB_AAA S
           SET S.SN = LPAD(to_number(S.VALUE2)+1, 4, '0')
        WHERE S.TYPE = P_CTYPE
         RETURNING substr(S.DT, 3) || S.SN INTO V_SEQ;
   else
         UPDATE OWNER.TB_AAA S
         SET S.SN = '0001',
             S.DT = to_char(SYSDATE,'YYYYMMDD')
       WHERE S.TYPE = P_CTYPE
       RETURNING substr(S.DT, 3) || S.SN INTO V_SEQ;
    end if;
 
  END;

  COMMIT;
 
  RETURN V_SEQ;
END FN_XXXX_SEQ;

function實作上,基本上就是,你來我就先update 一次,然後把 序號 給到 V_SEQ內。
P_DT會先暫存 DB目前的日期(想做個Varible,一直GG..就算了Orz..),然後看是要用舊的資料,還是改新值給1...(日期若比資料庫小的,一律就以資料庫的時間為主~)

然後都寫成 function了,那就組好流水號格式送出去,AP端不再加工了~

雖說 oracle 對數字format 也有其他的寫法,不過 LPAD,好像比較正確~

另外,原本是要採用 EXCEPTION的判斷寫入(感覺得這個才比較正統)
但是~不知道為什麼~就是一直都掉不進  NO_DATA_FOUND
https://docs.oracle.com/cd/B10500_01/appdev.920/a96624/07_errs.htm
換了好幾個Exception也都不是...

事後,寫了個多Thread,模擬壓測的程式狂取號~似乎寫到function內,沒有重覆號的問題~原本想說不行就要DB lock(但是那個太危險了),但目前看起來是OK的樣子就是~

java call oracle funtion

一般常用都是用 PreparedStatement 來處理 SQL...
不過如果要 Call procedure 或 function的話,就要用其他的寫法~
我這邊是用 CallableStatement 來處理

另外,看了一下網路資源,似乎不同家的DB,下法也不一樣~總之這邊用oracle的~

           Connection conn = getDataSource().getConnection();
           
            CallableStatement cs = null;
            String callsql = "{? = call FN_XXXXX(?,?) }";
            cs = conn.prepareCall(callsql);
            cs.registerOutParameter(1, java.sql.Types.VARCHAR);
            cs.setString(2, "AAA");
            cs.setString(3, "BBB");
            cs.execute();

            re = cstamt.getString(1);

總之就是output取值,要用 registerOutParameter 來放,執行後再取值出來~

在oracle裡面,似乎將這些東西分得很多類  function/package/procedure....
但是雖然我覺得都是差不多的東西阿一u一a...好啦~有的是整包,有的是單一功能~等等~
java來 call,寫法都差不多,會差在 語法怎麼下的差別就是~

(但是最難的還是去擠出那個DB Function的語法,sql語法很難debug阿阿阿阿阿~)