***

13706 ワード

概要
以前の記事ではmybatisの基本的な構成とアプリケーションについて議論しましたが、実際のアプリケーションでは、複数のテーブル間のデータへの接続アクセスなど、より複雑な操作をサポートする必要があります.ここでは,データ関係モデリングにおける種々の関係マッピングに関与する.例えば、一対一マッピング、一対多マッピングなどです.ここではこのいくつかの状況の実現について議論する.
 
データベース・テーブル構造定義{{でーたべーすてーぶるこうぞう:ていぎ}}
具体的な実装コードについて説明する前に、一連のデータベース・テーブルを定義します.それらは一対一の関係があり、一対多の関係がある.これらの表の詳細は次のとおりです.
 
CREATE TABLE ADDRESSES 
(
  ADDR_ID INT(11) NOT NULL AUTO_INCREMENT,
  STREET VARCHAR(50) NOT NULL,
  CITY VARCHAR(50) NOT NULL,
  STATE VARCHAR(50) NOT NULL,
  ZIP VARCHAR(10) DEFAULT NULL,
  COUNTRY VARCHAR(50) NOT NULL,
  PRIMARY KEY (ADDR_ID)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=LATIN1;

CREATE TABLE STUDENTS 
(
  STUD_ID INT(11) NOT NULL AUTO_INCREMENT,
  NAME VARCHAR(50) NOT NULL,
  EMAIL VARCHAR(50) NOT NULL,
  PHONE VARCHAR(15) DEFAULT NULL,  
  DOB DATE DEFAULT NULL,
  GENDER VARCHAR(6) DEFAULT NULL, 
  BIO LONGTEXT DEFAULT NULL,
  PIC BLOB DEFAULT NULL,
  ADDR_ID INT(11) DEFAULT NULL,  
  PRIMARY KEY (STUD_ID),
  UNIQUE KEY UK_EMAIL (EMAIL),
  CONSTRAINT FK_STUDENTS_ADDR FOREIGN KEY (ADDR_ID) REFERENCES ADDRESSES (ADDR_ID)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=LATIN1;

CREATE TABLE TUTORS 
(
  TUTOR_ID INT(11) NOT NULL AUTO_INCREMENT,
  NAME VARCHAR(50) NOT NULL,
  EMAIL VARCHAR(50) NOT NULL,
  PHONE VARCHAR(15) DEFAULT NULL,  
  DOB DATE DEFAULT NULL,
  GENDER VARCHAR(6) DEFAULT NULL,
  BIO LONGTEXT DEFAULT NULL,
  PIC BLOB DEFAULT NULL,
  ADDR_ID INT(11) DEFAULT NULL,
  PRIMARY KEY (TUTOR_ID),
  UNIQUE KEY UK_EMAIL (EMAIL),
  CONSTRAINT FK_TUTORS_ADDR FOREIGN KEY (ADDR_ID) REFERENCES ADDRESSES (ADDR_ID)  
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=LATIN1;


CREATE TABLE COURSES 
(
  COURSE_ID INT(11) NOT NULL AUTO_INCREMENT,
  NAME VARCHAR(100) NOT NULL,
  DESCRIPTION VARCHAR(512) DEFAULT NULL,
  START_DATE DATE DEFAULT NULL,
  END_DATE DATE DEFAULT NULL,
  TUTOR_ID INT(11) NOT NULL,
  PRIMARY KEY (COURSE_ID),
  CONSTRAINT FK_COURSE_TUTOR FOREIGN KEY (TUTOR_ID) REFERENCES TUTORS (TUTOR_ID)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=LATIN1;


CREATE TABLE COURSE_ENROLLMENT
(
  COURSE_ID INT(11) NOT NULL,
  STUD_ID INT(11) NOT NULL,
  PRIMARY KEY (COURSE_ID,STUD_ID),
  CONSTRAINT FK_ENROLLMENT_STUD FOREIGN KEY (STUD_ID) REFERENCES STUDENTS (STUD_ID),
  CONSTRAINT FK_ENROLLMENT_COURSE FOREIGN KEY (COURSE_ID) REFERENCES COURSES (COURSE_ID)
) ENGINE=INNODB DEFAULT CHARSET=LATIN1;

表の詳細な定義とデータ部分については、添付ファイルの内容を参照してください.すべてのテーブルとデータが構築されたと仮定したら、どうやってアクセスしますか?次に,一般的な2つのマッピング方式について議論する.
 
1対1マッピング
前のデータベースの定義では、テーブルstudents、addressesが表示されます.2つのエンティティオブジェクトStudentとAddressを定義しているとします.定義は次のとおりです.
 
public class Student implements Serializable {
	private static final long serialVersionUID = 1L;
	private Integer studId;
	private String name;
	private String email;
	private PhoneNumber phone;
	private Address address;
        // get, set methods omitted
	
	@Override
	public String toString() {
		return "Student [studId=" + studId + ", name=" + name + ", email=" + email
				+ ", phone=" + (phone==null?null:phone.getAsString()) + ", address=" + address + "]";
	}
}

  
public class Address implements Serializable {
	
	private static final long serialVersionUID = 1L;
	
	private Integer addrId;
	private String street;
	private String city;
	private String state;
	private String zip;
	private String country;
        // get set methods omitted...	
	@Override
	public String toString() {
		return "Address [addrId=" + addrId + ", street=" + street + ", city=" + city
				+ ", state=" + state + ", zip=" + zip + ", country=" + country
				+ "]";
	}
}

コードからStudentとAddressクラスは一対一の関係であることが明らかになった.実装の観点から、対応するmaper xmlファイルでマッピング関係を定義する必要があります.よく使われるいくつかの方法は以下の通りです.
 
ResultMapの継承
この方法は、Studentタイプから継承されたResultMapを定義し、対応する必要があるaddress情報をマッピングすることです.既存のStudioのマッピング定義は次のとおりです.

	
	
	
	

 
継承Studioの定義は次のとおりです.

	
	
	
	
	
	

この定義はaddressの対応する属性と表のフィールドを個別に定義して1つの場所にマッピングすることに相当する.次の検索文を定義します.

 
対応するmapperインタフェースは次のように定義されます.
public interface StudentMapper {
	Student selectStudentWithAddress(int id);
}

 
対応するStudioサービスでは、次のような方法を定義します.
public Student findStudentWithAddressById(int id) {
	SqlSession sqlSession = MyBatisUtil.getSqlSessionFactory().openSession();
	try {
		StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
		return studentMapper.selectStudentWithAddress(id);
	} finally {
		sqlSession.close();
	}
}

この場合、次のコードを実行します.
public static void main( String[] args ) {
    	StudentService studService = new StudentService();
    	Student student1 = studService.findStudentWithAddressById(1);
    	System.out.println(student1);
}

次の出力結果が表示されます.
Student [studId=1, name=Timothy, [email protected], phone=123-123-1234, address=Address [addrId=3, street=710 N Cable Rd, city=Lima, state=OH, zip=45825, country=Allen]]

明らかに、ここでaddressの情報を全部読みました.
この方式に対して,個別のAddressを定義するマッピング関係を実現しなければ,比較的合理的な選択である.しかし、場合によってはAddressのマッピングResultMapを定義しており、ここで再定義すると冗長に見えます.では、他の方法はありませんか.
 
association直接関連
Addressのマッピングを定義した場合は、次のように定義します.
 

	
  	
  		
		
		
		
		
		
  	
  	
  	
  	

では、対応するStudio ResultMapは次のように変更する必要があります.

		
		
		
		
		
	

この配置には、比較的注意すべき点があります.1つは、ここでassociationのプロパティを定義し、対応するresultMapを対応するAddressResultに指します.ここで定義したAddressResultは別のファイルとネーミングスペースに定義されているので、そのフルネームを参照する必要があります.
 
埋め込みselect関連付け
前のAddressのマッピングファイルを見ると、idに基づいてaddressを検索する方法がいくつかあります.マッピング結果を直接参照できる以上、ここでは検索方法を直接参照することもできます.定義した結果にこの検索の定義方法を埋め込むだけでいいです.この方法の定義は次のとおりです.
 

	
	
	
	
	

この方式を採用しても同様に注意しなければならないのは,ターゲットメソッドのネーミング空間参照である.ここでさらに設定するのは、associationでcolumnフィールドを指定し、select文に埋め込まれた対応するパラメータとして使用することです.
 
ResultMapネスト
実際には,上記の継承タイプに加えて,ResultMapネスト方式を用いて,この一対一のマッピング関係を実現することもできる.この設定の定義は次のとおりです.

	
	
	
	
		
		
		
		
		
		
	

この方式を採用することは,前に直接associationを用いた方式と近いが,具体的なaddressのマッピングの詳細をここに置いた. 
 
1対のマルチマッピング
一対一の関係に比べて、一対多のマッピングは少し複雑です.まず、1対以上のオブジェクトのセットを定義します.
Tutor:
public class Tutor implements Serializable {
	private static final long serialVersionUID = 1L;
	
	private Integer tutorId;
	private String name;
	private String email;
	private Address address;
	private List courses;
        // get, set methods omitted...
	@Override
	public String toString() {
		return "Tutor [tutorId=" + tutorId + ", name=" + name + ", email=" + email
				+ ", address=" + address + ", courses=" + courses + "]";
	}
}

Tutorクラスの定義には、リストのメンバー変数が含まれています.これはCourseと一対多の関係を構成していることに相当する.
 
埋め込みResultMap
Courseクラスの対応するResultMapは次のように定義されていると仮定します.

  	
  		
  		
  		
  		
  		
  	
  
  	
  	

では、対応するTutor ResultMapの定義は以下の通りです.

	
	
	
	
	

実はTutorの定義では、AddressとCourseの2つのオブジェクトが関連付けられています.ただAddressとは一対一の関係であり、Courseとは一対多の関係である.そこでここで関連する方法には少し違いがあります.複数のcourseの場合、collectionプロパティを使用し、対応するCourseResultを指定する必要があります. 
対応するselect文は次のように使用できます.

ここでは、2つのテーブル接続によって必要なすべてのフィールドを返します.この方式はcoursesを含めることをメソッド名に直接説明していないが,結果にはこの結果が含まれる.対応するmapperインタフェースとTutorServiceの実装は次のとおりです.
TutorMapper:
public interface TutorMapper {
	
	Tutor selectTutorById(int tutorId);
}

 
TutorService:
public Tutor findTutorById(int tutorId) {
	SqlSession sqlSession = MyBatisUtil.getSqlSessionFactory().openSession();
	try {
		TutorMapper mapper = sqlSession.getMapper(TutorMapper.class);
		return mapper.selectTutorById(tutorId);
	} 
		
	finally {
		sqlSession.close();
	}
}

 
埋め込みselect
前述した埋め込みselect法と同様に、coursesに対応する選択を埋め込む必要がある.その対応は以下のように実現される.

	
	
	
	
	
  

ここで注意したいのは、選択したselectのネーミングスペースと対応するcolumn名です.もちろん、対応するselectがあれば、対応するselect方法は少し簡単にできます.

この方式と前述の方式の違いは,対応するcourseを検索する方法を対応するcourseのマッピング定義に委任することであるので,ここで値はaddressのマッピングと考慮すればよい.前の2つの接続操作は、すべての結果を返すことに相当し、他のクエリーを利用する必要はありません.もちろん、1対の複数のマッピングが複数の結果を返すことに関連しているため、この方法では複数回のクエリーを実行する必要があり、パフォーマンスの問題を引き起こす可能性があります.
 
まとめ
mybatisでは、さまざまなマッピング関係に対するクエリーには、小さな詳細がたくさんあります.一対一の関係のように、定義できるタイプ継承、タイプネスト、またはクエリーネスト.私たちは実際のニーズに合わせて調整することができます.
 
きじゅんざいりょう
java persistence with mybatis 3