MySQLからRedisへのimport tool
この程度のことは何かしらの言語を使って、一瞬で作らなきゃだなぁ、と思って何が適当か少し考えてみた。
想定される制約条件は以下。
と色々(もしかすると上記条件以外にも頭の片隅にある暗黙の制約があるかも)考えるとJavaが良さげ。
で、シンプルに書くとこんな感じかな?ということでメモ書き。
- pom.xmlのdependencies
<dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.15</version> <type>jar</type> <scope>compile</scope> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>1.5.2</version> <type>jar</type> <scope>compile</scope> </dependency> </dependencies>
- Main.java(クラス名は適当, DBのユーザーとかも)
public class Main { public static void main(String[] args) throws SQLException { final String url = "jdbc:mysql://localhost/hogehoge"; final Jedis jedis = new Jedis("localhost"); Connection conn = DriverManager.getConnection(url, "root", "hogehoge"); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("select m.name, m.id, m.age, d.name from members m, depts d where d.id = m.dept_id"); while (rs.next()) { String key = rs.getString("m.name"); String val = "[age:" + rs.getInt("age") + "][dept:" + rs.getString("d.name") + "]"; jedis.set(key, val); } rs.close(); stmt.close(); jedis.disconnect(); } }
実行はMavenのexec pluginを使うとこんな感じ。
$ mvn exec:java -Dexec.mainClass=com.komamitsu.importer.Main
Hibernate触ってみたメモ
そろそろ仕事がらこの辺りもからんできそうなので家事育児の隙をついて弄ってみました。という備忘録的メモ。まぁ実際はSpring経由でやりそうなんですが、まず直接使ってみたいなぁと。
方針は以下。
-1. 事前準備 => RDBMS(今回、MySQL)にDatabaseとTableを作っとく
mysql> desc depts; +-------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | varchar(255) | YES | | NULL | | +-------+--------------+------+-----+---------+----------------+ 2 rows in set (0.00 sec) mysql> select * from depts; +----+-----------------+ | id | name | +----+-----------------+ | 1 | Development | | 2 | Sales | | 3 | Human Resources | +----+-----------------+ 3 rows in set (0.00 sec) mysql> desc members; +---------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +---------+-------------+------+-----+---------+-------+ | id | int(11) | YES | | NULL | | | name | varchar(64) | YES | | NULL | | | age | int(11) | YES | | NULL | | | dept_id | int(11) | YES | | NULL | | +---------+-------------+------+-----+---------+-------+ 4 rows in set (0.00 sec) mysql> select * from members; +------+------+------+---------+ | id | name | age | dept_id | +------+------+------+---------+ | 1 | aaa | 11 | 1 | | 2 | bbb | 12 | 1 | | 3 | ccc | 13 | 1 | | 4 | ddd | 21 | 2 | | 5 | eee | 22 | 2 | | 6 | fff | 23 | 2 | | 7 | ggg | 31 | 3 | | 8 | hhh | 32 | 3 | | 9 | iii | 33 | 3 | +------+------+------+---------+ 9 rows in set (0.04 sec)
0. Maven Projectの作成
mvnコマンド経由でも何でもいいんですが、今回はEclipseで作りました。普通に作ったので省略。
1. pom.xmlにdependenciesを書き書き
試行錯誤の末、こんな感じ。
<dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.15</version> <type>jar</type> <scope>compile</scope> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.16</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.4.2</version> <type>jar</type> <scope>compile</scope> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-annotations</artifactId> <version>3.4.0.GA</version> <type>jar</type> <scope>compile</scope> </dependency> <dependency> <groupId>javassist</groupId> <artifactId>javassist</artifactId> <version>3.12.1.GA</version> <type>jar</type> <scope>compile</scope> </dependency> </dependencies>
あまり良く解っていないのだけど、Logging周りがSlf4jというのになっていて、Log4jとのブリッジ的なものとしてslf4j-log4j12が必要っぽい。で、普通に入れると結構新しめなやつが入るのだけど、そうするとhibernate-coreが依存しているslf4j-apiとバージョン不一致を起こすので、一致させるようにしました。
あと、Hibernate-coreがこっそりとjavassistを使っているくせにpom上は依存していないみたいで、Runtimeでエラーが発生していたので、javassistも入れた、みたいな。
src/main/resources/hibernate.cfg.xmlを以下のように作成
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="connection.url">jdbc:mysql://localhost/hogehoge</property> <property name="connection.username">root</property> <property name="connection.password">hogehoge</property> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="dialect">org.hibernate.dialect.MySQLDialect</property> <property name="show_sql">true</property> <!-- JDBC connection pool (use the built-in) --> <property name="connection.pool_size">1</property> <property name="current_session_context_class">thread</property> <mapping class="com.komamitsu.model.Dept"/> <mapping class="com.komamitsu.model.Member"/> </session-factory> </hibernate-configuration>
3. entity的なclass的なもの
- Dept.java
package com.komamitsu.model; import java.io.Serializable; import java.util.Set; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.Table; @Entity @Table(name="depts") public class Dept implements Serializable { @Id @GeneratedValue private int id; private String name; @OneToMany(mappedBy="dept") private Set<Member> members; /** * @return the id */ public int getId() { return id; } /** * @param id the id to set */ public void setId(int id) { this.id = id; } /** * @return the name */ public String getName() { return name; } /** * @param name the name to set */ public void setName(String name) { this.name = name; } /** * @return the members */ public Set<Member> getMembers() { return members; } /** * @param members the members to set */ public void setMembers(Set<Member> members) { this.members = members; } @Override public String toString() { return "id=" + id + ", name=" + name; } }
- Member.java
package com.komamitsu.model; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; @Entity @Table(name="members") public class Member { @Id @GeneratedValue private int id; private String name; private int age; @ManyToOne @JoinColumn(name="dept_id") private Dept dept; /** * @return the id */ public int getId() { return id; } /** * @param id the id to set */ public void setId(int id) { this.id = id; } /** * @return the name */ public String getName() { return name; } /** * @param name the name to set */ public void setName(String name) { this.name = name; } /** * @return the age */ public int getAge() { return age; } /** * @param age the age to set */ public void setAge(int age) { this.age = age; } /** * @return the dept */ public Dept getDept() { return dept; } /** * @param dept the dept to set */ public void setDept(Dept dept) { this.dept = dept; } @Override public String toString() { return "id=" + id + ", name=" + name + ", age=" + age + ", dept=" + dept; } }
@OneToMany, @ManyToOneとか使ってみたかったので使えて良かったなぁという。
4. main()
package com.komamitsu.runner; import java.util.List; import org.hibernate.Criteria; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.AnnotationConfiguration; import org.hibernate.criterion.Restrictions; import com.komamitsu.model.Dept; import com.komamitsu.model.Member; public class Runner { /** * @param args */ public static void main(String[] args) { SessionFactory sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory(); Session session = sessionFactory.getCurrentSession(); Transaction tx = session.beginTransaction(); /* Dept dept = new Dept(); dept.setName("Development"); session.save(dept); */ Criteria c = session.createCriteria(Dept.class); c.add(Restrictions.eq("name", "Sales")); List<Dept> list = c.list(); System.out.println(list.size()); for (Dept d : list) for (Member m : d.getMembers()) System.out.println(m); tx.commit(); } }
まぁ普通な感じで。
5. 実行
Hibernate: select this_.id as id0_0_, this_.name as name0_0_ from depts this_ where this_.name=? 1 Hibernate: select members0_.dept_id as dept4_1_, members0_.id as id1_, members0_.id as id1_0_, members0_.age as age1_0_, members0_.dept_id as dept4_1_0_, members0_.name as name1_0_ from members members0_ where members0_.dept_id=? id=6, name=fff, age=23, dept=id=2, name=Sales id=4, name=ddd, age=21, dept=id=2, name=Sales id=5, name=eee, age=22, dept=id=2, name=Sales
show_sql=trueなのでそれと混じっていますが、こんな感じで。
まぁなんというか普通ですよね... 普通のくせに何か長いな... とか。
java.lang.NoClassDefFoundError: org/apache/log4j/Logger
ちゃんとpathが通っているにもかかわらず、Tomcat起動時にこのエラーが出まくるときは、ServerをCleanするとなおることがある。というメモ。
途方にくれたときは mvn help:describe
最近、Mavenを勉強しているのだけど、何をすれば良いのか分からなくなり途方にくれることが良くある。特に疲れているとき。
そんな時は, mvn help:describe -Dcmd=install とか打つと何か光が見えるような気がしないでもない。まぁ lsを一定間隔で打つようなものか。
DBアクセスのサンプル
Spring frameworkを使うとDBまわりのコードがすっきりするらしいので、練習兼メモがてらサンプルを書いてみた。
RDBMSは何でも良いのだけどPostgreSQLで。適当なテーブルを用意しておく。
sample=# \c psql (8.4.4) You are now connected to database "sample". sample=# \d users Table "public.users" Column | Type | Modifiers --------+-------------------+----------- id | integer | name | character varying | age | integer |
ValueObject(っていうの?)はこんなの。
public class User { private final int id; private final String name; private final int age; public User(int id, String name, int age) { this.id = id; this.name = name; this.age = age; } public int getId() { return id; } public String getName() { return name; } public int getAge() { return age; } }
で、肝心のDAOはこんな感じ。PreparedStatementが無いし、毎度毎度の例外処理も無くてすっきり。JdbcDaoSupport、MappingSqlQuery、SqlUpdate 辺りのクラスが鍵。
import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.util.List; import javax.sql.DataSource; import org.springframework.jdbc.core.SqlParameter; import org.springframework.jdbc.core.support.JdbcDaoSupport; import org.springframework.jdbc.object.MappingSqlQuery; import org.springframework.jdbc.object.SqlUpdate; public class UserDao extends JdbcDaoSupport { private static class FindUser extends MappingSqlQuery { private static final String SQL = "SELECT id, name, age FROM users"; private FindUser(DataSource ds) { super(ds, SQL); } @Override protected User mapRow(ResultSet rs, int rowNum) throws SQLException { return new User(rs.getInt("id"), rs.getString("name"), rs.getInt("age")); } } private static class InsertUser extends SqlUpdate { private static final String SQL = "INSERT INTO users (id, name, age)" + "VALUES (NEXTVAL('seq_users_id'), ?, ?)"; private InsertUser(DataSource ds) { super(ds, SQL); declareParameter(new SqlParameter("name", Types.VARCHAR)); declareParameter(new SqlParameter("age", Types.INTEGER)); } } private FindUser fu = null; private InsertUser iu = null; protected void initDao() { this.fu = new FindUser(getDataSource()); this.iu = new InsertUser(getDataSource()); } public List<User> findUser() { @SuppressWarnings("unchecked") List<User> users = fu.execute(); return users; } public void insertUser(String name, int age) { Object[] params = { name, age }; iu.update(params); } }
で、これらを利用するコードはこちら。
import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; public class Main { public static void main(String[] argv) { Resource resource = new ClassPathResource("applicationContext.xml"); BeanFactory bf = new XmlBeanFactory(resource); UserDao udao = (UserDao) bf.getBean("userDao"); udao.insertUser("Foo", 64); udao.insertUser("Bar", 72); for (User user : udao.findUser()) { System.out.printf("%3d: %s: %3d\n", user.getId(), user.getName(), user.getAge()); } } }
あと、DIの設定はこれ。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd"> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>org.postgresql.Driver</value></property> <property name="url"> <value>jdbc:postgresql://localhost/sample</value></property> <property name="username"> <value>komamitsu</value></property> </bean> <bean id="userDao" class="UserDao"> <property name="dataSource" ref="dataSource"></property> </bean> </beans>
これを実行すると。
1: Foo: 64 2: Bar: 72
となり成功。
ちょっと悔しいのは、UserDao.findUser()で警告を消すために@SuppressWarnings("unchecked")を使っていること。もう一回, Effective Javaのジェネリクスの章を読み直して、再挑戦したい。
Mixin的なもの
普段使いのRubyでは、ModuleのMixinによって実装の継承ができて大変嬉しいのですが、Javaの場合はできなさそうで少し悲しいなぁ、と思っていたところSpring AOPをつかうとそれっぽいことができそうなので試してみました。
まず、Mixinのことなど何も知らないUserクラスがあるとします。nameしか情報を持っていません。
package com.hoge; public class User { private String name; public void setName(String name) { this.name = name; } public String getName() { return name; } }
で、これに年齢に関する機能をつけたい場合、Spring AOPのIntroductionを使って、Ageインターフェースと...
package com.hoge; public interface Age { void setAge(int age); int getAge(); }
AgeMixinクラスを作ります。
package com.hoge; import org.springframework.aop.support. DelegatingIntroductionInterceptor; public class AgeMixin extends DelegatingIntroductionInterceptor implements Age { private static final long serialVersionUID = 1L; private int age; @Override public int getAge() { return age; } @Override public void setAge(int age) { this.age = age; } }
で、これらをくっつけます。
package com.hoge; import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.support. DefaultIntroductionAdvisor; public class Main { public static void main(String[] args) { ProxyFactory beanFactory = new ProxyFactory(new Class[] { Age.class }); beanFactory.setProxyTargetClass(true); beanFactory.addAdvisor( new DefaultIntroductionAdvisor(new AgeMixin())); beanFactory.setTarget(new User()); User user = (User) beanFactory.getProxy(); user.setName("komamitsu"); System.out.println(user.getName()); ((Age) user).setAge(18); System.out.println(((Age) user).getAge()); } }
setAge()を呼ぶときにAgeクラスにキャストしているところが悲しいですが、気を取り直して実行してみると...
komamitsu 18
と表示され、Ageの(set|get)Age()が使えているのが分かります。RubyのModuleと異なり、Mixin用のクラスは継承可能なので、もしかしたら何か嬉しいかも知れません。
Springを使ってみるメモ
簡単なサンプルを動かそうとするだけで結構はまったのでメモ。
前提とか事前準備:
- Ubuntu 10.04
- Eclipse 3.5 - aptで入れたやつ
- Tomcat 6 - http://tomcat.apache.orgからダウンロード(aptで入れると/usr/share/tomcat6と/var/lib/tomcat6でdirectoryが分割されていて、Eclipse WTPが認識しないので)
- spring-framework-2.5.6.SEC01-with-dependencies - http://www.springsource.com/download/community からダウンロードしてどっかに展開
手順:
- Help -> Install New Software
- Web, XML, and Java EE DevelopmentのInstall
- Web Tool Platform(WTP)のInstall
- Add http://download.eclipse.org/webtools/updates の後、それを選択
- Project provided components, WTP3.1.2を選択しNext
- AgreeしてFinish
- Spring IDEのInstall
- Add http://springide.org/updatesite/ の後、それを選択
- CoreとExtensions(どちらもSTS以外)を選択しNext
- AgreeしてFinish
- spring-framework-2.5.6.SEC01-with-dependenciesをプロジェクト化
- Serverの追加
- "New -> Other -> Server" から適当なもの(今回はTomcat6)を
- SpringSource dm Server とかも使える?
- 新規プロジェクト(非Webアプリの場合)
- "New -> Project -> Spring Project"
- libとか適当にディレクトリを作って、spring-frameworkプロジェクトからspring.jar, commons-logging.jarをコピって置く. そして "Build Path -> Add JARs" とかで追加しておく
- CLASSPATHの通ったところに "New -> Other -> Spring Bean Configuration File" を追加して、必要に応じて編集
- Beanを取り出すコードはこんな感じ
import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.*; : Resource res = new ClassPathResource("applicationContext.xml"); BeanFactory factory = new XmlBeanFactory(res); User komamitsu = (User)factory.getBean("komamitsu");
- 新規プロジェクト(Webアプリの場合)
<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
-
- ServletからBeanを取り出す場合は以下のような感じで
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(this.getServletContext()); User komamitsu = (User)wac.getBean("komamitsu");