顯示具有 3rd 標籤的文章。 顯示所有文章
顯示具有 3rd 標籤的文章。 顯示所有文章

2013年10月6日 星期日

opencsv - 使用CsvToBean.parse()總是無法成功


參考致上述網址code如下:
 
ColumnPositionMappingStrategy strat = new ColumnPositionMappingStrategy();
strat.setType(Country.class);
String[] columns = new String[] {"countryName", "capital"}; // the fields to bind do in your JavaBean
strat.setColumnMapping(columns);
 
CsvToBean csv = new CsvToBean();
 
String csvFilename = "C:\\sample.csv";
CSVReader csvReader = new CSVReader(new FileReader(csvFilename));
 
List list = csv.parse(strat, csvReader);
for (Object object : list) {
    Country country = (Country) object;
    System.out.println(country.getCapital());
}
 

package net.viralpatel.java;
 
public class Country {
    private String countryName;
    private String capital;
 
    public String getCountryName() {
        return countryName;
    }
 
    public void setCountryName(String countryName) {
        this.countryName = countryName;
    }
 
    public String getCapital() {
        return capital;
    }
 
    public void setCapital(String capital) {
        this.capital = capital;
    }
}

以上Country class為inner class
但執行以上code時csv.parse() (LINE11)都會報錯:
java.lang.RuntimeException: Error parsing CSV!

仔細追了一下opencsv source發現,會在MappingStrategy.createBean()時發生
InstantiationException的錯誤,試驗一下發現原來inner class使用reflection的newInstance()都會出現這問題

因為opencsv中的parse會直接使用newInstance() create new instance,因此使用的TYPE不可為INNER CLASS
至於有沒有可以使用inner class的方式要再查查看

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();
      }
     }
    }
    
    
  • 2013年8月25日 星期日

    MyBatis - 如何一次取得SELECT 的大量資料

    參考:Result Maps
    需求:我們常常會有SELECT Query回傳大量資料列的情形,與之前指定userid只回傳單一User不同,該如何做呢?

    方式很簡單,首先我們新增一個新的function getAllUsers:


  • UserMapper.xml
    
     
      
       
       
      
    
    
      < select id="getAllUsers" resultMap="userMap">
        SELECT 
          userid , 
          email , 
          Password, 
          name
         FROM USER 
     < select>
    
    

    (原有getUserById在這邊先拿掉)請注意其中我們使用的屬性為resultMap而不是先前getUserById中使用的resultType,依照官方說明,resultMap與resultType只能選其中一個使用


  • UserDAO.java
    public class UserDAO {
       //...
        public static List getAllUsers(SqlSessionFactory sqlSessionFactory) {
     SqlSession sqlSession = sqlSessionFactory.openSession();
     try {
      UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
      return userMapper.getAllUsers();
     } finally {
      sqlSession.close();
     }
        }
    }
    

    跟之前使用getUserById()沒甚麼差異,唯一差別就是回傳型態變成了 List<user>
  • 呼叫方式如下:
     private static void getAllUsers(SqlSessionFactory sqlSessionFactory){
      List list = UserDAO.getAllUsers(sqlSessionFactory);
      for(User user : list)
       System.out.println(user);
     }
    
  • 使用方式非常簡單呢,比起以往自己寫JDBC API為了接收大量資料寫了一大堆Code又不好用,ResultMap的機制真的非常簡易又方便呢!

    MyBatis - 使用resultMap(1)


    這這一篇MyBatis - 基本安裝 + 使用Select有提過,mybatis使用了reflection的機制對應Field Name將DataBase的資訊映射至指定Class Data(User.class)中,那麼,如果像我習慣把class field name前面加上 _ 或 m_ 的識別符號,如下:
    public class User {
     private String _userid;
     private String _name;
     private String _password;
     private String _email;
    

    但是在SELECT Query依然維持:
         SELECT 
          userid as userid, 
          email as email , 
          password, 
          name as Name
         FROM USER 
    

    該怎麼做呢?

    需求: 我們需要一次回傳大量的SELECT資料,該怎麼做呢?

    這時候就要使用resultMap的機制了,我們將先前的UserMapper.xml更改為如下:
    
    
      < select id='getUserById' parameterType='String' resultType='User'>
         SELECT 
          userid as Userid, 
          email as Email , 
          Password, 
          name as Name
         FROM USER 
         WHERE USERID = #{userId}
      </ select>
    
    
      
       
       
      
    
      
    
    

    在這裡,我們加上了< resultmap></ resultmap>的設定,依照上面設定很容易可以看得出來,
    column代表的是SELECT 出來的 field name,而對應的property代表的就是User Class中的field name,如此一來,利用這個方式就不需要名字一定得相同囉。


    2013/9/7補充:
      此種方式只適用一次回傳大量資料的resultMap,並不適用SELECT單一資料的resultType,
    如果有需要,還是得乖乖指定回傳fieldName,詳情請見MyBatis - 使用resultType回傳單一SELECT資料時都是Null

    MyBatis - 基本安裝 + 使用Select

    1. 於eclipse建立New Project【TestMyBatis】
    2. eclipse中的BuildPath加入mybatis-xxxx.jar(我安裝這時候為mybatis-3.2.2.jar)
    3. 建立res資料夾,這個資料夾會存放所有mybatis的config、mapper...等XML設定
    4. 於eclipse的Build Path加入res路徑(Add Folder->選擇res資料夾)

    5. 於res下加入jdbc.properties,這是讓我們設定jdbc資料庫連線的各種設定;這邊我們使用hsqldb,方便測試使用:
      jdbc.driver=org.hsqldb.jdbcDriver
      jdbc.url=jdbc:hsqldb:mem:my-project-test;shutdown=true
      jdbc.username=sa
      jdbc.password=
      
    6. 於res下加入mybatis-config.xml
      
        
        
          
            
            
              
              
              
              
            
          
        
        
          
        
      
      

      這是主要讓mybatis設定環境連線參數的設定檔
    7. 於res下加入UserMapper.xml
      
        < select id="getUserById" parametertype="String" resulttype="cmpnts.User">
           SELECT 
            userid as userid, 
            email as email , 
            password, 
            name as Name
           FROM USER 
           WHERE USERID = #{userId}
        </ select>
      
      

      (以上select開頭的tag空格請自行刪除,因為不用空格會被Blogger判定為HTML select)
      因為這篇只做SELECT因此以上設定檔只列出SELECT,可以看到SQL的語法直接寫在Mapper設定中就好了!非常的方便! 甚至若有邏輯上判斷的需求也可以直接寫在這,詳細請看這Dynamic SQL
    8. 建立package cmpnts,並加入User Class
      package cmpnts;
      
      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;
       }
       
       @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();
       }
      }
      
    9. 加入UserMapper Class

      package cmpnts;
      import java.util.List;
      
      public interface UserMapper{
       //public void insertUser(User user);
       public User getUserById(String userId);
       //public List getAllUsers();
       //public void updateUser(User user);
       //public void deleteUser(Integer userId);
      }
      

      這是我們的UserMapper Interface,由於這篇我們只會使用SELECT,因此實際上使用到的只有getUserById(String userId);


      到這邊準備工作都完成了,可以開始呼叫測試看結果了。
    10. 呼叫方式如下:
      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();
                      
           User user = UserDAO.getUserById(sqlSessionFactory,"Q011");
           System.out.println(user);
      
        } catch (Exception e) {
           e.printStackTrace();
        }
      }
      

      其中的createTableUser()我使用HSQLDB來建立Table,由於跟MyBatis並無關聯因此就不列出了, 習慣使用其他DataBase的直接依照一般習慣建立Table使用即可。(記得上面的jdbc.properties也要更改為自行使用的DataBase設定)
    11. UserDAO.java
      public static User getUserById(SqlSessionFactory sqlSessionFactory, String userid) {
         SqlSession sqlSession = sqlSessionFactory.openSession();
         try {
       UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
       return userMapper.getUserById(userid);
         } finally {
       sqlSession.close();
         }
      }
      

      • 首先我們傳入在main中建立的SqlSessionFactory
      • 使用SqlSessionFactory建立SqlSession 
      • 利用SqlSession獲得UserMapper的對應
      • 呼叫 getUserById(),取得我們要的結果User
      • 關於各種mybatis物件的life time請參考Getting started下面的Scope and Lifecycle
    12. 以下就是main呼叫後取得的User Println()結果
      UserId:Q011
       name:Q011anne
       password:annepwd123
       email:anne@mail.piwi.com.tw
      

    以上看起來很神奇,其實說穿了就是使用Java Reflection的機制來做的(請見Java Reflection - 存取Field 資訊);
    實際上測試了一下,他是直接對應field name;也就是說在User.java中的各種field name: userid、name、password、email都要能與在UserMapper.xml中寫下的SELECT Query相符(大小寫不限):
    SELECT 
          userid as Userid, 
          email as Email , 
          password, 
          name as Name
         FROM USER 
         WHERE USERID = #{userId}

    而與method name(getter、setter)沒有關聯。

    2013/9/7補充:
       上述的SELECT語法,若沒有使用ResultMap一次傳回大量資料(請見此篇MyBatis - 使用resultMap、resultType自行定義回傳資料)則務必要使用如上述的 userid as userid 這種方式才有辦法對應的到,後面的 as userid,一定要使用回傳Java Type的field name,比如如果習慣field name使用_usierid、_eamil,就一定要寫成
    SELECT 
          userid as _userid, 
          email as _email, 
          password as _password, 
          name as _name
         FROM USER 
         WHERE USERID = #{userId}
    否則怎麼回傳都會是Null Data,倒是 #{userId}使用甚麼名稱都無所謂,使用 #{xx}也是可以的,應該是只要param順序對應到就可以

    但是,如果像我Class field都習慣前面加上 _name、_userid這種方式,DB的field又不能修改成這樣,該怎麼辦呢? 這時候可以藉助好用的resultMap機制達到喔,請見此篇MyBatis - 使用resultMap、resultType自行定義回傳資料

    2013年7月10日 星期三

    android初體驗 - 如何將lib打包為.jar後還能存取到其中的activity與resource


    繼上一篇的使用Lib
    前情提要:
    最近因為案子需要開始接觸android,完全沒碰過android的我只能硬者頭皮上架,第一個就遇到問題:
    因為我的lib有activity,所以如果直接做成.jar供Client呼叫,如下方式:

     
    Intent intent = new Intent(v.getContext(),  LibMainActivity.class);
    intent.putExtra("userid", "breeze");
    intent.putExtra("facextra", "server_1");
    startActivity(intent);
    

    會在執行時出現 classdefnotfound exception!
    後來找了一下文章,官方提到以下面的方式使用lib:(看友方(?)的使用文件也是一樣方式)
    Setting up a Library Project

    但很明顯的..這個等於完全將我的垃圾Code 曝露給其他人看到了啊!!(雖然說直接給jar照樣能反組...但至少沒那麼蠢....0rz..)

    然後後來看到facebook sdk也是一樣這樣做法,心想可能真的就得如此做了吧,再加上隔天就要交差實在找不到解法的情況下,第二天只好這樣子交給廠商了,結果......

    第二天廠商表明他們使用U3D開發,不是純android project,所以無法這樣include Lib,但是因為我真的不懂要怎麼弄成jar還能順利將activity成功啟動...因此他就自己做掉了,厚者臉皮詢問下他跟我說只要將R.id.xxx的resource拿掉改成如下方式:

     
     public static int getLayoutId(Context ctx,String paramString){
      return ctx.getResources().getIdentifier(paramString, "layout", ctx.getPackageName());
     }
     
    
    就可以了,我照者試做,的確把所有的R.id.xxx的引用點改掉,原先為:

     
    TextView text = (TextView) findViewById(R.id.noticeText);
    

    改為:
     
      int rid = ResourceUtil.getId(LibMainActivity.this,"noticeText");
      TextView text = (TextView) findViewById(rid);
    

    的確可以運作成功,可是!!!打包成.jar一樣會有錯誤阿!!!
    找了半天終於找到解答了,簡單說明就是(明明打了很多廢話還簡單說明...)

    1. 將所有R.id.xxx的引用方式改為上述使用
    2. 所有資源檔的名字不能重複:例如預設的activity_main.xml就最好不要使用,因為使用的client也有可能很偷懶用了預設名稱
    3. 將所有lib會使用到的activity在Client的AndroidManifest.xml中也一併宣告,請參考下方的Declaring library components in the manifest file
    4. lib打包成jar時,除了選擇src以外其他都不能選擇
    5. 將打包好的jar置放(貼)於client project的 libs/ 底下
    6. 將所有會引用到的resource一併手動複製貼到client端的resource對應位置(資料夾下),例如res/layout/...
    所以這麼一來雖然每次release時都要給出lib.jar與resource,但的確感覺比直接給出整個lib project要來的好得多了。
    詳細圖文請參考Android将Activity打成jar包供第三方调用(解决资源文件不能打包的问题)

    p.s剛剛才發現這篇文章是2013/7/6發布的耶!!!今天是2013/7/10,我真要感謝他剛好在我有問題前幾天發文...