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

2016年1月9日土曜日

[NIO2]を使ってZipファイルの読み込みを行う

試してみたのは、郵便番号CSV?をダウンロードしてきて、zip内のcsvデータを標準出力するもの。

NIO2では、zipファイルを仮想ファイルシスムとして扱えるので、Files.walkFileTreeでzip内のファイルだけを対象にした処理を簡単に実装できる。


// zipのダウンロード
String url = "http://www.post.japanpost.jp/zipcode/dl/oogaki/zip/10gumma.zip";
final Path zip = Paths.get("10gumma.zip");
try (InputStream stream = new URL(url).openStream()) {
    Files.copy(stream, zip, StandardCopyOption.REPLACE_EXISTING);
}

// zip内のcsvデータを標準出力に出力
try (FileSystem fileSystem = FileSystems.newFileSystem(zip, null)) {
    Files.walkFileTree(fileSystem.getPath("/"), new SimpleFileVisitor() {
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
            try (BufferedReader reader = Files.newBufferedReader(file, Charset.forName("windows-31j"))) {
                reader.lines().forEach(System.out::println);
            }
            return FileVisitResult.CONTINUE;
        }
    });
}


2015年4月2日木曜日

[jdbc]カレントのスキーマ名を取得する

現在の接続のカレントスキーマ名を得る方法。

Connectionインタフェースにカレントスキーマ名を取得するメソッド(getSchema)があるのでそれを使えばよい。
※getSchemaは、Java7から追加された機能。

サンプルコード

        final Connection connection = dataSource.getConnection();
        final String schema = connection.getSchema();
        System.out.println("schema = " + schema);

2015年1月31日土曜日

[JPA]中間テーブルを作らずに直接結合させる

例えば1対多の依存があるエンティティをJPAで使用した場合、デフォルトでは中間テーブルを使った結合となる。

Entityの関連定義

    @OneToMany(cascade = {CascadeType.ALL})
    public List<Tel> getTels() {
        return tels;
    }

テーブル定義

上のEntityの関連定義だと下のようなテーブル構造となります。
中間テーブルのテーブル名やカラム名を変更したい場合には、OneToManyとセットでJoinTableアノテーションを定義して任意の値に変更します。

どうしても過去の資産などを使わないとダメな場合なので中間テーブルを使えない場合には、OneToManyとセットでJoinColumnを定義してあげる。JoinColumnのname属性には、子供側テーブルの外部キーとなる項目名を定義します。

Entityを次のように変更すると、Userテーブルの子テーブルのTelテーブルにはUserテーブルを参照するための外部キー(user_id)が作成される。
    @OneToMany(cascade = {CascadeType.ALL})
    @JoinColumn(name = "user_id")
    public List<Tel> getTels() {
        return tels;
    }

テーブル定義は次のようになります。

2015年1月25日日曜日

[JPA]ElementCollectionとCollectionTableアノテーション

JPAのEntityクラスに基本型のCollectionやListの属性を定義する場合は、ElementCollectionアノテーションを使用する。

ElementCollectionをつけることで、基本型(Entityじゃないクラス)をもつCollectionやListの属性を別のテーブルに保存してくれるようになる。
CollectionTableアノテーションを使うと、保存先のテーブルの名前などをカスタマイズできるようになる。

例

Entityクラスの定義です。ソースコードが長くなると見づらくなるので、JPAのアノテーションはpublicフィールドに設定してアクセッサは定義していません。
@Entity
public class User {

    @Id
    @GeneratedValue
    public Long id;

    public String name;

    @ElementCollection
    public Set mail;
}

上のEntity定義の場合、CollectionTableアノテーションを使用していないのでデフォルトの定義でテーブルが作られます。
テーブル名は、親テーブルのテーブル名とその属性の名前(user_mail)となり、カラム名は属性名(mail)となります。


テーブル名を変更したい場合には、CollectionTableアノテーションのname属性を使用します。
カラム名を変更したい場合には、Columnアノテーションを使用します。



2015年1月20日火曜日

[JDBC]DatabaseMetaDataから識別子の格納方法を判断する

JDBCのDatabaseMetaDataから、データベースの識別子(テーブル名やカラム名)がどのように格納されているか判断する方法。
DatabaseMetaDataを使ってカラム一覧や主キー一覧などを取得するときには、格納方法を判断して識別子を大文字や小文字に変換してから使用する必要がある。

引用符付きの識別子は、基本使わない(使っとこともない)ので、引用符なしの識別子が大文字、小文字どちらで格納されているかを判断するだけのサンプルコード
// storesMixedCaseIdentifiersで引用符なしの識別子の格納方法を判断する。
// falseが返される場合は、大文字、小文字のどちらかで格納されている。
System.out.println("大文字小文字混在の引用符なし識別子を大文字小文字混在で格納する → " + metaData.storesMixedCaseIdentifiers());

// storesLowerCaseIdentifiersで小文字で格納されているかを判断する。
// trueが返される場合は、小文字で格納されている。
System.out.println("引用符なし識別子を小文字で格納する → " + metaData.storesLowerCaseIdentifiers());

// storesUpperCaseIdentifiersで大文字で格納されているかを判断する。
// trueが返される場合は、大文字で格納されている。
System.out.println("引用符なし識別子を大文字で格納する → " + metaData.storesUpperCaseIdentifiers());

実行結果

Oracleの場合

大文字で格納されていることが分かる
大文字小文字混在の引用符なし識別子を大文字小文字混在で格納する → false
引用符なし識別子を小文字で格納する → false
引用符なし識別子を大文字で格納する → true

PostgreSQLの場合

小文字で格納されていることが分かる
大文字小文字混在の引用符なし識別子を大文字小文字混在で格納する → false
引用符なし識別子を小文字で格納する → true
引用符なし識別子を大文字で格納する → false

2015年1月7日水曜日

OracleでJDBC経由でレコード追加後にROWIDを取得する

OracleのJDBCドライバで、Statement生成時にStatement.RETURN_GENERATED_KEYSフラグを指定している場合、自動生成キーは追加されたレコードのROWIDが返されます。

何が返されるかわからなかったので、試してみたらROWIDが取得出来たのですが、OracleのドキュメントにROWIDが取得されることが明記されていました。
以下が、ドキュメントの内容となります。
java.sql.Statement

キー列を明示的に指定しないと、Oracle JDBCドライバでは取り出す列を特定できません。特定できるのは、列名または列索引の配列が使用されている場合です。
ただし、int型のStatement.RETURN_GENERATED_KEYSフラグが使用されている場合、Oracle JDBCドライバではこれらの列を特定できません。int型フラグを使用して自動生成キーが戻るように指定した場合は、ROWID擬似列がキーとして戻ります。
このROWIDは、ResultSetオブジェクトからフェッチして、他の列を取り出すために使用できます。

例

PreparedStatement statement = connection.prepareStatement("INSERT INTO HOGE VALUES (?, ?)",
Statement.RETURN_GENERATED_KEYS);
statement.setLong(1, 1);
statement.setString(2, "name");
statement.executeUpdate();
ResultSet generatedKeys = statement.getGeneratedKeys();
if (generatedKeys.next()) {
    ROWID rowid = (ROWID) generatedKeys.getRowId(1);
    System.out.println(rowid.stringValue());
}
上のコードを実行した場合、標準出力にはROWIDを文字列表現に変換した値が出力されます。
例えば、「AAAOKLAAEAAACQTAAA」のような値が出力されます。

2014年12月22日月曜日

[Oracle12c]generatedカラムの値をJDBC経由でinsert後に取得する

Oracle12cで追加された自動生成カラムの値を、JDBC経由でINSERTを行った際に取得する方法です。

コードサンプル

テーブル定義

CREATE TABLE USER_INFO
(
    ID INTEGER PRIMARY KEY NOT NULL,
    NAME VARCHAR2(100)
)

Javaコード

PreparedStatement statement = connection
        .prepareStatement(
                "insert into USER_INFO (name) values (?)",
                new String[]{"id"});
statement.setString(1, "なまえ");
int count = statement.executeUpdate();
System.out.println("count = " + count);

try (ResultSet generatedKeys = statement.getGeneratedKeys()) {
    if (generatedKeys.next()) {
        System.out.println(generatedKeys.getInt(1));
    }
}

解説

PreparedStatement生成時に、自動生成キーを返す機能を持つPreparedStatementを生成します。
上のサンプルでは、自動生成カラムはID列なので、prepareStatementの2番めの引数にID列を指定しています。

必要な値をバインド後に、executeUpdateを呼び出してINSERT処理を実行します。

INSERT処理が正常に終了後、getGeneratedKeysを呼び出して、自動生成カラムに対して自動的に採番されて設定された値を取得します。
getGeneratedKeysは、ResultSetを返すのでフェッチ(nextメソッドの呼び出し)しながら値を取得していきます。
Oracleでは、getGeneratedKeysで返されるResultSetからの値の取得はインデックスのみ可能なので、カラム名によるアクセスは出来ません。

[Java8]インタフェースのデフォルトメソッド

Java8から新しく追加されたインタフェースのデフォルトメソッド。

デフォルトメソッドの定義

デフォルトメソッドは、defaultキーワードと共にメソッドを宣言します。
この例では、printメソッドがデフォルトメソッドとして宣言されています。
interface Hoge {
    default void print() {
        System.out.println("hoge");
    }
}

同じデフォルトメソッドを持つ複数のインタフェースを実装した場合

何もしなければコンパイルエラーとなります。
例えば、下のコードではImplクラスが同じ定義のデフォルトメソッドを持つI1とI2インターフェースを実装しているので、コンパイル時にエラーとして報告されます。
interface I1 {
    default void print() {
        System.out.println("I1");
    }
}
interface I2 {
    default void print() {
        System.out.println("I2");
    }
}

class Impl implements I1, I2 {
}

もしどちらか一方の実装を使用したいのであれば、superキーワードを使用して一方のインタフェースの実装を選択します。
下の例では、I2インタフェースのデフォルト実装を選択しています。
class Impl implements I1, I2 {
    @Override
    public void print() {
        I2.super.print();
    }
}

もちろん、下のコードのように実装を上書きすることもできます。
class Impl implements I1, I2 {
    @Override
    public void print() {
        System.out.println("Impl");
    }
}

片方がデフォルトメソッドで片方が抽象メソッドの場合も、コンパイルエラーとなります。
この場合も、上で説明したようにデフォルトメソッドを選択するか、このクラスで新たな実装をするか選択する必要があります。
interface I1 {
    default void print() {
        System.out.println("I1");
    }
}
interface I2 {
    void print();
}

class Impl implements I1, I2 {
}

一方が抽象クラスの場合には、抽象クラスに定義されたメソッドが優先されて選択されます。
例えば下のコードの場合、A1抽象クラスに定義されたprintメソッドが呼び出されます。
interface I1 {
    default void print() {
        System.out.println("I1");
    }
}

abstract class A1 {
    public void print() {
        System.out.println("A1");
    }
}

class Impl extends A1 implements I1 {
}

2014年11月3日月曜日

[Java8]メソッド参照

lambda式で定義したい処理内容と全く同じ処理をするメソッドがすでに提供されている場合には、
そのメソッドをメソッド参照という機能を使用してメソッドを渡してあげれば良い。

メソッド参照は、以下のように::演算子でクラス名(オブジェクト名)とメソッド名を連結して使用する。
使用できるパターンは、以下のとおり。

* オブジェクト::instanceメソッド
* クラス名::staticメソッド
* クラス名::instanceメソッド

使用例

System.out.printlnを使用した例は以下のようになる。
list.forEach(System.out::println);

// 上記は、以下と全く同じ
list.forEach(s -> System.out.println(s));

オーバロードされたメソッドがある場合

メソッド参照使用時で、オーバロードされたメソッドが存在していた場合にどのメソッドを呼びだそうとしているかについては、コンパイラがコードの内容から判断しようとする。

例えば、以下の例だとメソッド参照が使用された箇所の関数型インタフェースが受け取る型に対応したメソッドが使用される。

public class Hoge {

    public static void main(String[] args) {
        List strings = Arrays.asList("1", "2", "3");
        strings.forEach(Hoge::output);  // Stringを引数に取るoutputが選択される

        List numbers = Arrays.asList(1, 2, 3);
        numbers.forEach(Hoge::output);  // intを引数に取るoutputが選択される
    }

    public static void output(String s) {
        System.out.println("string");
    }

    public static void output(int i) {
        System.out.println("int");
    }
}

2014年11月2日日曜日

[Java8]Lambda式の形式

ラムダ式は、引数 -> 式本体の形式で記述する。

基本パターン

(String first, String second) -> {
  return Integer.compare(first.length, second.length)
}

型推論可能な場合は、引数の型を省略出来る

左辺の総称型などから推論可能な場合などに型推論される。
(first, second) -> {
  return Integer.compare(first.length, second.length)
}

型推論された引数を一つだけ保つ場合には、カッコも省略可能

Arrays.asList(1, 2, 3, 4, 5).forEach(n -> System.out.println(n));

アノテーションなど

メソッドの引数と同じように、、アノテーションやfinal修飾子をつけることも可能
(final String first, final String second) -> {
  return Integer.compare(first.length, second.length)
}

戻り値について

lambdaの戻り値は明示しない。常に文脈から型が判断される。
以下の例だとintが期待されているところで使用できる。

※分岐などで結果を返したり返さなかったりするようなlambda式は不正となる。(コンパイルエラー)
(final String first, final String second) -> {
  return Integer.compare(first.length, second.length)
}

2014年10月22日水曜日

Java8のFunctionalInterfaceアノテーション

FunctionalInterfaceは、関数型インタフェースであることを示すために使用するアノテーション。

基本的に、関数型インタフェースの要件(インタフェースで抽象メソッドは1つなど・・・)を満たしていれば良いが、
FunctionalInterfaceアノテーションを付加するとコンパイル時に関数型インタフェースの要件を満たしていない場合コンパイルエラーとしてくれる。
また、このインタフェースが関数型インタフェースであることが、明確なので関数型インタフェースにはこのアノテーションを付加するとよい。

例えば、以下の様なインタフェース定義は関数型インタフェースの要件を満たさないためコンパイルエラーとなる

@FunctionalInterface
interface Hoge {
    void hoge();
    void fuga();
}

エラー内容としては以下の様なものが出力される。
Error:(1, 1) java: 予期しない@FunctionalInterface注釈
  Hogeは機能インタフェースではありません
    インタフェース Hogeで複数のオーバーライドしない抽象メソッドが見つかりました

2014年7月11日金曜日

Activiti-explorerをtomcatにデプロイ

1. activitiをダウンロード

http://activiti.org/download.html からActivitiをダウンロードする。
ダウンロードされたファイルは任意のディレクトリに解凍しておく。

2. Tomcatをダウンロード

http://tomcat.apache.org/download-80.cgiからTomcatをダウンロードする。
ダウンロードされたファイルは任意のディレクトリに解凍しておく。

3.Activiti-ExplorerをTomcatにデプロイする。

ダウンロードしたAcitivitiを解凍したディレクトリ内の「wars/activiti-explorer.war」を、
Tomcatを解凍したディレクトリの「webapps」にコピーする。

4.Tomcatを起動する。

Tomcatを解凍したディレクトリの「bin/startup.sh」を実行し、Tomcatを起動する。

5.画面を開く

http://localhost:8080/activiti-explorer/ にアクセスする。
以下の様な画面が開けば、デプロイは成功しています。


6.ログインの実行

アカウントは「http://www.activiti.org/userguide/#deploymentWithExplorer」の「Activiti setup」の章に記述されています。

まずは、デプロイして実行出来るところまで。
UI気にしなければこのまま、使えるのかな?もうちょっと調べてみないと。

2012年9月7日金曜日

Nio2でファイル属性

NIO2を使ってファイルの属性を取得する方法。

// 最後の更新日時
System.out.println("Files.getLastModifiedTime(path) = " + Files.getLastModifiedTime(path));
// ファイルサイズ
System.out.println("Files.size(path) = " + Files.size(path));
// 読み込み可能?
System.out.println("Files.isReadable(path) = " + Files.isReadable(path));
// 書き込み可能?
System.out.println("Files.isWritable(path) = " + Files.isWritable(path));
// シンボリックリンク?
System.out.println("Files.isSymbolicLink(path) = " + Files.isSymbolicLink(path));
// ディレクトリ
System.out.println("Files.isDirectory(path) = " + Files.isDirectory(path));
// 属性を纏めて取得
System.out.println("Files.readAttributes(path, \"*\") = " + Files.readAttributes(path, "*"));

2012年8月24日金曜日

NIO2を使ってファイルの作成

Files.createFileを使ってファイルの作成を行う。ファイル作成時にはアクセス権限も設定できたりする。

属性指定なしでファイル作成

Path path = Paths.get("test.txt")
Files.createFile(path)

umaskが022なので644でファイルが作られる。
sh-3.2$ umask
0022
sh-3.2$ ls -ln test.txt
-rw-r--r--  1 501  20  0  8 24 23:40 test.txt


アクセス権限(rwx------)を指定してファイル作成

FileAttribute<Set<PosixFilePermission>> attribute = PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwx------"))
Path path = Paths.get("test.txt")
// 2番目の引数にFileAttributeを指定する。
Files.createFile(path, attribute)

指定したアクセス権限でファイルが作られる。
sh-3.2$ ls -ltr test.txt
-rwx------  1 hove  staff  0  8 24 23:45 test.txt

2012年8月19日日曜日

NIO2で再帰的にファイルを扱う方法

Files.walkFileTreeメソッドを使用して、従来のjava.io.Fileを使う時よりも簡単にディレクトリを再帰的にたどってファイルを扱える。

下のコードだと、ディレクトリを再帰的にたどっていって、拡張子が「.groovy」の場合に標準出力にファイル名(フルパス)を出力しています。
import java.nio.file.*
import java.nio.file.attribute.BasicFileAttributes

def dir = Paths.get("../..")
Files.walkFileTree(dir, new FileVisitor());

class FileVisitor extends SimpleFileVisitor {
  @Override
  FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
    if (file.toString().endsWith(".groovy")) {
      println "groovyファイル:${file.toAbsolutePath().normalize()}"
    }
    return FileVisitResult.CONTINUE
  }
}

2012年8月18日土曜日

java.io.Fileとjava.nio.file.Pathの変換

File.toPathとPath.toFileを使う。

File file = new File('.')
// FileからPathへ
Path path = file.toPath()
// PathからFileへ
File toFile = path.toFile()

2012年8月16日木曜日

NIO2でglobを使ってファイル抽出

Files.newDirectoryStreamの第二引数にglobを指定する。


「*」を使用すると全ての文字
// すべてのファイルにマッチ
Files.newDirectoryStream(path, "*").each {
  println it.fileName
}

// sから始まるファイルにマッチ
Files.newDirectoryStream(path, "s*").each {
  println it.fileName
}

「?」は任意の1文字
// 拡張子をのぞいたファイル名が3文字
Files.newDirectoryStream(path, "???.groovy").each {
  println it.fileName
}

「[]」を使用すると[]の中で指定した文字
// 小文字から始まるファイル
Files.newDirectoryStream(path, "[a-z]*").each {
  println it.fileName
}

// mとnから始まるファイル
Files.newDirectoryStream(path, "[mn]*").each {
  println it.fileName
}

「{}」は、{}内に指定した値のいずれか(カンマ区切りで指定する。)
// 拡張子がjavaとgroovyのファイル
Files.newDirectoryStream(path, "*.{groovy,java}").each {
  println it.fileName
}

2012年8月5日日曜日

NIO2でファイルのコピー

Files#copyでコピーができる。

// コピー元
Path from = Paths.get("C:\\work\\test.txt");
// コピー先
Path to = Paths.get("C:\\work\\test2.txt");

// コピー
Files.copy(from, to);

2012年7月22日日曜日

Java7のNIO2-Pathクラス

Java7のNIO2の勉強し始めたので、まずはPathクラスあたりを。

Path path = Paths.get("./test.txt");
System.out.println("path.toString() = " + path.toString());
System.out.println("path.getFileName() = " + path.getFileName());
// 不要な「.」や「\」なんかを取り除いてくれる。
System.out.println("path.normalize() = " + path.normalize());
// 絶対パスかどうか
System.out.println("path.isAbsolute() = " + path.isAbsolute());
// 絶対パスに変換
System.out.println("path.toAbsolutePath() = " + path.toAbsolutePath());
// 不要なものを取り除いて絶対パスに
System.out.println("path.normalize().toAbsolutePath() = " + path.normalize().toAbsolutePath());

実行結果
path.toString() = .\test.txt
path.getFileName() = test.txt
path.normalize() = test.txt
path.isAbsolute() = false
path.toAbsolutePath() = C:\work\test\.\test.txt
path.normalize().toAbsolutePath() = C:\work\test\test.txt

2012年4月12日木曜日

keytoolを使ってみた

keytoolを使用して、暗号化の鍵を生成してみよう。

※keytoolは、JAVA_HOME/bin配下にいます。

鍵を登録

keytoolの「-genseckey」を使用して暗号化の鍵を登録できます。 【ポイント】
  • storetypeには、「JCEKS」を指定する。
  • keyalgには、鍵を生成するためのアルゴリズムを指定する。
  • keysizeは、秘密鍵を生成する際には必須。今回は、AES暗号用の128ビットの鍵を生成している。
  • keystoreのファイル名。指定しない場合は、HOME/.keystoreとなる。
  • aliasには、鍵を識別するための任意の名前を指定する。 KeyStore#getKeyを使用して、鍵を取得する際に指定する名前となる。
  • storepassには、キーストアにアクセスするためのパスワードを設定する。
keytool -genseckey -storetype JCEKS -keyalg AES -keysize 128 -keystore ./.key -storepass password -alias key1 

上記コマンド実行後に、以下のメッセージが表示されます。 鍵ごとにパスワードを設定する場合には、新しいパスワードを入力します。 keysotreにアクセスする際に必要となるパスワードと同じにする場合には、なにも入力しなくて大丈夫です。
 の鍵パスワードを入力してください。 (キーストアのパスワードと同じ場合は RETURN を押してください): 

登録されている鍵をリスト表示

listを指定すると、登録されている鍵や証明書をリスト表示することが出来ます。

$ keytool -list -storepass password -keystore .key -storetype JCEKS

キーストアのタイプ: JCEKS キーストアのプロバイダ: SunJCE

キーストアには 2 エントリが含まれます。

key1, 2012/04/12, SecretKeyEntry, mykey, 2012/04/12, SecretKeyEntry, 

登録されている鍵を削除

deleteを指定すると登録されている鍵を削除できます。 deleteを実行後にlist表示してみると、鍵が上のlist表示の時から減っていることが分かります。
$ keytool -delete -alias mykey -keystore .key -storepass password -storetype JCEKS $ keytool -list -storepass password -keystore .key -storetype JCEKS

キーストアのタイプ: JCEKS キーストアのプロバイダ: SunJCE

キーストアには 1 エントリが含まれます。

key1, 2012/04/12, SecretKeyEntry,