2013年9月21日 星期六

MyBatis - 出現錯誤org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: Could not find resource jdbc.properties

需求:使用mybatis時出現以下錯誤:
org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.io.IOException: Could not find resource jdbc.properties




以下為我的mybatis-config檔案:

  
  
      
  
  
  

而我的jdbc.properties放在外部的config資料夾下(並非src package之下),因此產生這個錯誤,解決的方法是在eclipse中將此config資料夾加入classpath下:
打開project->properties設定
在build path的source中加入該config資料夾就可以了


2013/10/19更新:
上述方式若還是不行,有另一種方法如下圖,在欲加入的的folder按右鍵->Build Path->Use as Source Folder


2013年9月20日 星期五


參考:How To Set Environment Variables On Mac OS X新手問題, 關於.bash_profile 問題

需求:要在mac OS上設定環境變數

在win上設定環境變數是非常方便的事情,然而對於我這個大懶人以前也不熟linux的一換到mac上需要設定就得花點心思,幸好沒有很麻煩,以下已安裝gradle 1.7為範例:

  1. 將下載好的gradle解壓縮後置放於指定位置下,我放在Users/wuanne/tools之下,如下圖
  2. 然後呼叫出終端機,執行以下指令,確認gradle是未安裝的情況:

    目前環境變數並沒有包含gradle的路徑
     

  3. 所以執行gradle會顯示指令不存在  
  4. 再來我們要建立bash_profile,如果之前完全沒建立過則不會有這個檔案,先輸入

  5. touch .bash_profile
    $vim .bash_profile


  6. 然後建立以下內容(按小寫a進入編輯模式):

    輸入完畢後按下esc->wq->enter存檔離開
  7. 鍵入exit離開目前終端機
  8. 再次打開終端機,確認是否安裝成功:

    環境變數已經包含我們置放gradle的目錄位置,執行gradle也不再顯示command not found
  9. 安裝成功XD

2013年9月19日 星期四

如何在mac OS上可以存取android手機資訊,並且eclipse專案時可以使用實機測試

參考:Android Transfer

這兩天發狠換了mac(窮到快被鬼抓走了...0rz..)第一件事情就是要恢復所有工作環境,
差不多都快弄完了卻發現兩個問題:


  1. 無法連接到我的android手機抓取資料,只能充電
  2. eclipse跑android project時進行測試無法使用實機進行測試
後來看到這篇文章Android Transfer 下載Android File Transfer後就可以解決上述問題囉~

話說回來買了mac確在寫android實在很詭異....無奈現在實在沒心力再多去負擔學習IOS開發了...android都還摸不熟也很難抽出時間....唉~~~

mac超新手繼續移植回工作環境中....

2013年9月13日 星期五

Android - Eclipse啟動時出現錯誤 Please ensure that adb is correctly located at %ADB_FILE_PATH%



今天在run android專案時一直出現錯誤:
 Please ensure that adb is correctly located at %ADB_FILE_PATH%
重啟Eclipse、電腦、手機都沒有用,後來找到了解決方法如下:


  1. 打開cmd -> netstat -aon|findstr "5037",找出占用ADB Port的Service,如下圖我的是被7928這個PID的程式占掉了

  2. 工作管理員砍掉這個Service,砍掉PID==7928的

  3. 重新啟動eclipse
問題解決

2013年9月11日 星期三

JAVA Reflection:如何取得Class的Super Class、Interfaces..


參考:http://tutorials.jenkov.com/java-reflection/classes.html#superclass

需求:
最近開始研究Spring對於他使用XML可以做DI(Dependency injection)的方式產生了興趣,其中產生bean之後的constructor parameter居然也可以使用XML直接設定而不需要在application中指定id名稱的特性...想來想去也只有Reflection可以辦到了,因此有了以下的小實驗:
我們需要達到兩項目標:
  • 取得指定Class的Super Class
  • 取得指定Class的implement Interface(s)
直接看Code:

我們定義了兩個Interface:ISuperA、ISuperB
 
public interface ISuperA {
}
 
public interface ISuperB {
}

接下來定義implement:
 
public class ImplementClass implements ISuperA, ISuperB {
}

呼叫看結果:
 
 public static void main(String[] args) {
  Class superclass = ImplementClass.class.getSuperclass();
  Class[] interfaces = ImplementClass.class.getInterfaces();
  
  System.out.println("superclass:"+superclass.getName());
  
  System.out.println("interfaces:");
  for(Class interface1 : interfaces)
   System.out.print("\t"+interface1.getName());

 }

結果:
 
superclass:java.lang.Object
interfaces:
 ISuperA ISuperB


補充說明,推測Spring的用法:
下面的Code來自Spring in Action 3rd - Listing 1.4、1.6、1.7:
package com.springinaction.knights;

public classBraveKnightimplementsKnight{
  privateQuestquest;
  public BraveKnight(Questquest){
    this.quest=quest;
  }
  public voidembarkOnQuest()throwsQuestException{
    quest.embark();
  }
}

XML設定:
  
  
    
  

  
  

Spring中呼叫方式如下:
package com.springinaction.knights;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public classKnightMain{
   public staticvoidmain(String[]args){
      ApplicationContextcontext = new ClassPathXmlApplicationContext("knights.xml");
      Knightknight=(Knight)context.getBean("knight");
      knight.embarkOnQuest();
   }
}

依照上述呼叫的Code我們可以看到,指定了XML中的"knight" create...ok這沒甚麼問題,"knight"直接對應到XML的bean id="knight",然而有趣的事情在於,需create的BraveKnight並非是使用default constructor直接建立,是有帶參數的!然而在上述呼叫中卻沒有看到指定bean id="quest"的部分。僅僅是在bean中設定了<constructor-arg ref="quest"/>就對應的到....而quest代表的並非Type型別。
因此如果按照上述的實驗,推測是先取得id="quest"的"com.springinaction.knights.SlayDragonQuest",接者取得"com.springinaction.knights.BraveKnight"中的Constructor,由Reflection機制可以取得各種Constructor的參數型別,只要能找到"一個參數",這裡我們找到的為public BraveKnight(Questquest),因此只要SlayDragonQues符合以下條件之一,那麼XML的指定條件就可成立:

  1. SlayDragonQues的Type為Questquest
  2. Questquest為SlayDragonQuest的super class
  3. Questquest為Interface且被SlayDragonQuest implement
最後答案是3(未列出Questquest Code),因此XML設定正確,可以被順利creation :)

Eclipse - 出現Access restriction: The type XXXX is not accessible due to restriction on required library

參考:http://www.digizol.com/2008/09/eclipse-access-restriction-on-library.html
需求:最近承接一個舊系統翻新的案子,由於舊系統使用了 sun.net.ftp.FtpClient 然而被報錯誤:Access restriction: The type FtpClient is not accessible due to restriction on required library C:\Program Files\Java\jre7\lib\rt.jar



  1. Eclipse->window->Preferences
  2. Java->Compiler->Errors/Warings
  3. Deprecated and restricted API->Forbidden reference將原本下拉Error改為Warring

其實這種作法還真是鴕鳥...視而不見...= =

Mybatis - 推薦網站



  1. http://mybatis.github.io/mybatis-3/getting-started.html
    MyBatis官方網站,寫得很仔細
  2. http://www.sivalabs.in/2012/10/mybatis-tutorial-part1-crud-operations.html
    寫得還蠻清楚仔細,且也附上基本UnitTest的方式

2013年9月7日 星期六

MyBatis - Update Data


UserMapper.xml
  
 UPDATE UserData 
 SET
    money = #{_money}
 WHERE userid = #{_userid}
    

UserMapper.java
public interface UserDataMapper{
 public int updatUserMoney(UserData);
}


User.java
public class UserData {
 private String _userid;
 private int     _money;

...
}

Call
   SqlSessionFactoryBuilder sqlSessionFactory = new SqlSessionFactoryBuilder()
    .build(inputStream);
   SqlSession sqlSession = sqlSessionFactory.openSession();
   BaseDataMapper mapper = sqlSession.getMapper(BaseDataMapper.class);
   int updateCount = mapper.updatUserMoney(UserData);
   sqlSession.commit(true); //important!

這裡要注意的是有兩個地方:

  1. 如果需要接收Update後的row column count,記得必須把Mapper的回傳type設定為int,這樣子在呼叫的時候才有辦法讓mybatis回傳row count
  2. 因為會影響實際數據,所以記得要做session.commit(true)否則無法正常更新

MyBatis - 使用resultType回傳單一SELECT資料時都是Null


今天用Mybatis的時候發生一個問題,回傳的Data怎樣都是Null,確認SQL語法無任何問題,資料也都正常,查了半天才發現是我使用上的錯誤:


使用code如下:
UserMapper.xml
  < select id="getByUserId" parametertype="String" resulttype="UserData">
     SELECT *
     FROM UserData 
     WHERE USERID = #{xx}
  </ select>  

UserMapper.java
public interface UserDataMapper{
 public UserData getByUserId(String userId);
}


UserData.java
public class UserData {
 private String _userid;
 private int     _money;

...
}

但是不管我傳入甚麼userid,getByUserId(userid)永遠回傳Null,
查了好久才發現我使用方式錯誤,其實我們在UserMapper.xml中定義的<resultMap>
(resultMap使用請見MyBatis - 使用resultMap(1))
這種方式並不適用在使用resultType的時候,因此我們的UserMapper.xml得改為如下:
  < select id="getByUserId" parameterType="String" resultType="UserData" >
     SELECT userid  as _userid,
   money as _money
     FROM UserData
     WHERE USERID = #{xx}
  < /select>  

還是得乖乖用 sql field as java field name的方式一一指定而不能只是使用 SELECT * 否則就會發生一直都回傳Null的問題。
不過我也是mybatis的初學者,所以說不定有方式只是我還沒發現用法而已。

2013/10/19更新:

可以回傳*,mybatis設定欄位的方式為查找setter,setter的規則為setParamName
,因此假設SELECT * FROM TEST 其中一個欄位名稱為paramName,那麼只要對應的resultType擁有 setParamName這個名稱的setter,mybatis就會呼叫此setter將paramName的value丟入assign囉 (大小寫不限)

2013年9月4日 星期三

MyBatis - 範例與步驟總結




  1. 建立config檔案,用來設定連線DB的envirimont各種設定
    jdbc.properties:
     
    jdbc.driver=org.hsqldb.jdbcDriver
    jdbc.url=jdbc:hsqldb:mem:my-project-test;shutdown=true
    jdbc.username=sa
    jdbc.password=
    

    mybatis-config.xml:
      
    < configuration>
      
      
          
      
      
      
        
          
          
            
            
            
            
          
        
      
      
        
      
    </ configuration>
    
  2. 建立Mapper的interface,並定義所有會被用來呼叫的CRUD等functions, 之後會與Mapper.xml對應
    UserMapper.java:
    public interface UserMapper{
    
     public void insertUser(User user);
     public User getUserById(String userId);
     public List getAllUsers();
     public void updateUser(User user);
     public void deleteUser(String userId);
    
    }
    
  3. 建立mapper.xml,每一個id都需與出現在步驟2的interface functions名稱相同,並定義好每一個functions所需處理的SQL  Command
    UserMapper.xml
    < mapper namespace="cmpnts.UserMapper">
    
      < select id='getUserById' parameterType='String' resultType='User'>
         SELECT 
          userid as Userid, 
          email as Email , 
          Password, 
          name as Name
         FROM USER 
         WHERE USERID = #{userId}
      </ select>
    
    
      
       
       
      
    
      
      < select id="getAllUsers" resultMap="userMap">
        SELECT 
          userid , 
          email , 
          Password, 
          name
         FROM USER 
      </ select> 
    
      
        INSERT INTO USER(userid,email, password, name)
        VALUES(#{_userid},#{_email}, #{_password}, #{_name})
        
      
      
      
        UPDATE USER 
        SET
         PASSWORD = #{_password},
         NAME = #{_name},
         EMAIL = #{_email}
        WHERE USERID = #{_userid}
      
      
      
      
        DELETE FROM USER WHERE USERID = #{userid}
      
    </ mapper>
    
  4. 如果會使用比較複雜的資料型態,且有在步驟2、3時使用,記得要定義好這些資料型態
    User.java:
    public class User {
     private String _userid;
     private String _name;
     private String _password;
     private String _email;
    
     public String getUserId() {
      return _userid;
     }
    
     public void setUserId(String userid) {
      this._userid = userid;
     }
    
     public String getName() {
      return _name;
     }
    
     public void setName(String username) {
      this._name = username;
     }
    
     public String getPassword() {
      return _password;
     }
    
     public void setPassword(String pwd) {
      this._password = pwd;
     }
     
     public String getEmaill(){
      return _email;
     }
     
     public void setEmaill(String mail){
      _email = mail;
     }
     
     @Override
     public String toString(){
      StringBuffer buf = new StringBuffer();
      buf.append("UserId:").append(this.getUserId());
      buf.append("\r\n\tname:").append(this.getName());
      buf.append("\r\n\tpassword:").append(this.getPassword());
      buf.append("\r\n\temail:").append(this.getEmaill());
      return buf.toString();
     }
    }
    
  • 呼叫main.java:
    public class Main {
    
     /**
      * @param args
      */
     public static void main(String[] args) {
      String resource = "mybatis-config.xml";
      InputStream inputStream;
      try {
       inputStream = Resources.getResourceAsStream(resource);
       SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
         .build(inputStream);
    
       createTableUser();
       getAllUsers(sqlSessionFactory);
       
       //insert user
       User nuser = new User();
       nuser.setUserId("nuid");
       nuser.setPassword("nuserpwd");
       nuser.setName("rosie");
       nuser.setEmaill("rosie@mail.com.tw");
       insertUser(sqlSessionFactory,nuser);
       System.out.println("---After---");
       getAllUsers(sqlSessionFactory);
       
       User uuser = getUserById(sqlSessionFactory,"Q12");
       uuser.setEmaill("Q12update@mail.com.tw");
       uuser.setPassword("Q12updatePwd");
       uuser.setName("Q12updateName");
       updateUser(sqlSessionFactory,uuser);
       System.out.println("---UpdateAfter---");
       getAllUsers(sqlSessionFactory);
       
       deleteUser(sqlSessionFactory,"Q12");
       System.out.println("---DeleteAfter---");
       getAllUsers(sqlSessionFactory);
       
      } catch (Exception e) {
       e.printStackTrace();
      }
     }
     
     private static User getUserById(SqlSessionFactory sqlSessionFactory,String userid){
      return UserDAO.getUserById(sqlSessionFactory,userid); 
     }
     
     private static void updateUser(SqlSessionFactory sqlSessionFactory,User user){
      UserDAO.updateUser(sqlSessionFactory,user);
     }
     
     private static void deleteUser(SqlSessionFactory sqlSessionFactory, String userid) {
      UserDAO.deleteUser(sqlSessionFactory, userid);
     }
     
     private static void getAllUsers(SqlSessionFactory sqlSessionFactory){
      List list = UserDAO.getAllUsers(sqlSessionFactory);
      for(User user : list)
       System.out.println(user);
     }
     
     private static void insertUser(SqlSessionFactory sqlSessionFactory, User user) {
      UserDAO.insertUser(sqlSessionFactory, user);
     }
    
     public static void createTableUser() {
      try {
       // DBUnitUtilities.createTable(create);
       Connection conn = DBUnitUtilities.createHsqlDBConnection();
       SqlGetResultMain sqlmain = new SqlGetResultMain(conn);
       sqlmain.setIsAutoCommit(false);
       int update = sqlmain.update(new CreateUser());
    
       conn = DBUnitUtilities.createHsqlDBConnection();
       MsSqlConnection dbunitConnection = 
         new MsSqlConnection(conn, null);
       DBUnitUtilities.insertTableDataFromExistingXml("res/UserDatas.xml",
         dbunitConnection);
       
       sqlmain.commit();
      } catch (Exception ex) {
       ex.printStackTrace();
      }
     }
    }