interneto sistemų architektūraklevas.mif.vu.lt/~vaidasj/tp/skaidres/09-mybatis.pdf · maven...
TRANSCRIPT
Data Mapper: Main idea
2Data Mapper - MyBatis
Maven MyBatis generator plugin
Data Mapper - MyBatis 3
<plugin><groupId>org.mybatis.generator</groupId><artifactId>mybatis-generator-maven-plugin</artifactId><version>1.3.5</version><configuration>
<contexts>StudentsDB</contexts><verbose>true</verbose>
</configuration><dependencies>
JDBC driver dependencies...</dependencies>
</plugin>
Naudoja: src\main\resources\generatorConfig.xml
src\main\resources\generatorConfig.xml
Data Mapper - MyBatis 4
<generatorConfiguration><context id="StudentsDB" targetRuntime="MyBatis3Simple">
<jdbcConnection driverClass="org.postgresql.Driver"connectionURL="jdbc:postgresql://localhost/studentsdb"userId="postgres"password="postgres">
</jdbcConnection><javaModelGenerator targetPackage="lt.vu.usecases.mybatis.model"
targetProject="MAVEN" /><!-- Anotacijų variantas - mažiau galimybių, bet paprastesnis --><!--<javaClientGenerator type="ANNOTATEDMAPPER"
targetPackage="lt.vu.usecases.mybatis.dao" targetProject="MAVEN" /> -->
<!-- XML variantas - daugiau galimybių --><sqlMapGenerator targetPackage="lt.vu.usecases.mybatis.xml"
targetProject="MAVEN" /><javaClientGenerator type="XMLMAPPER"
targetPackage="lt.vu.usecases.mybatis.dao" targetProject="MAVEN" />
src\main\resources\generatorConfig.xml (2)
Data Mapper - MyBatis 5
<table schema="public" tableName="university"><generatedKey column="id" identity="true" sqlStatement="JDBC" /><ignoreColumn column="opt_lock_version" />
</table><table schema="public" tableName="student">
<generatedKey column="id" identity="true" sqlStatement="JDBC" /><ignoreColumn column="opt_lock_version" />
</table><table schema="public" tableName="course">
<generatedKey column="id" identity="true" sqlStatement="JDBC" /><ignoreColumn column="opt_lock_version" />
</table><table schema="public" tableName="student_course"></table>
</context></generatorConfiguration>
Generavimo rezultatas
Data Mapper - MyBatis 6
Maven MyBatis dependencies
Data Mapper - MyBatis 7
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.4.2</version>
</dependency><dependency>
<groupId>org.mybatis</groupId><artifactId>mybatis-cdi</artifactId><version>1.0.0-beta7</version>
</dependency>
MyBatis: http://www.mybatis.org/mybatis-3 SqlSessionFactory
Once created, the SqlSessionFactory should exist for the duration of your application execution.
The simplest is to use a Singleton pattern or Static Singleton pattern.
SqlSession Each thread should have its own instance of SqlSession.
Instances of SqlSession are not to be shared and are not thread safe. Therefore the best scope is request or method scope.
Never keep references to a SqlSession instance in a static field or even an instance field of a class. Never keep references to a SqlSession in any sort of managed scope, such as HttpSessionof the Servlet framework.
Data Mapper - MyBatis 8
MyBatisResources.java
Data Mapper - MyBatis 9
@Slf4j@ApplicationScopedpublic class MyBatisResources {
@Produces@ApplicationScoped@SessionFactoryProviderprivate SqlSessionFactory produceSqlSessionFactory() {
try {return new SqlSessionFactoryBuilder().build(
Resources.getResourceAsStream("MyBatisConfig.xml"));
} catch (IOException e) {log.error("produceSqlSessionFactory(): ", e);throw new RuntimeException(...);
}}
}
Data Mapper - MyBatis 10
<configuration><typeAliases><typeAlias type="lt.vu.usecases.mybatis.model.Student"
alias="Student"/>...
</typeAliases><environments default="development"><environment id="development"><transactionManager type="MANAGED"/><dataSource type="JNDI">
<property name="data_source" value="openejb:Resource/StudentsDataSource"/>
</dataSource></environment>
</environments><mappers><mapper resource="mybatis/xml/StudentMapper.xml"/>...
</mappers></configuration>
src\main\resources\MyBatisConfig.xml
MyBatis Student Model class
Data Mapper - MyBatis 11
@Getter@Setterpublic class Student {
private Integer id;private String firstName;private String lastName;private Integer universityId;private String registrationNo;
// Rankomis prirašyti:private University university;private List<Course> courses;
}
Student Mapper XML file
Data Mapper - MyBatis 12
<mapper namespace="lt.vu.usecases.mybatis.dao.StudentMapper"><delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
DELETE FROM public.studentWHERE id = #{id,jdbcType=INTEGER}
</delete>
<insert id="insert" keyColumn="id" keyProperty="id" parameterType="Student" useGeneratedKeys="true">
INSERT INTO public.student (first_name, last_name, registration_no, university_id)
VALUES (#{firstName,jdbcType=VARCHAR}, #{lastName,jdbcType=VARCHAR}, #{registrationNo,jdbcType=VARCHAR}, #{universityId,jdbcType=INTEGER})
</insert>
<update id="updateByPrimaryKey" parameterType="Student">UPDATE public.studentSET first_name = #{firstName,jdbcType=VARCHAR},last_name = #{lastName,jdbcType=VARCHAR},registration_no = #{registrationNo,jdbcType=VARCHAR},university_id = #{universityId,jdbcType=INTEGER}WHERE id = #{id,jdbcType=INTEGER}
</update>
Student Mapper XML file
Data Mapper - MyBatis 13
<resultMap id="BaseResultMap" type="lt.vu.usecases.mybatis.model.Student"><id column="id" jdbcType="INTEGER" property="id"/><result column="first_name" jdbcType="VARCHAR" property="firstName"/><result column="last_name" jdbcType="VARCHAR" property="lastName"/><result column="registration_no" jdbcType="VARCHAR"
property="registrationNo"/><result column="university_id" jdbcType="INTEGER" property="universityId"/>...
</resultMap>
<select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
SELECT id, first_name, last_name, registration_no, university_idFROM public.studentWHERE id = #{id,jdbcType=INTEGER}
</select>
<select id="selectAll" resultMap="BaseResultMap">SELECT s.id, s.first_name, s.last_name, s.registration_no, s.university_id,
u.title as university_titleFROM public.student s
LEFT JOIN public.university u ON s.university_id = u.id</select>
Ryšiai
Data Mapper - MyBatis 14
<resultMap id="BaseResultMap" type="lt.vu.usecases.mybatis.model.Student">...
<!-- Rankomis prirašytas ryšiui ManyToOne Student -> University --><!-- Nested result (nėra N+1 select problemos) --><association property="university" columnPrefix="university_"
resultMap="lt.vu.usecases.mybatis.dao.UniversityMapper.BaseResultMap"/>
<!-- Rankomis prirašytas ryšiui ManyToMany Student -> Course --><!-- Nested select (yra N+1 select problema) --><collection property="courses" javaType="ArrayList" ofType="Course"
column="id" select="selectCoursesForStudent"/></resultMap>
<!-- Rankomis prirašytas ryšiui ManyToMany Student -> Course --><select id="selectCoursesForStudent" resultType="Course">
SELECTc.id,c.name
FROM public.course cJOIN public.student_course sc on c.id = sc.course_id
WHERE sc.student_id = #{id,jdbcType=INTEGER}</select>
UniversityMapper.xml
Data Mapper - MyBatis 15
<mapper namespace="lt.vu.usecases.mybatis.dao.UniversityMapper"><resultMap id="BaseResultMap" type="University">
<id column="id" jdbcType="INTEGER" property="id"/><result column="title" jdbcType="VARCHAR" property="title"/>
</resultMap>
MyBatis DAO: StudentMapper
Data Mapper - MyBatis 16
@Mapper // rankomis prirašytas: MyBatis CDI integracijapublic interface StudentMapper {
int deleteByPrimaryKey(Integer id);
int insert(Student record);
Student selectByPrimaryKey(Integer id);
List<Student> selectAll();
int updateByPrimaryKey(Student record);}
Metodų pavadinimai atitinka XML faile atributų „id“ reikšmes
Use case controller
Data Mapper - MyBatis 17
@Model // tas pats kaip: @Named ir @RequestScoped@Slf4jpublic class RequestUseCaseControllerMyBatis {
@Getter private Course course = new Course();@Getter private Student student = new Student();
@Inject private StudentMapper studentMapper;@Inject private CourseMapper courseMapper;@Inject private StudentCourseMapper studentCourseMapper;
public List<lt.vu.usecases.mybatis.model.Student> getAllStudents() {return studentMapper.selectAll();
}@Transactionalpublic void createCourseStudent() {
courseMapper.insert(course);studentMapper.insert(student);StudentCourse studentCourse = new StudentCourse();studentCourse.setCourseId(course.getId());studentCourse.setStudentId(student.getId());studentCourseMapper.insert(studentCourse);log.info("Maybe OK...");
}}
simple-usecase-mybatis.xhtml
Data Mapper - MyBatis 18
<p:fieldset legend="All students" toggleable="true" style="width: 50%"><p:dataTable var="student"
value="#{requestUseCaseControllerMyBatis.getAllStudents()}">
<p:column headerText="Student"><h:outputText value="#{student.firstName} #{student.lastName}"/>
</p:column>
<p:column headerText="Courses"><ui:repeat var="course" value="#{student.courses}"><h:outputText value="#{course.name} " />
</ui:repeat></p:column>
<p:column headerText="University"><h:outputText value="#{student.university.title} " />
</p:column></p:dataTable>
</p:fieldset>
ORM vs Data Mapper SQL sakiniai:
ORM: generuojami (ir prisitaiko prie skirtingų DBVS automatiškai)
Data Mapper: rašomi rankomis; skirtingoms DBVS reikės skirtingų SQL...
SQL sakinių našumas/greitaveika: ORM: praktiškai nekontroliuojama (yra galimybės nurodyti
„hint‘us“...)
Data Mapper: pilnai kontroliuojama SQL lygyje
N+1 select problema: ORM: valdoma dalinai (galime pasakyti norus)
Data Mapper: valdoma pilnai
Data Mapper - MyBatis 19
MyBatis: Dynamic SQL
Data Mapper - MyBatis 20
<select id="findActiveBlogLike" resultType="Blog">SELECT * FROM BLOG <where><if test="state != null">
state = #{state} </if><if test="title != null">
AND title like #{title} </if><if test="author != null and author.name != null">
AND author_name like #{author.name} </if>
</where></select>
The where element knows to only insert "WHERE" if there is any content
returned by the containing tags.
Furthermore, if that content begins with "AND" or "OR", it knows to strip it off.