ラベル java の投稿を表示しています。 すべての投稿を表示
ラベル java の投稿を表示しています。 すべての投稿を表示

2012年5月30日水曜日

ScalaからAWS SDK for Javaを使ってDynamoDBをいじったTips

AWS SDK for Javaのバージョンは、1.3.10です。
使ってみた時に詰まった点をメモってみました。


1. ResourceNotFoundExceptionが出る原因


以下のような例外が発生する場合の原因と対処法です。

[error] (run-main) Status Code: 400, AWS Service: AmazonDynamoDB, AWS Request ID: QSQDQO1RMAIDU4S2G6QNCR7S3JVV4KQNSO5AEMVJF66Q9ASUAAJG, AWS Error Code: ResourceNotFoundException, AWS Error Message: Requested resource not found
Status Code: 400, AWS Service: AmazonDynamoDB, AWS Request ID: QSQDQO1RMAIDU4S2G6QNCR7S3JVV4KQNSO5AEMVJF66Q9ASUAAJG, AWS Error Code: ResourceNotFoundException,AWS Error Message: Requested resource not found

原因1 Endpointが設定されていない

val client : AmazonDynamoDBClient = ...// init
client.setEndpont("http://dynamodb.ap-northeast-1.amazonaws.com"); // <- 東京RegionのEndpoint設定。
自分のDynamoDBを使用しているRegionを設定しましょう。Endpoingの一覧はこちら

原因2 Tableが作成されていない

指定したTableが存在しない場合にも同じ例外が発生します。Tableが正常に作成されているかと、プログラム中でTable名の指定が間違ってないかを確認しましょう。
Tableがまだ作成されていない場合は、ManagedConsoleから作成してあげましょう。


2. ValidationExceptionが出る原因


以下のような例外が発生する場合の原因と対処法です。
[error] (run-main) Status Code: 400, AWS Service: AmazonDynamoDB, AWS Request ID: HE5SG08RJAMBQQUBJ031DRGRTVVV4KQNSO5AEMVJF66Q9ASUAAJG, AWS Error Code: ValidationException, AWS Error Message: The provided key element does not match the schema
Status Code: 400, AWS Service: AmazonDynamoDB, AWS Request ID: HE5SG08RJAMBQQUBJ031DRGRTVVV4KQNSO5AEMVJF66Q9ASUAAJG, AWS Error Code: ValidationException, AWS Error Message: The provided key element does not match the schema

原因1 HashKeyまたはRangeKeyの型が違う


HashKeyとRangeKeyの型が間違っている場合に良く起こります。 Table定義を確認して、StringとNumber型の指定が正しいことを確認してください。

new AttributeValue().withS("hogehoge") //Table定義ではNumberになっている場合コレが原因になる



3. O/R mapperを使ってRangeKeyにDate型を指定したけど、queryで上手くフィルターできない


O/R mapperでDateを使用する場合は、DynamoDB上ではISO8601の日付フォーマットの文字列として保存されます。
import com.amazonaws.util.DateUtils

DateUtils dateUtils = new DateUtils();

dateUtils.formatIso8601Date(date)
dateUtils.parseIso8601Date(dateString)

で、変換できます。queryやscan時のConditionで指定する場合は、このメソッドを使いましょう。


4. ScalaからAWS SDKのO/R mapperを使う


Java Beansにしか対応していないので、通常のScalaPlainObjectでは上手く認識してくれません。
基本的には、@BeansInfoアノテーションをつければ、getter/setterを生成してくれるので変数の定義に関しては問題ありません。
しかし、varにつけたアノテーションは、コンパイルされるタイミングで、同名のprivate fieldのほうに付けれられてしまいます。
そのため、AWS SDKのアノテーションが有効にならないので、地道に手作業でgetter/setterを作成してアノテーションをつけないと行けません。
また、case classにしておくと勝手にequalsやhashCode関数をオーバーライドしてくれるので便利ですが、引数なしのコンストラクタもあわせて定義しておいてあげましょう。

NG

import scala.reflect.BeanProperty
import com.amazonaws.services.dynamodb.datamodeling._

case class Hoge( @BeansInfo @DynamoDBHashKey(attributeName="Id") var id : Long,
 @BeansInfo var nickname : String){
  def this() = this(0,"")
}

OK

import scala.reflect.BeanProperty
import com.amazonaws.services.dynamodb.datamodeling._

case class Hoge(var id : Long,
 @BeansInfo var nickname : String){
  def this() = this(0,"")

  @DynamoDBHashKey(attributeName="Id")
  def getId() : Long = id
  def setId(id : Long) = this.id = id;
}



5. O/R Mapper時にクラスの継承を行うと変数が上手くマップされない


幾つかのテーブルが同じようなテーブルで同じ変数持ってるから、継承で共通化しちゃえと思って継承を使ってみたところ、上手くO/R Mapperがフィールドをマップしてくれませんでした。
回避方法は、「継承元のクラスにDynamoDBTableアノテーションを付ける」になります。

NG

class Hoge(... , var hoge : String){
  def this() = this(...) 
  ...
   def getHoge():String = hoge
   def setHoge( hoge : String){ this.hoge = hoge}
}
@DynamoDBTable(tableName="FugaTable")
class Fuga extends Hoge

OK

@DynamoDBTable(tableName="DummyAnnotation") //<- これ重要
class Hoge ...


@DynamoDBTable(tableName="FugaTable")
class Fuga extends Hoge

ちなみに、こうなっちゃう原因はDynamoDBRefrector@isRelevantGetter(DynamoDBRefrector.java:92)の
private boolean isRelevantGetter(Method m) {
    return (m.getName().startsWith("get") || m.getName().startsWith("is")) && m.getParameterTypes().length == 0
        && m.getDeclaringClass().getAnnotation(DynamoDBTable.class) != null
        && !m.isAnnotationPresent(DynamoDBIgnore.class);
}
で、メソッドのDeclaringClassのアノテーションを取ってしまっちゃってるからです。

2012年1月23日月曜日

JDBCで使えるURL一覧

JDBCに指定するURLとDriverClassのまとめ




DB名DriverClass種別url
MySQLcom.mysql.jdbc.Driverサーバーjdbc:mysql://[server_name]:[port=3306]/[database_name]
H2DBorg.h2.Driverサーバーjdbc:h2:tcp://[server]:[port]/[path]/[database_name]
ファイルjdbc:h2:file:[path][database_name]
メモリjdbc:h2:mem:[database_name]
Oracleoracle.jdbc.driver.OracleDriverサーバーjdbc:oracle:thin:@[server_name]:[port=1521]:[database_name]
Microsoft SQL servercom.microsoft.jdbc.sqlserver.SQLServerDriveサーバーjdbc:microsoft:sqlserver://[server_name]:[port=1433]/[database_name]; DatabaseName=[database_name]
Derbyorg.apache.derby.jdbc.EmbeddedDriverファイルjdbc:derby:[path]/[database_name];create=true
メモリjdbc:derby:memory:[database_name]
PostgreSQLorg.postgresql.Driverサーバーjdbc:postgresql://[server_name]:[port=5432]/[database_name]
Sqliteorg.sqlite.JDBCファイルjdbc:sqlite:[path]/[database_name]
org.sqlite.JDBC

2011年9月22日木曜日

Javaのクラスに同名、同引数のメソッドが定義される件

javaの仕様?と思われますが、ある特定の条件下でリフレクションを使用した場合、困ったことになります。

例えば、このようなクラスを定義します。

scala

trait FirstDef[T]{
def id : T
}

class Conc extends FirstDef[Long]{
def id = 23L
}


java

interfaceFirstDef{
public T id();
}

class Conc implements FirstDef{
public Long id() = 23L
}

そして、ConcクラスをReflectionにかけると・・・


> classOf[Conc].getMethods(println(_))

public long Conc.id()
public java.lang.Object Conc.id()
public final native void java.lang.Object.wait(long) th
public final void java.lang.Object.wait() throws java.l
public final void java.lang.Object.wait(long,int) throw
public boolean java.lang.Object.equals(java.lang.Object
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.ge
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()



なんと、引数なしのid()メソッドが2つ定義されてしまいます!!!
しかし、戻り値はlongとjava.lang.Object。
Reflectionをがりがり使うライブラリを使用している場合、高確率でバグが出ちゃいます。

これは、継承しているTrait(Interface)の型テンプレートを実現するためと思われ、回避不可能です。Reflectionを使用するライブラリを作る場合はこのようなことがあるということに気をつけましょう。

2010年5月12日水曜日

JavaのString.formatいろいろ

String.formatによる書式指定覚書

基本

String.format("本日は%sなり%s","晴天","!");
//本日は晴天なり!



進数を変える
//16進数
String.format("%x",12);
//c
String.format("%X",12);
//C

//8進数
String.format("%o",12);
//14


桁区切りにカンマ
String.format("%,d",10000);
//return 10,000


桁数を合わせて、足りない桁は0詰め
String.format("%04d",1);
//0001
String.format("%04d",23);
//0023


小数を指定の小数点以下の桁まで表示する
String.format("%.2f",0.1);
String.format("%.2f",0.0002);
String.format("%.2f",100.0);
//0.10
//0.00
//100.00


インデックスを明示
String.format("%2$s %1$s %2$s","1番","2番");
//2番 1番 2番

2009年12月3日木曜日

ThreadPoolとThreadLocalを併用する際の注意

ThreadLocalは便利だけど実は落とし穴ががが

TomcatなどのThreadPoolを使用する場合、同一のThreadが使いまわされるため、
ThreadLocalの値も引き継がれてしまうという現象が起き、それがもとでエラーが発生することが。。。

以下その検証コード

public static void main(String[] args){

System.out.println("hoge");


threadLocal = new ThreadLocal();
executor = java.util.concurrent.Executors.newFixedThreadPool(3);
for(int i = 0;i < 10;i++)
{
executor.execute(new Runnable() {

@Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String value = threadLocal.get();
if(value == null){
value = new Random().nextInt() + "";
threadLocal.set(value);
}

System.out.println(value);

}
});


}

}
}





結果として、全て違う値が表示されることが期待されるが、
実際は同じ値がなんども表示される。
そのため、Threadの終了時に

ThreadLocal.remove();

を明示的に呼び出すようにしてやることをお勧めします。

2009年8月31日月曜日

JUnit assertThat用のBetweenクラスを作ってみた

JUnitのassertThatで有効範囲を指定してテストを行うためのクラスを作った

使い方は


Assert.assertThat(2,Between.between(1,4));



みたいな感じ


以下コード



import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
/**
* Comparableなものをが特定の範囲内の値であるかどうかを判定する
* @author takeshita
*
*/
public class Between extends BaseMatcher<Comparable>{

/**
* lower <= value <= upper
* @param lowerLimit
* @param upperLimit
* @return
*/
public static Between between(Comparable lowerLimit ,Comparable upperLimit)
{
return between_UE_LE(lowerLimit,upperLimit);
}
/**
* lower < value < upper
* @param lowerLimit
* @param upperLimit
* @return
*/
public static Between between_UT_LT(Comparable lowerLimit ,Comparable upperLimit)
{
return new Between(lowerLimit,upperLimit,0,0);
}
/**
* lower <= value < upper
* @param lowerLimit
* @param upperLimit
* @return
*/
public static Between between_UE_LT(Comparable lowerLimit ,Comparable upperLimit)
{
return new Between(lowerLimit,upperLimit,1,0);
}
/**
* lower < value <= upper
* @param lowerLimit
* @param upperLimit
* @return
*/
public static Between between_UT_LE(Comparable lowerLimit ,Comparable upperLimit)
{
return new Between(lowerLimit,upperLimit,0,-1);
}

/**
* lower <= value <= upper
* @param lowerLimit
* @param upperLimit
* @return
*/
public static Between between_UE_LE(Comparable lowerLimit ,Comparable upperLimit)
{
return new Between(lowerLimit,upperLimit,1,-1);
}


Comparable lowerLimit ,upperLimit;
int lowerThreshold = 0,upperThreshold = 0;
private Between(Comparable lowerLimit ,Comparable upperLimit,
int lowerThreshold,int upperThreshold)
{
this.lowerLimit = lowerLimit;
this.upperLimit = upperLimit;
this.lowerThreshold = lowerThreshold;
this.upperThreshold = upperThreshold;
}



@Override
public boolean matches(Object obj) {
return lowerLimit.compareTo(obj) < lowerThreshold &&
upperLimit.compareTo(obj) > upperThreshold;
}



@Override
public void describeTo(Description desc) {
desc.appendText(
lowerLimit.toString() +
(lowerThreshold == 0 ? " < value " : " <= value ") +
(upperThreshold == 0 ? "< " : "<= ") +
upperLimit.toString());

}
}