/**MyBatis：封装SQL语句，将其执行结果直接映射到Object或List<Object>
 * 1.mybatis-config.xml中做系统配置，主要是jdbc driver的配置
 * 2.可以用注解或xml两种方式封装SQL语句：
 *   1）xml方式又可以细分为Interface引用和Sqlsession引用两种情况
 *   2）注解方式也可以细分为普通注解和xxxProvider注解两种情况
 * 3.SQL结果映射（主要是Select语句）主要用于对象属性名与字段名不同、对象属性是Object或List<Object>的情况
 */
import org.apache.ibatis.annotations.*
import org.apache.ibatis.annotations.Result
import org.apache.ibatis.io.Resources
import org.apache.ibatis.jdbc.SQL
import org.apache.ibatis.session.SqlSessionFactoryBuilder
import java.util.*

class Student {
    var id = 0
    var name: String? = null
    var age:Int=0
    var gradeclass:GradeClass?=null     //1:1---association
    var course:List<Course>?=null       //1:N---collection
    override fun toString(): String {
        return "Student:"+id+"---"+name+"-----"+age
    }
}
class GradeClass{
    var id = 0
    var name: String? = null
    var teacher:String ?= null
    override fun toString(): String {
        return "GradeClass:"+id+"---"+name+"---"+teacher
    }
}

class Course{
    var id = 0
    var name: String? = null
    var studentID:Int=0
    override fun toString(): String {
        return "Course:"+id+"---"+name+"---"+studentID
    }
}

//无需XML,直接在注解中定义SQL语句
interface StudentMapper {
    @Select("select min(s_id) from student")
    fun getMinId(): Int

    //多参数传递
    @Select("SELECT * FROM student where s_name LIKE #{param1} and age > #{param2}")
    @Results(id = "studentResult", value = [
        Result(property = "id", column = "s_id", id = true),
        Result(property = "name", column = "s_name"),
        Result(property = "age", column = "age")]
    )
    fun getStudentByNameAge1(name: String?, age: Int):List<Student>

    @Select("""SELECT * FROM student where s_name LIKE #{name} and age > #{age}""")
    @ResultMap("studentResult")
    fun getStudentByNameAge2(@Param("name") name: String?, @Param("age") age: Int):List<Student>

    @Select("SELECT s_id as id,S_NAME,age FROM student where s_name LIKE #{name} and age > #{age}")
    @Result(property = "name", column = "s_name")
    fun getStudentByNameAge3(params: Map<String, Any>):List<Student>

    //在UserSqlBuilder类中提供SQL语句

    @SelectProvider(type = UserSqlBuilder::class, method = "buildGetStudentByNameAge")

    fun  getStudentByNameAge9(name: String?,age: Int):List<Student>
    @SelectProvider(type = UserSqlBuilder::class, method = "buildGetStudentByNameAgeWithParamAnnotation")
    fun  getStudentByNameAge10(@Param("xname") name: String?, @Param("xage") age: Int):List<Student>
}

//需XML配合（在其中定义SQL语句），但使用多参数和调用会更直观和方便
interface MyMapper {
    fun getStudentByNameAge4(name: String?, age: Int):List<Student>
    fun getStudentByNameAge5(@Param("name") name: String?, @Param("age") age: Int):List<Student>
    fun getStudentByNameAge5(params: Map<String, Any>):List<Student>

    fun getStudentByNameAge6(@Param("name") name: String? = null, @Param("age") age: Int = 0):List<Student>
    fun getStudentByNameAge7(@Param("name") name: String? = null, @Param("age") age: Int = 0):List<Student>

    fun getStudentByNameAge8(gList: List<Int>):List<Student>
}


class UserSqlBuilder {
    // 如果不使用 @Param，就应该定义与 mapper 方法相同的参数
    fun buildGetStudentByNameAge(name: String?, age: Int): String {
     return SQL()
            .SELECT("s_id as id,S_NAME as name,age")
            .FROM("student")
            .WHERE("s_name like #{arg0}" )
            .AND()
            .WHERE("age > #{arg1}")
            .toString()
    }

    // 如果使用 @Param，就可以只定义需要使用的参数
    fun buildGetStudentByNameAgeWithParamAnnotation(@Param("xname")name: String?,@Param("xage") age: Int): String {
      val sqlSting= (SQL()
            .SELECT("s_id as id,S_NAME as name,age")
            .FROM("student")
            .WHERE("s_name like #{xname}")
          .AND()
          .WHERE("age > #{xage}"))
                    .toString()
        println(sqlSting)
        return sqlSting;
    }
}

fun main() {
    // 根据 mybatis-config.xml 配置的信息构造 sqlSessionFactory
    val resource = "./resources/mybatis-config.xml"
    val inputStream = Resources.getResourceAsStream(resource)

    val sqlSessionFactory = SqlSessionFactoryBuilder().build(inputStream)
    //用代码注册Mapper。也可以在mybatis-config.xml中注册
    sqlSessionFactory.getConfiguration().addMapper(StudentMapper::class.java)
    // 然后根据 sqlSessionFactory 得到 session
    val session = sqlSessionFactory.openSession(true)
    // 用session获取Mapper
    val sMapper: StudentMapper = session.getMapper(StudentMapper::class.java)
    val mMapper: MyMapper = session.getMapper(MyMapper::class.java)
    // 用session或Mapper调用SQL语句，操作数据库
    val maxId:Int=session.selectOne("getMaxId")   //XML定义操作SQL
    val minId:Int=sMapper.getMinId()              //注解定义操作SQL

    // 增加学生和班级
    val student1 = Student()
    student1.id = maxId+1
    student1.name = "李四"
    student1.age = 10
    val gradeClass1=GradeClass()
    gradeClass1.id=12
    gradeClass1.name="5-1班"
    gradeClass1.teacher="王老师"
    student1.gradeclass=gradeClass1
    session.insert("addStudent", student1)

    //删除学生
    val student2 = Student()
    student2.id = minId
    session.delete("deleteStudent", student2)

    //查询特定
    val student3: Student? = session.selectOne("getStudent", minId + 1)

    //修改学生
    student3?.name = "赵六"
    session.update("updateStudent", student3)

    //查询全部
    println("----查询全部----")
    var students: List<Student> = session.selectList("listStudent")
    students.forEach(::println)

    //多参数查询
    println("----多参数查询----")
    val params: MutableMap<String, Any> = HashMap<String, Any>()
    params["name"] = "%李%"
    params["age"] = 7
    println(params)
   // students = session.selectList("findStudentByNameAge", params)
    /*等效替代
    students=sMapper.getStudentByNameAge1("%李%",7)
    students=sMapper.getStudentByNameAge2("%李%",7)
    students=sMapper.getStudentByNameAge3(params)

    students=mMapper.getStudentByNameAge4("%李%",7)
    students=mMapper.getStudentByNameAge5(params)
    */
    students=sMapper.getStudentByNameAge2("%李%",7)
    students.forEach(::println)

    //动态SQL
    println("----动态SQL查询----")
    students=mMapper.getStudentByNameAge6("%李%")
    students.forEach(::println)
    students=mMapper.getStudentByNameAge7(age = 8)
    students.forEach(::println)
    val gidList=listOf(1, 2, 3, 4)
    students=mMapper.getStudentByNameAge8(gidList)  //多参数时需要使用map传递参数
    students.forEach(::println)

    println("----SQLProvider----")
    students=sMapper.getStudentByNameAge9("%李%",8)
    students.forEach(::println)
    students=sMapper.getStudentByNameAge10("%李%",8)
    students.forEach(::println)
    println("----复杂查询----")
    val student13:Student =session.selectOne("selectStudent6", 20)
    println(student13)
    println(student13.gradeclass)
    println(student13.course)

    session.close()
    inputStream.close()

}