SQLite 데이터 베이스
- 안드로이드에서 사용하는 내장 데이터 베이스로 표준 SQL문을 사용하는 관계형 데이터 베이스이다.
- MySQL 과 유사한 문법을 사용하고 있으며 일반적인 관계형 데이터 베이스가 가지고 있는 기능을 가지고 있다.
동작 방식
- SQLite 데이터베이스는 임베디드형 데이터베이스로써 데이터베이스를 사용하는 애플리케이션에 셋팅되는 데이터 베이스이다.
- 안드로이드는 안드로이드 OS에 내장되어 있으며 개발자가 만드는 애플리케이션은 안드로이드 OS에게 쿼리문을 전달하고 안드로이드 OS가 직접 데이터 베이스에 대한 처리를 하게된다.
작성 방식
- 안드로이드에서의 SQLite 데이터베이스 사용은 쿼리문을 이용하는 방법과 제공되는 클래스를 이용하는 방법 두 가지가 있다.
- 쿼리문을 이용하는 방식은 일반적인 SQL문을 사용하며 MySQL과 유사한 문법을 사용한다.
- 클래스를 이용하는 방법은 개발자가 정해줘야 하는 몇 가지 정보를 제공하면 쿼리문이 생성되고 실행되는 구조이다.
SQLite OpenHelper
- 안드로이드에서 SQLite 데이터 베이스를 사용하려면 SQLiteOpenHelper를 상속받은 클래스를 만들어야 한다.
- 이 클래스는 사용할 데이터베이스의 이름을 설정하는 것 뿐만 아니라 다음 기능들을 제공하고 있다.
- 지정된 데이터베이스 파일을 사용하려고 할 때 파일이 없으면 파일을 만들고 onCreate 메서드를 호출한다. 이 메서드에서는 테이블을 만드는 쿼리를 실행해주면 된다.
- 애플리케이션을 서비스하다가 데이터 베이스 구조를 변경하려면 데이터베이스의 버전을 변경하면 된다. 버전을 변경하면 onUpgrade 메서드가 호출되고 여기에서 테이블을 새로운 구조로 변경해주는 작업을 해주면 된다.
1. DBHelper.kt
// 생성자로 Context 받아야한다
// Test.db :사용할 데이터 베이스 파일의 이름
// null : NullFactory, Null 에 대한 처리를 어떻게 할 것인가.. Null 셋팅하면 된다
// 1 : 버전
// 예를 들어 학생들의 시험 점수를 입력받는 테이블이 있는데
// 국어, 수학, 영어 점수를 받는다 ( 과목의 점수들을 열에 해당)
// 그런데, 어는 한 학생 A 가 영어 시험을 안받으면 영어 점수가 없는데, 이러면 안된다
// 그래서 점수가 없으므로 null 값이 들어 가도록 해야한다
// 즉, 행에대한 값은 비어있으면 안된다
class DBHelper(context:Context) : SQLiteOpenHelper(context, "Test.db", null, 1) {
// 데이터 베이스 파일이 없으면 만들고 이 메서드를 호출해준다.
// 테이블을 생성하는 작업을 수행하면 된다.
override fun onCreate(sqliteDatabase: SQLiteDatabase?) {
// 테이블의 구조를 정의한다.
// 항상 최신의 형태를 가지도록 한다
// create table 테이블이름
// (컬럼이름 자료형 제약조건 ... )
// 자료형 : 정수 - integer, 문자열 - text, 실수 - real, 날짜 - date
// 제약조건 : 저장할 수 있는 값에 대한 조건
// primary key : null을 허용하지 않고 중복된 값을 허용하지 않는다.
// 각 행들을 개별적으로 구분할 수 있는 값을 저장하기 위해 사용한다.
// autoincrement : 컬럼에 저장할 값을 지정하지 않으면 1부터 1씩 증가되는 값이 자동으로 저장한다.
// not null : null 값을 허용하지 않는다. 즉, 개발자가 무조건 값을 정해줘야 한다.
val sql = """create table TestTable
(idx integer primary key autoincrement,
textData text not null,
intData integer not null,
doubleData real not null,
dateData date not null)
""".trimIndent()
// 쿼리문을 수행한다.
sqliteDatabase?.execSQL(sql)
}
// 사용하는 데이터 베이스 파일의 버전이 변경되어 있을 때 호출되는 메서드
// 부모의 생성자의 마지막에 넣어준 버전 번호가 데이터 베이스 파일에 기록 버전 보다 높을 때 호출된다.
// 과거에 만들어진 테이블을 현재의 구조가 될 수 있도록 테이블을 수정하는 작업을 하면 된다.
// ex) 과거에는 컬럼이 3개이나 현재는 업데이트로 인해 컬럼이 5개 이므로 업데이트가 필요하다
// 즉, 과거의 데이터베이스를 쓰고 있는 사람들의 위함이다
override fun onUpgrade(sqliteDatabase: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
TODO("Not yet implemented")
}
}
[챗 GPT 설명]
DBHelper 클래스는 SQLiteOpenHelper를 상속하여 SQLite 데이터베이스를 생성하고 관리하는 역할을 한다.
onCreate() 메서드는 데이터베이스 파일이 생성되어 있지 않을 때 호출되어 테이블을 생성하고, onUpgrade() 메서드는 데이터베이스 파일의 버전이 변경되었을 때 호출되어 업그레이드 작업을 수행할 수 있다.
sqliteDatabase?.execSQL(sql)
- sqliteDatabase 객체의 execSQL() 메서드를 사용하여 SQL 문을 실행합니다.
- 이를 통해 테이블이 생성됩니다.
2. activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="insert" />
<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="select all" />
<Button
android:id="@+id/button3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="select condition" />
<Button
android:id="@+id/button4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="update" />
<Button
android:id="@+id/button5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="delete" />
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
</LinearLayout>
3. MainActivity
class MainActivity : AppCompatActivity() {
lateinit var activityMainBinding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(activityMainBinding.root)
activityMainBinding.run{
button.setOnClickListener {
// 현재 시간을 구해 년-월-일 양식의 문자열을 만들어준다.
val sdf = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
val now = sdf.format(Date())
// 저장할 정보를 가지고 있는 객체를 생성한다.
// - idx 는 autoincrement 으로 자동으로 증가되므로, 그냥 0 값 넣어주었다
val obj1 = TestClass(0, "문자열1", 100, 11.11, now)
DAO.insertData(this@MainActivity, obj1)
val obj2 = TestClass(0, "문자열2", 200, 22.22, now)
DAO.insertData(this@MainActivity, obj2)
}
button2.setOnClickListener {
// 데이터를 가져온다.
val dataList = DAO.selectAllData(this@MainActivity)
// 출력
textView.text = ""
for(obj in dataList){
textView.append("idx : ${obj.idx}\n")
textView.append("textData : ${obj.textData}\n")
textView.append("intData : ${obj.intData}\n")
textView.append("doubleData : ${obj.doubleData}\n")
textView.append("dateData : ${obj.dateData}\n\n")
}
}
button3.setOnClickListener {
val obj = DAO.selectData(this@MainActivity, 1)
textView.text = "idx : ${obj.idx}\n"
textView.append("textData : ${obj.textData}\n")
textView.append("intData : ${obj.intData}\n")
textView.append("doubleData : ${obj.doubleData}\n")
textView.append("dateData : ${obj.dateData}\n")
}
button4.setOnClickListener {
val obj = DAO.selectData(this@MainActivity, 1)
obj.textData = "새로운 문자열"
DAO.updateData(this@MainActivity, obj)
}
button5.setOnClickListener {
DAO.deleteData(this@MainActivity, 1)
}
}
}
}
// DBHelper 의 TestTable 테이블안에 있는 컬럼들 과 같은 타입을 맞춰준다
data class TestClass(var idx:Int,
var textData:String,
var intData :Int,
var doubleData:Double,
var dateData:String)
4. DAO.kt
- DAO : Data Access Object
class DAO {
companion object {
// Create : 저장
fun insertData(context: Context, data: TestClass) {
// autoincrement가 있는 컬럼은 제외하고 나머지만 지정한다.
// 처음에는 값이 들어갈 부분은 ? 으로 하면된다
val sql = """insert into TestTable
| (textData, intData, doubleData, dateData)
| values (?, ?, ?, ?)
""".trimMargin()
// ? 에 설정할 값을 배열에 담아준다. (하나만 있더라도 무조건 배열로 담아준다)
// values (?, ?, ?, ?) 안에 순서 대로 넣어진다 (1대1 대응)
val arg1 = arrayOf(
data.textData, data.intData, data.doubleData, data.dateData
)
// 데이터베이스 오픈
val sqliteDatabase = DBHelper(context)
// 쿼리 실행 (쿼리문, ?에 셋팅할 값 배열)
sqliteDatabase.writableDatabase.execSQL(sql, arg1)
// 데이터 베이스를 닫아준다.
sqliteDatabase.close()
}
// Read Condition : 조건에 맞는 행 하나를 가져온다.
// - 먼저 autoincrement 을 가지고 있는 컬럼 (여기서는 idx)부터 조건을 만들어준다
fun selectData(context: Context, idx:Int) : TestClass{
// 쿼리문
val sql = "select * from TestTable where idx=?"
// select 는 무조건 무자열 배열로 담아둔다
// ?에 들어갈 값 (문자열 배열)
val arg1 = arrayOf("$idx")
// 데이터베이스 오픈
val dbHelper = DBHelper(context)
// 쿼리실행
val cursor = dbHelper.writableDatabase.rawQuery(sql, arg1)
cursor.moveToNext()
// 가져올 테이블(TestTable) 안에 있는 컬럼의 이름을 지정하여 컬럼의 순서값을 가져온다.
val idx1 = cursor.getColumnIndex("idx")
val idx2 = cursor.getColumnIndex("textData")
val idx3 = cursor.getColumnIndex("intData")
val idx4 = cursor.getColumnIndex("doubleData")
val idx5 = cursor.getColumnIndex("dateData")
// 데이터를 가져온다.
val idx = cursor.getInt(idx1)
val textData = cursor.getString(idx2)
val intData = cursor.getInt(idx3)
val doubleData = cursor.getDouble(idx4)
val dateData = cursor.getString(idx5)
val testClass = TestClass(idx, textData, intData, doubleData, dateData)
dbHelper.close()
return testClass
}
// Read All : 모든 행을 가져온다
fun selectAllData(context: Context):MutableList<TestClass>{
// 모든 행을 가져오는 쿼리문을 작성한다.
val sql = "select * from TestTable"
// 데이터베이스 오픈
val dbHelper = DBHelper(context)
// 쿼리 실행
// - 두 번째 매개변수(selectionArgs)에는 ? 들어갈 값을 넣어주면 되는데, ? 없으므로 null 값을 넣어준다
val cursor = dbHelper.writableDatabase.rawQuery(sql, null)
// cursor 객체는 쿼리문에 맞는 행에 접근할 수 있는 객체가 된다.
// 처음에는 아무 행도 가르치고 있지 않는다.
// moveToNext 메서드를 호출하면 다음 행에 접근할 수 있다.
// 이때 접근할 행이 있으면 true를 반환하고 없으면 false를 반환한다.
// - while 문 으로 쓴다
val dataList = mutableListOf<TestClass>()
while(cursor.moveToNext()){
// 가져올 테이블(TestTable) 안에 있는 컬럼의 이름을 지정하여 컬럼의 순서값을 가져온다.
val idx1 = cursor.getColumnIndex("idx")
val idx2 = cursor.getColumnIndex("textData")
val idx3 = cursor.getColumnIndex("intData")
val idx4 = cursor.getColumnIndex("doubleData")
val idx5 = cursor.getColumnIndex("dateData")
// 데이터를 가져온다.
val idx = cursor.getInt(idx1)
val textData = cursor.getString(idx2)
val intData = cursor.getInt(idx3)
val doubleData = cursor.getDouble(idx4)
val dateData = cursor.getString(idx5)
val testClass = TestClass(idx, textData, intData, doubleData, dateData)
dataList.add(testClass)
}
dbHelper.close()
return dataList
}
// Update : 조건에 맞는 행의 컬럼의 값을 수정한다.
fun updateData(context:Context, obj:TestClass){
// 쿼리문 의미
// - idx가 ? 인 행의 textData, intData, doubleData, dateData 컬럼의 값을 변경한다
val sql = """update TestTable
| set textData=?, intData=?, doubleData=?, dateData=?
| where idx=?
""".trimMargin()
// ? 에 들어갈 값
val args = arrayOf(obj.textData, obj.intData, obj.doubleData, obj.dateData, obj.idx)
// 쿼리 실행
val dbHelper = DBHelper(context)
dbHelper.writableDatabase.execSQL(sql, args)
dbHelper.close()
}
// Delete : 조건 맞는 행을 삭제한다.
fun deleteData(context:Context, idx:Int){
// 쿼리문
// TestTable에서 idx가 ? 인 행을 삭제한다.
val sql = "delete from TestTable where idx = ?"
// ?에 들어갈 값
val args = arrayOf(idx)
// 쿼리 실행
val dbHelper = DBHelper(context)
dbHelper.writableDatabase.execSQL(sql, args)
dbHelper.close()
}
}
}
[챗 GPT 설명]
1. inserDate 부분
val dbHelper = DBHelper(context)
- -DBHelper 클래스는 SQLiteOpenHelper를 상속받아 데이터베이스를 생성하고 관리합니다.
dbHelper.writableDatabase.execSQL(sql, arg1)
- writableDatabase 속성을 사용하여 데이터베이스를 열고 SQL 문을 실행합니다.
- execSQL() 메서드는 SQL 문을 실행하여 데이터베이스에 쿼리를 실행합니다.
2. selectData 은 selectAllData 과 거의 비슷하다
3. selectAllData 부분
val cursor = dbHelper.writableDatabase.rawQuery(sql, null)
- dbHelper 객체의 writableDatabase 속성을 사용하여 데이터베이스를 열고 SQL 문을 실행합니다.
- rawQuery() 메서드는 SQL 문을 실행하여 결과로 Cursor 객체를 반환합니다.
- sql 변수에 저장된 SELECT 문을 실행하여 데이터베이스로부터 결과를 가져옵니다.
[결과]
출처 : 안드로이드 앱스쿨 2기 윤재성 강사님
'안드로이드 앱 스쿨 2기 > Android' 카테고리의 다른 글
[13주차 - 화] JSON 문서 (0) | 2023.07.18 |
---|---|
[12주차 - 금] 9 patch 이미지 (0) | 2023.07.14 |
[11주차 - 수] Assets (0) | 2023.07.05 |
[11주차 - 수] Raw (0) | 2023.07.05 |
[11주차 - 화] FileStream (0) | 2023.07.04 |