MyBatis の定義がクソ面倒なので、生成を狙う
A5:SQL Mk-2 - フリーの汎用SQL開発ツール/ER図ツール .. 松原正和 使って作ったテーブルのマッピングを書くのがだるくなったので、自動生成を狙った。
まずはこんな感じの ER を書いて、CSV でエクスポートする。
一応設定は UTF-8 で吐く。
そうすると、a5m2_COLUMNS.csv
なるファイルが出来上がる。
TABLE_CATALOG,TABLE_SCHEMA,TABLE_NAME,COLUMN_NAME,LOGICAL_NAME,ORDINAL_POSITION,COLUMN_DEFAULT,IS_NULLABLE,DATA_TYPE,KEY_POSITION,DESCRIPTION Example,,example_entity,id,ID,1,,NO,BIGINT IDENTITY,1, Example,,example_entity,name,名前,2,,NO,NVARCHAR(64),, Example,,example_entity,price,価格,3,,NO,@INT,, Example,,example_entity,example_field,その他適当,4,,NO,"@DECIMAL(10, 3)",,"これが 複数行" Example,,seller,id,ID,1,,NO,BIGINT IDENTITY,1, Example,,seller,example_entry_id,商品ID,2,,NO,BIGINT,, Example,,seller,name,店名,3,,NO,NVARCHAR(64),, Example,,seller,created_at,作成ビ,4,CURRENT_TIMESTAMP,NO,@DATETIME,, Example,,seller,updated_at,更新日,5,,YES,@DATETIME,,
あとはこれを自動生成できりゃいい。
ちなみに SQL Server 用に設定したけど、他のものを使いたければちょっと改変すればいい。
Python3 で読み込んで、マッピング java bean (Lombok) と mapping 用 XML を吐き出した。
import csv def to_camel_name(name): components = name.split('_') return components[0] + "".join(x.title() for x in components[1:]) def to_upper_camel_name(name): components = name.split('_') return "".join(x.title() for x in components[:]) def starts_with(string_value, arr): for v in arr: if string_value.startswith(v): return True return False class Field: def __init__(self, name, id, dataType, description): self.name = name self.id = id self.dataType = dataType self.description = description def db_type(self): stn = self.dataType.lower() convert = { 'INTEGER' : ['@int', 'int'], 'BIGINT' : ['bigint', '@bigint'], 'DECIMAL' : ['@decimal', 'decimal'], 'VARCHAR' : ['@char', 'char', 'varchar'], 'NVARCHAR' : ['nchar', 'nvarchar'], 'BOOLEAN' : ['bit'], 'TIMESTAMP': ['date', 'datetime', 'timestamp', '@datetime'] } for key in convert.keys(): if starts_with(stn, convert[key]): return key return None def type_name(self): stn = self.dataType.lower() convert = { 'Integer' : ['@int', 'int'], 'Long' : ['bigint', '@bigint'], 'BigDecimal': ['@decimal', 'decimal'], 'String' : ['@char', 'char', 'varchar', 'nchar', 'nvarchar'], 'Boolean' : ['bit'], 'Date' : ['date', 'datetime', 'timestamp', '@datetime'] } for key in convert.keys(): if starts_with(stn, convert[key]): return key return 'Object' def get_field_definition(self): changed_type = self.type_name() field_name = to_camel_name(self.id) str = '' str = str + f' /**\n' str = str + f' * {self.name}.\n' str = str + f' *\n' str = str + f' * {self.description}.\n' str = str + f' */\n' str = str + f' private {changed_type} {field_name};\n\n' return str class_field = {} # クラス辞書 try: with open('a5m2_COLUMNS.csv', 'r') as csvfile: spamreader = csv.reader(csvfile, delimiter=',', quotechar='"') for row in spamreader: if len(row) == 0: continue class_name = to_upper_camel_name(row[2]) field = Field(row[4], row[3], row[8], row[10]) if class_name in class_field: class_field[class_name].append(field) else: class_field[class_name] = [field] # 起こりそうな例外をキャッチ except FileNotFoundError as e: print(e) except csv.Error as e: print(e) for class_name in class_field.keys(): file = open(f'{class_name}.java', 'w', encoding='UTF-8') file.write('\nimport lombok.*;') file.write('\nimport java.util.Date;\n') file.write('\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n') file.write('public class ' + class_name + ' {\n') for field in class_field[class_name]: file.write(field.get_field_definition()) file.write('}\n') file.close() file = open(f'{class_name}.mapping.xml', 'w', encoding='UTF-8') file.write(f'<resultMap id="{class_name}Map" type="{class_name}">\n') for field in class_field[class_name]: cur_db_type = field.db_type() db_str = ('jdbcType="' + cur_db_type + '"') if cur_db_type != None else '' file.write(f' <result property="{to_camel_name(field.id)}" column="{field.id}" {db_str} />\n') file.write('</resultMap>\n') file.close()
このスクリプトは好きに使って。
CSV の1行目呼んで変なの出るけどご愛嬌で!
Java9 入れて遊んでみた
まずはここからダウンロード。
Java SE - Downloads | Oracle Technology Network | Oracle
Early Access Releases を選ぶと、JDK9 がダウンロードできる。
インストーラを終えたら .bash_profile
を設定する。
export JAVA_HOME=`/usr/libexec/java_home -v 9`
としれっと記述。
後は bash 起動して jsell
と叩くとしれっと書くと動き始める。
$ jshell | JShellへようこそ -- バージョン9 | 概要については、次を入力してください: /help intro jshell> System.out.println("Hello jshell"); Hello jshell jshell>
とはいえ、Java は Shell のようなものが十全なほど言語仕様は十分か…はちょっとまだ解りませんが…
Bootstrap 以外の css フレームワーク
有名どころ。
個人的にきになったやつのみ
http://foundation.zurb.com/foundation.zurb.com
サイトや email に使用できるフレームワーク。
CSS フレームワークというより、HTML の組み方なんかも固定となっている。
やたらと部品が豊富で、一種のラブラリ集と化している雰囲気。
Semantic is a development framework that helps create beautiful, responsive layouts using human-friendly HTML. Human-Friendly な HTML で美しく、レスポンシブレイアウトの開発を支援する開発フレームワークだ
サンプルを見てもわかりやすい。
学習コストが低いのはいい事です。
Google の提唱する MaterialDesign を実装するコンパクトなフレームワーク。
コンパクトなだけあって昨日は低めだけど、Material-UI よりはだいぶ安定してるかな?
Goole 製 MaterialDesign フレームワーク。
公式で React を謳うとか、Polymer や Angular はどーした?
Google は結構、OSS でさえあれば、自社製に拘らない所があるし、ありえるっちゃありえるか…
v1.0 からマジで React 前提になってる臭い。
https://material-ui-1dab0.firebaseapp.com/getting-started/usage
この辺も参考になるかも?
現時点で Reactを Gulp 上コンパイルして見る
教科書はこれ
package.json
をしれっと。
{ "name": "react-base", "version": "0.1.0", "description": "React base application template", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "BSD-2-Clause", "dependencies": { "gulp-util": "^3.0.8", "react": "^15.6.1", "react-dom": "^15.6.1" }, "devDependencies": { "autoprefixer": "^7.1.2", "babel": "^6.23.0", "babel-preset-es2015": "^6.24.1", "babel-preset-react": "^6.24.1", "babelify": "^7.3.0", "browserify": "^14.4.0", "cssnano": "^3.10.0", "gulp": "^3.9.1", "gulp-postcss": "^7.0.0", "gulp-sass": "^3.1.0", "gulp-sourcemaps": "^2.6.0", "gulp-uglify": "^3.0.0", "gulp-webserver": "^0.9.1", "rimraf": "^2.6.1", "vinyl-buffer": "^1.0.0", "vinyl-source-stream": "^1.1.0" }, "scripts": { "start": "gulp", "clean": "gulp clean", "compile": "gulp compile" } }
とりあえず各バージョンは上記の通り。
その上で gulpfile.js
を下記のようにすればまずコンパイルできる
var gulp = require('gulp'); var gutil = require('gulp-util'); var browserify = require('browserify'); var babelify = require('babelify'); var source = require('vinyl-source-stream'); var buffer = require('vinyl-buffer'); var sourcemaps = require('gulp-sourcemaps'); var uglify = require('gulp-uglify'); var webserver = require('gulp-webserver'); let SOURCE_DIR = './src'; let JS_SOURCE_DIR = SOURCE_DIR + '/main'; let DIST_DIR = './dist'; // React コードのコンパイル設定 let jsCompileTask = browserify({ entries: [ JS_SOURCE_DIR + '/index.js'], transform: ['babelify'], cache: {}, packageCache: {} }); // コンパイル実行箇所 function compileJs() { function createErrorHandler(name) { return function (err) { console.error('Error from ' + name + ' in compress task', err.toString()); }; } return jsCompileTask.bundle() // ソースをコンパイル .on('error', gutil.log.bind(gutil, 'Browserify Error')) // コンパイルエラー出力 .pipe(source('main.min.js')) // 全てのソースをまとめた JavaScript 出力名 .pipe(buffer()) // バッファリング .pipe(sourcemaps.init({loadMaps: true})) // 既に出てるソースマップ取り込み .pipe(uglify()) // コード圧縮 .on('error', createErrorHandler('uglify')) // エラーメッセージ .pipe(sourcemaps.write('./')) // デバッグ情報出力 .pipe(gulp.dest(DIST_DIR + '/js')); // 出力ディレクトリ } function runServer() { gulp.src(DIST_DIR) .pipe(webserver({ livereload: true, open: true, fallback: '/index.html' })); } function watch() { gulp.watch(JS_SOURCE_DIR + '/**/*.js', ['compileJs']); } gulp.task('compileJs', compileJs); gulp.task('compile', ['compileJs']) gulp.task('watch', watch); gulp.task('debug', ['compile', 'watch'], runServer); gulp.task('default', ['debug']);
後は scss でもなんでもすればおk
ただし、この構成は欠点があって、CSS ファイルを include すると死ぬ。
多分 babel-preset-react の問題だと思う。
Gradle4 でマルチプロジェクト
教科書はこれ。 第57章 マルチプロジェクトのビルド
まずは Jersey の設定を作って、分離するものを指定する。
Jerseyの設定2(web.xmlとかApplicationクラスとか) - edgegram
サーブレット設定をソースで
web.xml
ファイルは昔は役に立ったアーキテクチャだ。
一つの Tomcat で複数のサーブレットをデプロイするにあたって、そのデプロイ設定を行うにはリーズナブルだったろう。
だが、はっきり言おう、今日日1サーバにそう何個もデプロイなんざしねーよと(汗
MVC ができてルーティングができるようになった時から、その役目を失ったと言える。
ということで設定
package net.white_azalea import org.glassfish.jersey.server.ResourceConfig import org.glassfish.jersey.server.ServerProperties import javax.ws.rs.ApplicationPath @ApplicationPath("/") class ApplicationSetting : ResourceConfig { constructor() { packages("net.white_azalea") property(ServerProperties.PROVIDER_SCANNING_RECURSIVE, true) } }
そしてこれがあると、設定ファイル( web.xml
)いらずなのです。
そしてプロジェクト分離
設定一つで、管理しようと思う。
何って完全分離とか難しすぎた
目的はこんな感じに配置すること。
- ROOT
- build.gradle
- settings.gradle
- src/main/kotlin/net/white_azalea/ApplicationSetting.kt
プロジェクト設定を含むソース - settings
サブプロジェクト- src/main/kotlin/net/white_azalea/api/Example.kt ソース
まずは ROOT/settings.gradle
include 'settings'
次に、ROOT/build.gradle
を編集
apply plugin: 'maven' apply plugin: 'war' apply plugin: 'org.akhikhl.gretty' apply plugin: 'kotlin' sourceCompatibility = 1.8 def jerseyVersion = '2.25.1' buildscript { repositories { jcenter() mavenCentral() } dependencies { classpath 'org.akhikhl.gretty:gretty:+' classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.3' } } def thisGroup = 'net.white-azalea' def thisVersion = '1.0' allprojects { apply plugin: 'maven' apply plugin: 'war' group = thisGroup version = thisVersion repositories { jcenter() mavenCentral() mavenLocal() } dependencies { compile 'javax.ws.rs:javax.ws.rs-api:2.0.1' compile "org.glassfish.jersey.containers:jersey-container-servlet:${jerseyVersion}" testCompile 'junit:junit:4.+' } } ext { appName = 'example' project.version = thisVersion project.group = thisGroup project.description = 'example gradle application.' } dependencies { compile project(':settings') }
ここまで書いたら後は settings
ディレクトリを掘ってファイルを配置すればおk
Gradle4 + Jersey2 + Kotlin でRESTサービスを作ってみる
SPA でアプリ作ろう思って、サーバに当初 Spring 考えてたけど、こっちの方が API サーバが楽そうだった。
で、Kotlin は個人的な好み
教科書はこれ
JAX-RS(Jersey)+GradleでWebアプリを作る - Olivinecafe - blog
Gradle
汎用ビルドツール。
Java 専用ではないので、Java 書きたければ基本はプラグイン。
独自の DSL で記述するが、Java ライブラリとか普通に叩けた。
個人的に好きなのは、gradle wrapper
コマンドで、これを実行するとカレントディレクトリに gradlew
コマンドを吐き出す。
こいつは単独実行可能な gradle
コマンドなので、いちいち使う人に Gradle のインストールを強要しない。
sbt も大概 jar 一つでやってのけるのだが、こういう仕様がないと、複数人での開発がだるくて仕方ない。
とりあえず、build.gradle
はこんな感じで書いた。
apply plugin: 'maven' apply plugin: 'war' apply plugin: 'org.akhikhl.gretty' apply plugin: 'kotlin' def jerseyVersion = '2.25.1' sourceCompatibility = 1.7 buildscript { repositories { jcenter() mavenCentral() } dependencies { classpath 'org.akhikhl.gretty:gretty:+' classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.3' } } ext { project.version = '0.0.1' project.group = 'net.white-azalea' appName = 'example' project.description = 'example gradle application/' } configurations { all*.exclude module: 'servlet-api' } dependencies { providedCompile 'javax.ws.rs:javax.ws.rs-api:2.0.1' compile "org.glassfish.jersey.containers:jersey-container-servlet:${jerseyVersion}" testCompile 'junit:junit:4.+' } repositories { jcenter() mavenCentral() mavenLocal() }
とてもざっくり説明すると、
- リリース時は war でビルドするよ
- gretty プラグインを使って、デバック実行時に jetty サーバ使うよ
- Kotlin 言語でやるよ
しかも Java プラグイン入れてないので、Java 書けないよ servlet-api
は war には含まない様にしてるよ
これが含まれてると、Tomcat とかのサーバにデプロイした時バージョン衝突とかめんどくさいことになる場合がある- Jersey2.25.1 を必要とするよ
これを gradlew appRun
すると、依存する jar とか、実行サーバとか諸々勝手にダウンロードしてくる。
開発マシンにわざわざ手動で Tomcat 入れさせる環境とか、ネットワーク接続禁止環境だけでしょ…Web アプリ作るのにネットワーク禁止とかこれいかに?
jersey2
JAX-RS 実装らしいがなんのことはない。
アノテーションでパス指定すれば色々できる。
まずは、src/main/webapp/WEB-INF/web.xml
を記述する。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <servlet> <servlet-name>jersey-example</servlet-name> <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> <init-param> <param-name>jersey.config.server.provider.packages</param-name> <param-value>net.white_azalea</param-value> </init-param> <init-param> <param-name>jersey.config.server.provider.scanning.recursive</param-name> <param-value>true</param-value> </init-param> <load-on-startup>10</load-on-startup> </servlet> <servlet-mapping> <servlet-name>jersey-example</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
設定は全て Gradle にお任せモードする web.xml だ。
お次はソース。src/main/kotlin/net.white-azalea/Example.kt
を作成して以下を記述
package net.white_azalea import javax.ws.rs.GET import javax.ws.rs.Path import javax.ws.rs.Produces import javax.ws.rs.core.MediaType @Path("/hello") class Example { @GET @Produces(MediaType.TEXT_HTML) fun hello(): String = "Hello!" }
ここまできたら全部おしまい。
./gradlew appRun
して、http://localhost:8080/jersey-example/hello
にアクセスできれば OK.
jar 依存がだいぶ足りてないので、オブジェクト返すとコケる。
まぁその辺は この辺 みて自力解決しよう。
Redmine3 に Backlogs を入れるメモ
先日入れた Redmine 3.3 にBacklogs を突っ込んでみる。
教科書はこれ。
確かに Gem 競合を除いてしまえば入るっちゃ入る。
[root@localhost redmine]# RAILS_ENV=production bundle exec rake redmine:backlogs:install 3.3.4.stable.16875. You are running backlogs v1.0.6, latest version is 1.0.6 ===================================================== Redmine Backlogs Installer ===================================================== Installing to the production environment. Fetching card labels from http://git.gnome.org...done! Configuring story and task trackers... ----------------------------------------------------- Which trackers do you want to use for your stories? 1. ストーリー 2. バグ 3. 機能 4. サポート Separate values with a space (e.g. 1 3): 1 You selected the following trackers: ストーリー. Is this correct? (y/n) y ----------------------------------------------------- Which tracker do you want to use for your tasks? 1. バグ 2. 機能 3. サポート Choose one from above (or choose none to create a new tracker): 3 You selected サポート. Is this correct? (y/n) y Story and task trackers are now set. Migrating the database...WARNING: 進行中のトランザクションがありません done! Installation complete. Please restart Redmine. Thank you for trying out Redmine Backlogs! [root@localhost redmine]#