プログラマ38の日記

主にプログラムメモです。

MySQL: 8.0でJDBCでの利用時にはまったこと

JDBCを使ってMySQLを使っていますが、MySQL 8.0 でいくつかつまづいたのでメモです。

 

1.JDBCドライバのクラス名が変更されている

com.mysql.cj.jdbc.Driver  以前はcom.mysql.jdbc.Driver

 

2.JDBCパラメータが追加が必要です。(次の記載は私がよく使うものです)

プロパティ 説明
allowLoadLocalInfile true

MySQL 8.0で追加

次を使うためにセット  ※他にサーバ側の設定も必要

LOAD DATA LOCAL INFILE 'ファイル名'
INTO TABLE テーブル名
~;

useSSL false

MySQL 8.0で追加

自分の環境では通信の暗号化はしていないためfalse 

serverTimezone JST

MySQL 8.0で追加

自分の環境ではMySQLをインストールしたPCの タイムゾーンが日本時間のためJSTをセット。(インストールしたサーバのタイムゾーンGMTならGMTでセットとなる)

characterEncoding utf8 文字化けを回避するためutf8をセット
rewriteBatchedStatements true JDBCの、PreparedStatement.addBatch  / executeBatch の速度が劇的に早くなるためセット(javaベースのETLツールなどを使う場合はほぼ必須の設定です)
useCursorFetch true これを使わないと大量データがさばけない 
defaultFetchSize 1000 これを使わないと大量データがさばけない 

 

  ※他のサーバ側の設定

my.ini に次の記載が必要です。

local_infile=ON

 

最後に

上記でいくつかはまりましたが、MySQL 8.0で分析関数が使えるようになってとても使いやすいです。

 

row_number() over() で連番を使えるようになっただけでも助かるのに、

row_number() over(partition by ブレークする項目 order by 並び替え項目) で項目単位に1から番号ふることができます。(縦横変換が助かります) 

 

 

雑記: 繊細さん(HSP)の可能性が高い自分の傾向と対策

オリラジの中田さんのyoutube動画を見て繊細さん(HSP)という言葉を初めて聞きました。

 

動画を見たり、その他のWebサイトを見たりしてどうやら自分は繊細さんなんだろうなと感じました。

繊細さんの特徴と、自分の現在の傾向や対策をメモしておこうと思います。

 

自己肯定感が低い

これはほんとにその通りです。自分の強みとか、アピールの仕方とかわからないまま生きていくことになりそうです。小さいことからできたこと/できることを認めて行って自己肯定感をあげていこうとはしているんですがなかなか難しいです。無理せず気長に続けていこうと思っています。

 

他人に共感しやすい

共感するという言葉が当てはまるわけではないですが、この人はきっとここでつまずくだろうなとか頭をよぎるので、先にフォローするということをやりがちです。そして、それは自分を犠牲にすることが多い。

全体を通してうまくいくならここでの自分の負担は価値があるという判断をよくします。(そして、その自分が負担したということは自分しか知らないということになります)

やっかいな性格だなとずっと思っていましたが、中田さんの動画では"困ってそうな人を助けなくていい"と言っていてとても気に入りました。勝手に先読みはしないできちんと状況を見るということを大切にしようと思いました。

 

短時間にしなければならないことが多いとオロオロしてしまう

これは、ほんとにその通りで身につまされます。1つのやることに対して色々なことを考えてあれをやってこれをやってと忙しく考えてしまうのに、それが複数あるとすぐに混乱します。

さらに困るのが、1つのことに着手している時に、ふっと別のことについての考えが頭をよぎることが多いことです。そうするとさらにあれもこれもとなるので悪循環に陥っていきます。

やることを整理することが苦手なのと、ひとつひとつに落ち着いて取り組めない人間なんだと自分を分析していました。

最近は、ノートを使って思いついたことを書いて後で整理するというやり方を取るようになってだいぶ改善されました。

 

Depth Of Processing

物事を深く、複雑に考える癖がある。これはそうなんですが、プログラマという職業ではとてもメリットを感じています。1つの記載を様々な場合分けして考えることは得意で好きな工程です。

 

その他

自分は他の人と同じことをするのをひどく嫌がるようです。さすがに義務教育ではちゃんとできていましたが、それ以降はみんなで同じ時間に同じ演習をするなど非常に辛さを感じました。ループワークがほんとに辛いです。

みんなで同じことをできるようになることに意味があるのか?(数人がそれをできるなら、ほかの人は別のことをできるほうがいいんじゃないか)などと考えをめぐらせてしまいます。今までそれは自分に都合のいい甘えなんだろうなとうっすらと感じてきましたが、そういうことを考えやすい傾向を持っていると理解ができてよかったです。

 

最後に

現時点で自分が感じていることを色々書いてみました。HSPは病気ではないので治すことはできないもののようです。グ、自分の傾向を知って日々の生活に役立てたいなと思います。

Salesforce:標準項目の選択リスト値をリリースする

標準項目の選択リスト値は今まで手動で行っていましたが、メタデータのデプロイでリリースができることを知ったのでメモとして残します。(API48時点での記載です)

 

メタデータの種類は「StandardValueSet」で、このメタデータは取得方法が他のメタデータと違います。
取得する場合は、次のヘルプから項目を選択することになります。

developer.salesforce.com

取得はAnt移行ツールが使いやすくretrieveタスクでのpackage.xmlの指定は次になります。

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>AccountContactMultiRoles</members>
        <members>AccountContactRole</members>
        <members>AccountOwnership</members>
        <members>AccountRating</members>
        <members>AccountType</members>
        <members>AssetStatus</members>
        <members>CampaignMemberStatus</members>
        <members>CampaignStatus</members>
        <members>CampaignType</members>
        <members>CaseContactRole</members>
        <members>CaseOrigin</members>
        <members>CasePriority</members>
        <members>CaseReason</members>
        <members>CaseStatus</members>
        <members>CaseType</members>
        <members>ContactRole</members>
        <members>ContractContactRole</members>
        <members>ContractStatus</members>
        <members>EntitlementType</members>
        <members>EventSubject</members>
        <members>EventType</members>
        <members>FiscalYearPeriodName</members>
        <members>FiscalYearPeriodPrefix</members>
        <members>FiscalYearQuarterName</members>
        <members>FiscalYearQuarterPrefix</members>
        <members>IdeaCategory1</members>
        <members>IdeaMultiCategory</members>
        <members>IdeaStatus</members>
        <members>IdeaThemeStatus</members>
        <members>Industry</members>
        <members>LeadSource</members>
        <members>LeadStatus</members>
        <members>OpportunityCompetitor</members>
        <members>OpportunityStage</members>
        <members>OpportunityType</members>
        <members>OrderStatus</members>
        <members>OrderType</members>
        <members>PartnerRole</members>
        <members>Product2Family</members>
        <members>QuestionOrigin1</members>
        <members>QuickTextCategory</members>
        <members>QuickTextChannel</members>
        <members>QuoteStatus</members>
        <members>RoleInTerritory2</members>
        <members>SalesTeamRole</members>
        <members>Salutation</members>
        <members>ServiceContractApprovalStatus</members>
        <members>SocialPostClassification</members>
        <members>SocialPostEngagementLevel</members>
        <members>SocialPostReviewedStatus</members>
        <members>SolutionStatus</members>
        <members>TaskPriority</members>
        <members>TaskStatus</members>
        <members>TaskSubject</members>
        <members>TaskType</members>
        <members>WorkOrderLineItemStatus</members>
        <members>WorkOrderPriority</members>
        <members>WorkOrderStatus</members>
        <name>StandardValueSet</name>
    </types>
    <version>48.0</version>
</Package>

 

 上記以外にも次の標準項目の選択リスト値が取得できるようです。これ以外にも標準項目の選択リスト値は存在するのですが、どう指定すれば取得できるかわかりませんでした。

メタデータの指定 オブジェクト 項目
ラベル 名前 ラベル 項目
DigitalAssetStatus 納入商品 Asset DigitalAssetStatus DigitalAssetStatus
AssetRelationshipType 納入商品リレーション AssetRelationship リレーション種別 RelationshipType
ChannelProgramCategory チャネルプログラム項目 ChannelProgram カテゴリ Category
UnitOfMeasure ConsumptionSchedule ConsumptionSchedule 測定単位 UnitOfMeasure
ContactRequestReason 問い合わせ要求 ContactRequest 要求理由 RequestReason
ContactRequestStatus 問い合わせ要求 ContactRequest 要求の状況 Status
MilitaryService 個人 Individual 兵役 MilitaryService
PartnerFundRequestActivity パートナー資金リクエス PartnerFundRequest アクティビティ Activity
QuantityUnitOfMeasure 商品 Product2 基準数量単位 QuantityUnitOfMeasure
ScorecardMetricCategory スコアカード総計値項目 ScorecardMetric カテゴリ Category

 

最後に

今までリードの状況/商談のフェーズ/ケースの状況など、リリースできないと思っていて手動で色々設定していました。この「StandardValueSet」でのデプロイを行えば、取引の開始済み/確度/完了フラグ といったその標準項目専用の属性もリリースできるのでとても便利だなと思いました。

Salesforce:Trigger.isDelete での before と after の違いではまったこと

今さらですが、Deleteトリガでの before と after の違いではまりました。

 

はまったこと

f:id:crmprogrammer38:20200525071847p:plain

f:id:crmprogrammer38:20200525072216p:plain

上記のように、オブジェクトBはオブジェクトAを参照しています。
オブジェクトBのオブジェクトAへの参照関係項目の「参照レコードが削除された場合の対処方法」は”この項目の値をクリアします。”としています。

この時にオブジェクトAを削除した時のDeleteトリガのbefore , after の違いで次でした。

before delete : オブジェクトBのオブジェクトAへの参照関係項目の値はセットされている

after delete:オブジェクトBのオブジェクトAへの参照関係項目の値がクリアされている。


なので、オブジェクトBの参照関係項目を使ってオブジェクトAが削除された時に関連するオブジェクトBも削除する場合、 before delete でオブジェクトBを検索して削除する必要がありました。

 

次のトリガの実行の順序は理解していてつもりですが、関連オブジェクトの参照関係項目の値のクリアについては正しく理解できていませんでした。

developer.salesforce.com

 

MySQL: lower_case_table_names の違いではまった

テーブルの大文字と小文字を区別するパラメータ「lower_case_table_names」の違いではまりました。

はまったのは次です。

 

lower_case_table_names=0 の場合(Linuxなどの場合)

  • テーブル名は、大文字小文字を区別するのでキャメルケースを利用している場合、正しくテーブル名を扱う必要がある。
  • SQLでテーブルに別名を付けた場合、その別名も大文字小文字を区別する必要がある。

 

WindowsMySQLから、LinuxMySQLへ移行する場合、とても注意が必要です。

WindowsMySQLで開発する際は、lower_case_table_names=1 となりテーブルは大文字小文字を区別せず、テーブルの別名も大文字小文字を区別しません。

テーブル名にキャメルケースを利用したとしても、テーブル作成時に小文字に変換されます。

 

そしてその定義をそのままLinuxMySQLへ持っていくと、大文字小文字を意識していないSQLはことごとくエラーとなります。(そして、テーブルがないというメッセージだけなので、エラーの理由に気づきずらいので注意です)

 

 

 

MySQL: 何も考えずにテーブル文字セットをutf8にしてはまった

文字コードはutf8にしておけば問題ないだろうと甘い認識で、MySQLではまりました。

 

create table sample1 ( column1 varchar(100)) default charset=utf8;

 

上記のようにテーブル文字セットをutf8にしていると、文字化けしている文字、外字、4バイト文字がエラーになります。

 

create table sample1 ( column1 varchar(100)) default charset=utf8mb4;

 

指定するなら上記のようにutf8mb4を指定します。

 

データベースの文字セットは正しく理解してからセットするものだと理解していたつもりですが、はまってしまいました。

そもそも、何も指定しなければutf8mb4になる場合が多いのに、あえて指定して間違ってしまい情けなさを感じました。正しく理解する重要性をあらためて認識しました。

 

Salesforce: メタデータAPIの renameMetadata を使って 統一感のあるfullNameにする

急いで開発していて、設定の物理名(API名やdeveloper名など)はあまり考えている時間がなかったり、ネーミングルールを後から作ったため、それまでの設定がルールとあっていなかったりする場合があります。

 

メンテナンス性を考えてそれまでの設定の物理名を変更するのにメタデータAPIの renameMetadata が便利です。(ラベルの変更には、deploy もしくは、 updateMetadata を使います)

 

jaxws-ri-2.3.2 を使ったサンプルが次になります。
※Lightning Platform Web Services Connector (WSC) でも良かったのですがあまり面白みがなかったのでこちらにしました。

jaxws-ri の使い方については、以前記事を書きました。

 

crmprogrammer38.hatenablog.com

 

サンプルコード  対象のメタデータタイプは一部のものになります。

以下のソースは次の環境で動作します。 

java8,  jaxws-ri-2.3.2, wsimportはjava8のものを使いました。

オブジェクトの物理名はObject__cとして表記しています。

メタデータタイプは例として次をピックアップしました。

  • レコードタイプ
  • カスタム項目
  • リストビュー
  • 入力規則
  • ワークフロールール
  • メールアラート
  • 項目自動更新
  • Lightningレコードページ、ユーティリティバー、ホームページ
  • アクション
  • グローバルアクション
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.handler.MessageContext;

import com.sforce.soap._2006._04.metadata.CustomField;
import com.sforce.soap._2006._04.metadata.FlexiPage;
import com.sforce.soap._2006._04.metadata.ListView;
import com.sforce.soap._2006._04.metadata.MetadataPortType;
import com.sforce.soap._2006._04.metadata.MetadataService;
import com.sforce.soap._2006._04.metadata.QuickAction;
import com.sforce.soap._2006._04.metadata.SaveResult;
import com.sforce.soap._2006._04.metadata.ValidationRule;
import com.sforce.soap._2006._04.metadata.WorkflowAlert;
import com.sforce.soap._2006._04.metadata.WorkflowFieldUpdate;
import com.sforce.soap._2006._04.metadata.WorkflowRule;
import com.sforce.soap.partner.LoginResult;
import com.sforce.soap.partner.SforceService;
import com.sforce.soap.partner.Soap;
import com.sun.xml.ws.api.message.Header;
import com.sun.xml.ws.api.message.Headers;
import com.sun.xml.ws.developer.WSBindingProvider;

public class Main {

  public static Soap soapBinding = new SforceService().getSoap();
  public static MetadataPortType metadataBinding = new MetadataService().getMetadata();

  public static String endpoint = "https://login.salesforce.com/services/Soap/u/48.0";
  public static String username = "sample@user.name";
  public static String password = "samplepassword";
  
  public static void main(String[] args) throws Exception {
    
    login();
    
    //レコードタイプ
    rename(RecordType.class.getSimpleName(), "Object__c.fullName1" , "Object__c.fullName2");
    //カスタム項目
    rename(CustomField.class.getSimpleName(), "Object__c.fullName1__c" , "Object__c.fullName2__c");
    //リストビュー
    rename(ListView.class.getSimpleName(), "Object__c.fullName1" , "Object__c.fullName2");
    //入力規則
    rename(ValidationRule.class.getSimpleName(), "Object__c.fullName1" , "Object__c.fullName2");
    //ワークフロールール
    rename(WorkflowRule.class.getSimpleName(), "Object__c.fullName1" , "Object__c.fullName2");
    //メールアラート
    rename(WorkflowAlert.class.getSimpleName(), "Object__c.fullName1" , "Object__c.fullName2");
    //項目自動更新
    rename(WorkflowFieldUpdate.class.getSimpleName(), "Object__c.fullName1" , "Object__c.fullName2");
    //Lightningレコードページ、ユーティリティバー、ホームページ
    rename(FlexiPage.class.getSimpleName(), "fullName1" , "fullName2");
    //アクション
    rename(QuickAction.class.getSimpleName(), "Object__c.fullName1" , "Object__c.fullName2");
    //グローバルアクション
    rename(QuickAction.class.getSimpleName(), "fullName1" , "fullName2");

  }
  
  public static void rename(String metadataType, String oldname, String newname) {
    boolean resultSuccess = false;
    String errorMessage = "";
    
    try{
      SaveResult sr = metadataBinding.renameMetadata(metadataType, oldname, newname);
      
      if(sr.isSuccess()){
        resultSuccess = true;
      } else {
        errorMessage = sr.getErrors().get(0).getMessage();
      }
    
    }catch(Exception e){
      errorMessage = e.getMessage();
    }
    
    
    LinkedHashMap<String, String> info= new LinkedHashMap<>();
    info.put("metadataType", metadataType);
    info.put("oldname", oldname);
    info.put("newname", newname);
    
    if( resultSuccess ){
      System.out.println("success : " + info );
    } else {
      System.out.println("error   : " + info + " error : " + errorMessage );
    }
  }
  
  
  public static void login() throws Exception{

    

    WSBindingProvider provider = (WSBindingProvider) soapBinding;

    Map<String,Object> reqContext = provider.getRequestContext();
    reqContext.put(WSBindingProvider.ENDPOINT_ADDRESS_PROPERTY,endpoint        );
    reqContext.put("javax.xml.ws.client.connectionTimeout", "180");
    reqContext.put("javax.xml.ws.client.receiveTimeout", "180");
    
    

    // Enable GZip compression
    Map<String, List<String>> httpHeaders = new HashMap<String, List<String>>();
    httpHeaders.put("Content-Encoding", Collections.singletonList("gzip"));
    httpHeaders.put("Accept-Encoding", Collections.singletonList("gzip"));
    reqContext.put(MessageContext.HTTP_REQUEST_HEADERS, httpHeaders);

    LoginResult loginResult = soapBinding.login(username,password);

    String serverUrl = loginResult.getServerUrl();
    String metadataUrl = loginResult.getMetadataServerUrl();
    String sessionId = loginResult.getSessionId();

    reqContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, serverUrl);

    com.sforce.soap.partner.SessionHeader sh = new com.sforce.soap.partner.SessionHeader();
    sh.setSessionId(sessionId);

    JAXBContext jaxbContext = null;
    try {
      jaxbContext = JAXBContext.newInstance(com.sforce.soap.partner.ObjectFactory.class);
    } catch (JAXBException e) {
    }

    ArrayList<Header> sessionheaderlst = new ArrayList<Header>();
    sessionheaderlst.add(Headers.create((JAXBContext) jaxbContext, sh));
    provider.setOutboundHeaders(sessionheaderlst);
    
    buildMetadataBinding(metadataUrl, sessionId);
    


  }
  
  public static void buildMetadataBinding(String metadataUrl, String sessionId) throws Exception{
    WSBindingProvider provider = (WSBindingProvider) metadataBinding;
    Map<String,Object> reqContext = provider.getRequestContext();
    reqContext.put(WSBindingProvider.ENDPOINT_ADDRESS_PROPERTY,metadataUrl        );
    reqContext.put("javax.xml.ws.client.connectionTimeout", "180");
    reqContext.put("javax.xml.ws.client.receiveTimeout", "180");
    // Enable GZip compression
    Map<String, List<String>> httpHeaders = new HashMap<String, List<String>>();
    httpHeaders.put("Content-Encoding", Collections.singletonList("gzip"));
    httpHeaders.put("Accept-Encoding", Collections.singletonList("gzip"));
    reqContext.put(MessageContext.HTTP_REQUEST_HEADERS, httpHeaders);
    
    com.sforce.soap._2006._04.metadata.SessionHeader sh = new com.sforce.soap._2006._04.metadata.SessionHeader();
    sh.setSessionId(sessionId);

    JAXBContext jaxbContext = null;
    try {
      jaxbContext = JAXBContext.newInstance(com.sforce.soap._2006._04.metadata.ObjectFactory.class);
    } catch (JAXBException e) {
    }

    ArrayList<Header> sessionheaderlst = new ArrayList<Header>();
    sessionheaderlst.add(Headers.create((JAXBContext) jaxbContext, sh));
    provider.setOutboundHeaders(sessionheaderlst);
    
  }

}

最後に

よくやってしまいがちなのが、LightningレコードページでFlexiPage1などシステムで自動で付与されたものをそのまま使ってしまったりなどです。

また、ユーティリティバーについては、身に覚えがないのにUtilityBar1などとなってしまうので後から修正することも多いのかなと思います。