3

Java EE環境では、通常、プロパティ/リソースファイルにテキストを保存することに慣れています。そして、そのプロパティファイルはいくつかのビューHTMLマークアップファイルに関連付けられています。たとえば、HTMLページでラベル「名」が「フルネーム」に変更された場合、プロパティを使用してその更新を行うことができます。

firstName=First Name
someOtherData=This is the data to display on screen, from property file

これらのプロパティファイルを定期的に更新することが難しい環境にいる場合、開発者は通常プロパティファイルに存在するテキスト/ラベルコンテンツを変更するためにどのアーキテクチャを使用していますか?または、プロパティファイルの変更を再デプロイする前に、そのコンテンツを変更する必要があるとします。悪い解決策はそれをデータベースに保存することですか?開発者はmemcacheを使用していますか?これは通常、キャッシュソリューションに使用されますか?

編集-1データベースは、実際にはこのタイプのタスク(画面に表示するテキストをプルする)用に設計されていませんが、データベースのユースケースがあります。ロケール列または状態フィールドを追加したり、グループごとに列フィルターを追加したりできます。データベースまたはプロパティファイルを使用しない場合、カスタムフィルターを追加できる分散キー/値ソリューションはどれですか?

編集-2Javaフレームワークの外部のソリューションを使用しますか?キー/値データストアのように?memcachedb?

4

5 に答える 5

11

ローカライズされたテキストを絶えず変更する必要がある場合、たとえば、展開ごとに異なる傾向がある場合は、データベースが最適です。ええと、データベースだけでなく、なんらかの方法で文字列をキャッシュする必要があります。そしてもちろん、リソースアクセスレイヤーを完全に再構築したくないと思います。

そのためにResourceBundle、データベースから文字列を自動的にロードしてに格納するようにクラスを拡張することを提案できますWeakHashMap。私はその機能のために選択WeakHashMapします-それはメモリフットプリントを減らす必要がなくなったときにマップからキーを削除します。とにかく、アクセサクラスを作成する必要があります。かなり古いテクノロジーであるJ2EEについて言及したので、Java SE 1.4互換の例を示します(新しいJava用に簡単に作り直すことができ、必要に応じて@Overrideを配置し、列挙に文字列の一般化を追加するだけです)。

public class WeakResourceBundle extends ResourceBundle {
    private Map cache = new WeakHashMap();
    protected Locale locale = Locale.US; // default fall-back locale

    // required - Base is abstract
    // @Override
    protected Object handleGetObject(String key) {
        if (cache.containsKey(key))
            return cache.get(key);

        String value = loadFromDatabase(key, locale);
        cache.put(key, value);

        return value;
    }

    // required - Base is abstract
    // @Override
    public Enumeration getKeys() {
        return loadKeysFromDatabase();
    }

    // optional but I believe needed
    // @Override
    public Locale getLocale() {
        return locale;
    }

    // dummy testing method, you need to provide your own
    // should throw MissingResourceException if key does not exist
    private String loadFromDatabase(String key, Locale aLocale) {
        System.out.println("Loading key: " + key
                + " from database for locale:"
                + aLocale );

        return "dummy_" + aLocale.getDisplayLanguage(aLocale);
    }

    // dummy testing method, you need to provide your own
    private Enumeration loadKeysFromDatabase() {
        return Collections.enumeration(new ArrayList());
    }
}

ResourceBundleの読み込みルールがおかしいため、実際にはクラスを拡張WeakResourceBundleして、サポートされている言語ごとに1つのクラスを作成する必要があります。

// Empty Base class for Invariant Language (usually English-US) resources
// Do not need to modify anything here since I already set fall-back language
package com.example.i18n;

public class MyBundle extends WeakResourceBundle {

}

それぞれ1つのサポートされている言語(私はそれが悪いことを知っています):

// Example class for Polish ResourceBundles
package com.example.i18n;

import java.util.Locale;

public class MyBundle_pl extends WeakResourceBundle {

    public MyBundle_pl() {
        super();
        locale = new Locale("pl");
    }
}

ここで、をインスタンス化する必要がある場合は、次のResourceBundleように呼び出すだけです。

// You probably need to get Locale from web browser
Locale polishLocale = new Locale("pl", "PL");
ResourceBundle myBundle = ResourceBundle.getBundle(
                "com.example.i18n.MyBundle", polishLocale);

そして、キーにアクセスするには:

String someValue = myBundle.getString("some.key");

考えられる落とし穴:

  1. ResourceBundle完全修飾クラス名(したがってパッケージ名)が必要です。
  2. Localeパラメータを省略すると、デフォルト(サーバーを意味する)Localeが使用されます。Localeインスタンス化中は必ず合格してくださいResourceBundle
  3. myBundle.getString()MissingResourceExceptionあなたが私の提案に従えば投げることができます。問題を回避するには、try-catchブロックを使用する必要があります。代わりに、キーが欠落している場合(など)にデータベースアクセス層からダミー文字列を返すことを決定できますreturn "!" + key + "!"が、どちらの方法でも、おそらくエラーとしてログに記録されるはずです。
  4. 言語と国コードの両方を渡すロケールオブジェクトの作成を常に試みる必要があります。これは、たとえば簡体字中国語(zh_CN)と繁体字中国語(zh_TW)のような言語は、(少なくとも書き込みに関しては)まったく異なる言語であり、2つのフレーバーをサポートする必要があるためです。他の国では、ResourceBundleは実際に正しい言語リソースを自動的にロードします(私が作成したので、まだ機能していることに注意してくださいMyBundle_pl.java。また、特定の言語のリソースクラスがない場合MyBundle_pl_PL.java、ResourceBundleは自動的にEnlish-US()にフォールバックします(MyBundle.javaそのため、このような奇妙なクラス階層を使用しました)。

編集

それをもっと恐ろしいものにする方法についてのいくつかのランダムな考え。

静的ファクトリ(ResourceBundleを直接使用することは避けてください)

ResourceBundleを使用してバンドルを直接インスタンス化する代わりに、静的ファクトリメソッドを追加できます。

public static ResourceBundle getInstance(Locale aLocale) {
    return ResourceBundle.getBundle("com.example.i18n.MyBundle", aLocale);
}

WeakResourceBundleクラスの名前をより適切な名前に変更することにした場合(私は使用することにしましたLocalizationProvider)、コードの消費からバンドルを簡単にインスタンス化できるようになりました。

ResourceBundle myBundle = LocalizationProvider.getInstance(polishLocale);

自動生成されたリソースクラス

ローカライズされたMyBundleクラスは、スクリプトを作成することで簡単に生成できます。スクリプトは、構成ファイルまたはデータベース駆動のいずれかである可能性があります。システム内で使用されているロケールを何らかの方法で知る必要があります。いずれにせよ、クラスは非常によく似たコードを共有しているので、それらを自動的に生成することは本当に理にかなっています。

ロケールの自動検出

クラスを実装するのはあなたなので、その動作を完全に制御できます。したがって、(アプリケーションアーキテクチャを知っている)ここにロケール検出を含め、getInstance()実際に適切な言語リソースを自動的にロードするように変更できます。

追加のローカリゼーション関連のメソッドを実装する

ローカライズされたアプリケーションで実行する必要のある一般的なタスクがあります。日付、数値、通貨などのフォーマットと解析が通常の例です。エンドユーザーのロケールを設定したら、そのようなメソッドをLocalizationProviderでラップするだけです。

ねえ、私は本当に私の仕事が大好きです:)

于 2011-05-01T21:22:46.663 に答える
4

プロパティファイルについて話しますが、実行時に、リソースバンドルなど、キーと値のペアのリストが必要なものが存在する可能性があります(ロケールによっては異なります)。

データを任意の形式で保存し、それを使用して適切なリソースバンドルを構築できます。それが記憶から来たとしても。したがって、データベースはそれを完全に行うことができます。なぜなら、プロパティはすべて起動時にロードされ、JVMメモリにキャッシュされ、それだけだからです。(SELECT * FROM LOCALIZATION_DATA)

そのために分散キャッシュを使用しないでください。あなたが持っているデータセットはとにかく小さい可能性があります...何ですか?最悪の場合、数MBかもしれません。また、すべてのビューが1ページあたり数十回、場合によっては数百回もデータへのアクセスをトリガーするため、そのデータへのアクセスは一度読み込まれると瞬時に行われる必要があります。

アプリケーションを再起動せずにデータを更新したい場合は、「ローカリゼーションデータの再読み込み」を含む管理画面をどこかに追加するか、このタイプのデータを更新できる画面を追加します(ただし、ファイル/ DBなどに保存します)。

ワークフローの観点からは、何を達成しようとしているかによって異なります。

クラシックプロパティファイルは、これを行うための好ましい方法です。ソースコードと一緒にバージョン管理を行うので、コードを常に最新の状態に翻訳できます。V1.3.0.1をデバッグしたいですか?このバージョンを入手するだけで、この時点で使用されていたプロパティファイルを使用できます。新しいキーを必要とする新しいコードを追加しましたか?または、何らかの理由でキー名を変更しただけですか?あなたは、コードとあなたの位置情報が首尾一貫した状態にリンクされていることを知っています。そして、これは自動です。

データがバージョン管理されていない場合、データの自動バージョン管理と履歴が失われます。新しいマシンにデプロイ/再デプロイすると、不一致が発生し、アプリケーションが適切に実行されなくなる可能性があります(新しいキーが必要であるが追加されていない場合。これはあまり良くなく、エラーが発生しやすく、手動による介入が多くなります。

ライブアップデートが本当に必要で、そのための新しいバージョンをリリースできない場合、私が行うことは、データの2つのソースを用意することです。標準データはバージョン管理下にあるため、すべてを最初から新規インストールするのに適しています。そして、標準値を上書きできるサーバー内の「カスタマイズされたデータ」。カスタマイズされた値は、バージョン間で更新するときに失われることはありません。これは、更新される標準値にすぎないためです。

サーバーでの変更が純粋に1回限りのカスタマイズである場合は、適切な管理Webページに移動し、ローカリゼーションデータのカスタマイズ画面を使用するだけです。

変更が新しいインストールのために保持したいものである場合は、2回追加します。サーバーで1回、バージョン管理で1回。

于 2011-04-28T08:46:28.653 に答える
1

いつでもJNDIを使用できます。あるいは、この種のことのためにJCRのようなドキュメントリポジトリを検討することもできます。

于 2011-04-25T15:05:07.893 に答える
1

データベースがこれを処理できなかったかどうかはわかりませんが、本当に探しているのは、これらのプロパティが変更されたときに無効になる可能性のあるキャッシュだと思います。JBoss Infinispan(http://www.jboss.org/infinispan)のようなものを使用することを考えましたか?使い方は非常に簡単で、複数のアプリケーションサーバーに分散できます。

Infinispanは、一度設定すると、マップのように使用できます。クラスタ全体に分散するように構成できることに注意してください。

NoSQLソリューションを使用してもかまわない場合は、RedisやMemcacheなどをお勧めします。もちろん、ローカルキャッシュを保持することをお勧めします(特に、これらのプロパティが頻繁に変更される可能性が低い場合は、ネットワーク呼び出しのコストが発生するのはなぜですか?)。

于 2011-05-04T11:22:51.007 に答える
1

ベルリンブラウンからの要請に応じて、特定のニーズに焦点を当てた別の回答を追加します。

必要なデータの量(数千のエントリなど)から、リモートURLを指定して、起動時にプロパティファイルをロードする必要があります。

データは、パフォーマンスを最大化するためにJVMメモリにキャッシュされます。

ワークフローに応じて、レガラーベースで更新をチェックするバックグラウンドプロセスがあります(たとえば、1分ごと、1時間ごとなど、十分なものは何でも)。更新が必要です。

データベースは必要ありません。memcachedも、NoSQLも必要ありません。本番サーバーからアクセスできる単純なURL。セキュリティと開発の面では、それはより簡単で、より速く、より柔軟です。

実装の詳細:標準形式を使用する場合は、言語/国ごとにファイルがあります。すべての言語を更新するか、それらを一緒にバンドルすることを忘れないでください(例としてzipを使用)。

于 2011-05-06T08:49:18.483 に答える