女子中高生とTwitter4Jについて発表してきました。#twtr_hack
TwitterAPI勉強会にて、以下のLTをしてきました。
10分の発表なのに、スライド60枚以上作ってしまったので、どうなるかと思いましたが、想定通り1分6枚ペースで時間内に無事?終わりました。
また、タイトルで釣ることを目的にしていたので、中身薄くてゴメンナサイ。
当日のTogetterはこちら
http://togetter.com/li/292726
全体のまとめはこちら
第6回Twitter API勉強会を開催いたしました - ビデオ・スライドまとめ #twtr_hack
発表資料は以下になります。
Ustを録画してくださったので、以下に張っておきます。
TwitterAPIのエラーが多いので、それに対応するコードがカオスになってしまうから、旨いリトライのやり方あったら教えて下さいと言ったら普通にTwitter4Jに実装されていたという恥ずかしいオチでしたが、勉強になりました。リファレンス読めってことですね。ありがとうございました。
.@i2key Twitter4Jでhttp.retryCount パラメータを1以上に設定すればリトライしてくれます twitter4j.org/en/configurati… #twtr_hack
— ゆーすけさん (@yusukey) 4月 24, 2012
関連情報
インフラ構成で軽く触れましたが、AWSのAutoScalingについてはこちらで記事を書いていますので、ご参考になれば。
Amazon WebserviceのAuto Scalingの設定について
Scalaで自動フォロー返し書いたら凄く簡単になった件はこちらに記事を書いています。ご参考になれば。
Javaで書かれたTwitterの自動フォロー返しを、Scalaで書いてみた #twitter4j #twtr_hack
今回MyBatisというORマッパー(HibernateほどなORマップではない)を使いました。某SIerの社内フレームワークはiBatisを使っていて、SQL文をxmlに記述していく(またはエクセル方眼紙からそのxmlを自動生成している)と思いますが、MyBatisはxmlを捨ててアノテーションでSQLを記述します。
※ちなみにMyBatisはiBatisが大人の事情でApache系列からGoogleCodeProjectに移ることになった際にネーミングが変わっています。
私は、自分でSQLを書きたい派なので、JPA的なインタフェースが少し苦手(笑)で、SQL感が強いMyBatisを採用しました。SpringのTransactionTemplateでもいいじゃんとなるのですが、MyBatisの付属品である、Generatorが便利だったので。
http://code.google.com/p/mybatis/wiki/Generator
データベースにあるテーブル情報から単純なCRUDアクセスパターンはSQL及びそれをラップしたDAOクラスを自動生成してくれます。
例えば、userId、userName、photoUrlのカラムを持つUSERテーブルに対してCRUD生成を行うと以下のようなクラスが生成されます。
public interface UserMapper { /** * This method was generated by MyBatis Generator. * This method corresponds to the database table user * * @mbggenerated Mon Oct 03 15:41:07 JST 2011 */ @SelectProvider(type=UserSqlProvider.class, method="countByExample") int countByExample(UserExample example); /** * This method was generated by MyBatis Generator. * This method corresponds to the database table user * * @mbggenerated Mon Oct 03 15:41:07 JST 2011 */ @DeleteProvider(type=UserSqlProvider.class, method="deleteByExample") int deleteByExample(UserExample example); /** * This method was generated by MyBatis Generator. * This method corresponds to the database table user * * @mbggenerated Mon Oct 03 15:41:07 JST 2011 */ @Delete({ "delete from user", "where userId = #{userId,jdbcType=VARCHAR}" }) int deleteByPrimaryKey(UserKey key); /** * This method was generated by MyBatis Generator. * This method corresponds to the database table user * * @mbggenerated Mon Oct 03 15:41:07 JST 2011 */ @Insert({ "insert into user (userId, userName, ", "photoUrl)", "values (#{userId,jdbcType=VARCHAR}, #{userName,jdbcType=VARCHAR}, ", "#{photoUrl,jdbcType=LONGVARCHAR})" }) int insert(UserWithBLOBs record); /** * This method was generated by MyBatis Generator. * This method corresponds to the database table user * * @mbggenerated Mon Oct 03 15:41:07 JST 2011 */ @InsertProvider(type=UserSqlProvider.class, method="insertSelective") int insertSelective(UserWithBLOBs record); /** * This method was generated by MyBatis Generator. * This method corresponds to the database table user * * @mbggenerated Mon Oct 03 15:41:07 JST 2011 */ @Select({ "select", "userId, userName, photoUrl", "from user", "where userId = #{userId,jdbcType=VARCHAR}" }) @Results({ @Result(column="userId", property="userId", jdbcType=JdbcType.VARCHAR, id=true), @Result(column="userName", property="userName", jdbcType=JdbcType.VARCHAR), @Result(column="photoUrl", property="photoUrl", jdbcType=JdbcType.LONGVARCHAR) }) UserWithBLOBs selectByPrimaryKey(UserKey key); /** * This method was generated by MyBatis Generator. * This method corresponds to the database table user * * @mbggenerated Mon Oct 03 15:41:07 JST 2011 */ @UpdateProvider(type=UserSqlProvider.class, method="updateByExampleSelective") int updateByExampleSelective(@Param("record") UserWithBLOBs record, @Param("example") UserExample example); /** * This method was generated by MyBatis Generator. * This method corresponds to the database table user * * @mbggenerated Mon Oct 03 15:41:07 JST 2011 */ @UpdateProvider(type=UserSqlProvider.class, method="updateByExampleWithBLOBs") int updateByExampleWithBLOBs(@Param("record") UserWithBLOBs record, @Param("example") UserExample example); /** * This method was generated by MyBatis Generator. * This method corresponds to the database table user * * @mbggenerated Mon Oct 03 15:41:07 JST 2011 */ @UpdateProvider(type=UserSqlProvider.class, method="updateByExample") int updateByExample(@Param("record") User record, @Param("example") UserExample example); /** * This method was generated by MyBatis Generator. * This method corresponds to the database table user * * @mbggenerated Mon Oct 03 15:41:07 JST 2011 */ @UpdateProvider(type=UserSqlProvider.class, method="updateByPrimaryKeySelective") int updateByPrimaryKeySelective(UserWithBLOBs record); /** * This method was generated by MyBatis Generator. * This method corresponds to the database table user * * @mbggenerated Mon Oct 03 15:41:07 JST 2011 */ @Update({ "update user", "set userName = #{userName,jdbcType=VARCHAR},", "photoUrl = #{photoUrl,jdbcType=LONGVARCHAR}", "where userId = #{userId,jdbcType=VARCHAR}" }) int updateByPrimaryKeyWithBLOBs(UserWithBLOBs record); /** * This method was generated by MyBatis Generator. * This method corresponds to the database table user * * @mbggenerated Mon Oct 03 15:41:07 JST 2011 */ @Update({ "update user", "set userName = #{userName,jdbcType=VARCHAR}", "where userId = #{userId,jdbcType=VARCHAR}" }) int updateByPrimaryKey(User record); }
こちらが動的にSQLをつくる必要があるものです。上記クラスからアノテーション経由で利用されます。
public class UserSqlProvider { /** * This method was generated by MyBatis Generator. * This method corresponds to the database table user * * @mbggenerated Mon Oct 03 15:41:07 JST 2011 */ public String countByExample(UserExample example) { BEGIN(); SELECT("count (*)"); FROM("user"); applyWhere(example, false); return SQL(); } /** * This method was generated by MyBatis Generator. * This method corresponds to the database table user * * @mbggenerated Mon Oct 03 15:41:07 JST 2011 */ public String deleteByExample(UserExample example) { BEGIN(); DELETE_FROM("user"); applyWhere(example, false); return SQL(); } /** * This method was generated by MyBatis Generator. * This method corresponds to the database table user * * @mbggenerated Mon Oct 03 15:41:07 JST 2011 */ public String insertSelective(UserWithBLOBs record) { BEGIN(); INSERT_INTO("user"); if (record.getUserId() != null) { VALUES("userId", "#{userId,jdbcType=VARCHAR}"); } if (record.getUserName() != null) { VALUES("userName", "#{userName,jdbcType=VARCHAR}"); } if (record.getPhotoUrl() != null) { VALUES("photoUrl", "#{photoUrl,jdbcType=LONGVARCHAR}"); } return SQL(); } /** * This method was generated by MyBatis Generator. * This method corresponds to the database table user * * @mbggenerated Mon Oct 03 15:41:07 JST 2011 */ public String updateByExampleSelective(Map<String, Object> parameter) { UserWithBLOBs record = (UserWithBLOBs) parameter.get("record"); UserExample example = (UserExample) parameter.get("example"); BEGIN(); UPDATE("user"); if (record.getUserId() != null) { SET("userId = #{record.userId,jdbcType=VARCHAR}"); } if (record.getUserName() != null) { SET("userName = #{record.userName,jdbcType=VARCHAR}"); } if (record.getPhotoUrl() != null) { SET("photoUrl = #{record.photoUrl,jdbcType=LONGVARCHAR}"); } applyWhere(example, true); return SQL(); } /** * This method was generated by MyBatis Generator. * This method corresponds to the database table user * * @mbggenerated Mon Oct 03 15:41:07 JST 2011 */ public String updateByExampleWithBLOBs(Map<String, Object> parameter) { BEGIN(); UPDATE("user"); SET("userId = #{record.userId,jdbcType=VARCHAR}"); SET("userName = #{record.userName,jdbcType=VARCHAR}"); SET("photoUrl = #{record.photoUrl,jdbcType=LONGVARCHAR}"); UserExample example = (UserExample) parameter.get("example"); applyWhere(example, true); return SQL(); } /** * This method was generated by MyBatis Generator. * This method corresponds to the database table user * * @mbggenerated Mon Oct 03 15:41:07 JST 2011 */ public String updateByExample(Map<String, Object> parameter) { BEGIN(); UPDATE("user"); SET("userId = #{record.userId,jdbcType=VARCHAR}"); SET("userName = #{record.userName,jdbcType=VARCHAR}"); UserExample example = (UserExample) parameter.get("example"); applyWhere(example, true); return SQL(); } /** * This method was generated by MyBatis Generator. * This method corresponds to the database table user * * @mbggenerated Mon Oct 03 15:41:07 JST 2011 */ public String updateByPrimaryKeySelective(UserWithBLOBs record) { BEGIN(); UPDATE("user"); if (record.getUserName() != null) { SET("userName = #{userName,jdbcType=VARCHAR}"); } if (record.getPhotoUrl() != null) { SET("photoUrl = #{photoUrl,jdbcType=LONGVARCHAR}"); } WHERE("userId = #{userId,jdbcType=VARCHAR}"); return SQL(); } /** * This method was generated by MyBatis Generator. * This method corresponds to the database table user * * @mbggenerated Mon Oct 03 15:41:07 JST 2011 */ protected void applyWhere(UserExample example, boolean includeExamplePhrase) { if (example == null) { return; } String parmPhrase1; String parmPhrase1_th; String parmPhrase2; String parmPhrase2_th; String parmPhrase3; String parmPhrase3_th; if (includeExamplePhrase) { parmPhrase1 = "%s #{example.oredCriteria[%d].allCriteria[%d].value}"; parmPhrase1_th = "%s #{example.oredCriteria[%d].allCriteria[%d].value,typeHandler=%s}"; parmPhrase2 = "%s #{example.oredCriteria[%d].allCriteria[%d].value} and #{example.oredCriteria[%d].criteria[%d].secondValue}"; parmPhrase2_th = "%s #{example.oredCriteria[%d].allCriteria[%d].value,typeHandler=%s} and #{example.oredCriteria[%d].criteria[%d].secondValue,typeHandler=%s}"; parmPhrase3 = "#{example.oredCriteria[%d].allCriteria[%d].value[%d]}"; parmPhrase3_th = "#{example.oredCriteria[%d].allCriteria[%d].value[%d],typeHandler=%s}"; } else { parmPhrase1 = "%s #{oredCriteria[%d].allCriteria[%d].value}"; parmPhrase1_th = "%s #{oredCriteria[%d].allCriteria[%d].value,typeHandler=%s}"; parmPhrase2 = "%s #{oredCriteria[%d].allCriteria[%d].value} and #{oredCriteria[%d].criteria[%d].secondValue}"; parmPhrase2_th = "%s #{oredCriteria[%d].allCriteria[%d].value,typeHandler=%s} and #{oredCriteria[%d].criteria[%d].secondValue,typeHandler=%s}"; parmPhrase3 = "#{oredCriteria[%d].allCriteria[%d].value[%d]}"; parmPhrase3_th = "#{oredCriteria[%d].allCriteria[%d].value[%d],typeHandler=%s}"; } StringBuilder sb = new StringBuilder(); List<Criteria> oredCriteria = example.getOredCriteria(); boolean firstCriteria = true; for (int i = 0; i < oredCriteria.size(); i++) { Criteria criteria = oredCriteria.get(i); if (criteria.isValid()) { if (firstCriteria) { firstCriteria = false; } else { sb.append(" or "); } sb.append('('); List<Criterion> criterions = criteria.getAllCriteria(); boolean firstCriterion = true; for (int j = 0; j < criterions.size(); j++) { Criterion criterion = criterions.get(j); if (firstCriterion) { firstCriterion = false; } else { sb.append(" and "); } if (criterion.isNoValue()) { sb.append(criterion.getCondition()); } else if (criterion.isSingleValue()) { if (criterion.getTypeHandler() == null) { sb.append(String.format(parmPhrase1, criterion.getCondition(), i, j)); } else { sb.append(String.format(parmPhrase1_th, criterion.getCondition(), i, j,criterion.getTypeHandler())); } } else if (criterion.isBetweenValue()) { if (criterion.getTypeHandler() == null) { sb.append(String.format(parmPhrase2, criterion.getCondition(), i, j, i, j)); } else { sb.append(String.format(parmPhrase2_th, criterion.getCondition(), i, j, criterion.getTypeHandler(), i, j, criterion.getTypeHandler())); } } else if (criterion.isListValue()) { sb.append(criterion.getCondition()); sb.append(" ("); List<?> listItems = (List<?>) criterion.getValue(); boolean comma = false; for (int k = 0; k < listItems.size(); k++) { if (comma) { sb.append(", "); } else { comma = true; } if (criterion.getTypeHandler() == null) { sb.append(String.format(parmPhrase3, i, j, k)); } else { sb.append(String.format(parmPhrase3_th, i, j, k, criterion.getTypeHandler())); } } sb.append(')'); } } sb.append(')'); } } if (sb.length() > 0) { WHERE(sb.toString()); } } }
使うにはSpringのBean定義に以下を書くだけ
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="configLocation" value="classpath:MyBatisConfig.xml" /> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactory" ref="sqlSessionFactory"/> <property name="basePackage" value= "生成したDAOがおかれるパッケージ" /> </bean>
生成されたDAOクラスは、当然GenerationGapパターンを適用できるので、自動生成したクラスをスーパークラスとして、個別要件(手動による追記)をextendsした側に記述してあげることで、CRUDの再生成が可能です。