wasent개발하면서 알게 된점

  • addListenerForSingleValueEvent 를 통해서 snapshot을 얻어올수도 있지만 최초로 addCildEventListener를 node에 연결할때 되돌아 오는 snapshot을 이용할수 있다. 단 addCildEventListener 의 경우 listener가 연결된 지점이하 child 들의 snapshot이 한번에 오는 것이 아니고 순차적으로 하나씩오게 된다. 
  • node의 key, value개념과 child개념을 혼동하면 안된다. 
  • children을 통해 child들을 통으로 가져오려는 경우 children의 parent node에 listener를 연결한다. 때때로 wrapper parent를 만들어야 하는경우도 있다.
  • getValue()사용시 collection형태로 data를 가져오려는 경우 GenericTypeIndicator를 이용한다. 
val type = object : GenericTypeIndicator<HashMap<String,String>>() {}

            val precios : HashMap<String,String>  = dataSnapshot.getValue(type!!)
            liveData.postValue(precios)

ref)kotlin https://stackoverflow.com/a/52684990/3151712

ref)official docs  https://firebase.google.com/docs/reference/android/com/google/firebase/database/GenericTypeIndicator

.

.

.

.

.

google firebase realtime database official docs
https://firebase.google.com/docs/database/android/start

To make your app data update in realtime, you should add a ValueEventListener to the reference you just created. (ChildEventListerner는 현 node아래 child를 관찰하는 것이고 ValueEventListener는 현재node 및 child를 관찰하는 것이다)

The onDataChange() method in this class is triggered once when the listener is attached and again every time the data changes, including the children.

All Firebase Realtime Database data is stored as JSON objects. 그러므로 json으로 표현되기 쉬운 형태로 database구성을 생각한다. 

jacob.body.head.eyes.pupil 이런식으로 저장된다고 생각하고 jacob body head eyes pupil은 key이름이며 이안에 다양한 타입의 data가 들어있다. 또 jacob boddy head eyes pupil 은 각각 node이다. 

t’s best to keep your data structure as flat as possible. 너무 많이 nested되지 않게 하며 각 node의 데이터를 가져온다는 이야기는 하부의 모든 데이터도 같이 가져온다는 이야기가 되기 때문에 염두에 둔다.

https://firebase.google.com/docs/database/android/read-and-write

각 node의 reference에서 setValue()를 통해 값을 쓰고 교환하고 지우기(null을 설정)를 할수 있다.

저장가능한 데이터 형태는 아래와 같다.

Pass types that correspond to the available JSON types as follows:

  • String
  • Long
  • Double
  • Boolean
  • Map<String, Object>
  • List<Object>

Pass a custom Java object, 

  • if the class that defines it has a default constructor that takes no arguments and has public getters for the properties to be assigned.

To read data at a path and listen for changes, use the  addValueEventListener()  or  addListenerForSingleValueEvent()  method to add a ValueEventListener to a DatabaseReference. (addValueEventListener 나 addListenerForSingleValueEvent 둘다 ValueEventListener를 사용하는 것은 같다.  다만addListenerForSingleValueEvent는 한번 데이터 snapshot을 가져오고 listener 스스로 삭제된다)

image

위 그림에서 ValueEventListener also defines the onCancelled() method that is called if the read is canceled. For example, a read can be canceled if the client doesn’t have permission to read from a Firebase database location.

updateChildren()을 이용 해당 node의 여러 child node의 데이터를 하나의 작업으로 수정가능하다. (update만 가능한게 아니라 create도 가능하다.)

image

Simultaneous updates made this way are atomic: either all updates succeed or all updates fail.

setValue() and updateChildren() 작업에는 아래와 같이 listener를 달아서 바로 결과를 확인할수 있다. (setValue()로 create, delete,update작업을 할수 있으므로 모든 작업에 listener를 아래와 같이 달아서 확인 가능) 

image

특정 node에 덧붙여진 listener를 removeEventListener() 를 통해 제거해야 하며 덧붙여진 갯수 만큼 제거 해야 하고 . child node에 있는 것이 자동으로 제거되는 건 아니므로 child node에 따로 접근 제거해야 한다.

https://firebase.google.com/docs/database/android/lists-of-data

collection 형태의 자료를 가지고 있는 node의 경우 node에 ChildEventListener를 붙이면 onChildAdded, onChildChanged, onChildRemoved 을 통해 돌아 오는 snapshot이 각각 하나의 element가 된다. ValueEventListener를 사용하는 경우는 snapshot이 collection전체를 가지고 있게 된다. 이때    for (postSnapshot in dataSnapshot.children) {}    이런 형태로 접근하게 된다.

image

데이터 정렬방법은 위와 같으며 단 한개만 설정할수 있다.

image

위그림은 먼저 정렬된 상태에서 ChildEventListener를 붙이는 모습을 보여준다.

image

위그림은 nested child를 경로 지정으로 정렬기준을 조정하는 방법

image

위 query method는 여러개를 사용할수 있다.

query 성능 개선을 위해서는 index를 이용할수 있다. ( https://firebase.google.com/docs/database/security/indexing-data )

.

.

.

.

original source : https://www.youtube.com/playlist?list=PLk7v1Z2rk4hg3cbALQuTQOTgpZ9e9T8bi

image

.

.

image

.

.

image

addListenerForSingleValueEvent 현재 상태에서 snapshot가져오는 함수

addValueEventListener 하나의 노드 실시간 감시 

addCildEventListener 자식노드까지 실시간 감시

.

.

image

.

.

image
image

.

.

image
image

.

.

image
image

.

.

image
image
image

.

.

firebase realtime database

에는 두가지조건을 동시에 검색하는것이 없다.

image

이런것을 불가능하고

또한 두테이블의 relation 도 만들수 없다.

https://developer.android.com/training/data-storage/room/accessing-data

@Dao
interface MyDao {
   @Insert(onConflict = OnConflictStrategy.REPLACE)
   fun insertUsers(vararg users: User)

   @Insert
   fun insertBothUsers(user1: User, user2: User)

   @Insert
   fun insertUsersAndFriends(user: User, friends: List<User>)
}

.

update

@Dao
interface MyDao {
   @Update
   fun updateUsers(vararg users: User)
}

.

delete

@Dao
interface MyDao {
   @Delete
   fun deleteUsers(vararg users: User)
}

.

query

@Dao
interface MyDao {
   @Query("SELECT * FROM user")
   fun loadAllUsers(): Array<User>
}
@Dao
interface MyDao {
   @Query("SELECT * FROM user WHERE age > :minAge")
   fun loadAllUsersOlderThan(minAge: Int): Array<User>
}
@Dao
interface MyDao {
   @Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge")
   fun loadAllUsersBetweenAges(minAge: Int, maxAge: Int): Array<User>

   @Query("SELECT * FROM user WHERE first_name LIKE :search " +
          "OR last_name LIKE :search")
   fun findUserWithName(search: String): List<User>
}

.

더 많은 내용이 있는데 web page 참조

original source: https://github.com/divanov11/crash-course-CRM/blob/Part-7-Database-Queries/crm1_v7_database_queries/accounts/queryDemos.py

#***(1)Returns all customers from customer table
customers = Customer.objects.all()

#(2)Returns first customer in table
firstCustomer = Customer.objects.first()

#(3)Returns last customer in table
lastCustomer = Customer.objects.last()

#(4)Returns single customer by name
customerByName = Customer.objects.get(name='Peter Piper')

#***(5)Returns single customer by name
customerById = Customer.objects.get(id=4)

#***(6)Returns all orders related to customer (firstCustomer variable set above)
firstCustomer.order_set.all()

#(7)***Returns orders customer name: (Query parent model values)
order = Order.objects.first() 
parentName = order.customer.name

#(8)***Returns products from products table with value of "Out Door" in category attribute
products = Product.objects.filter(category="Out Door")

#(9)***Order/Sort Objects by id
leastToGreatest = Product.objects.all().order_by('id') 
greatestToLeast = Product.objects.all().order_by('-id') 


#(10) Returns all products with tag of "Sports": (Query Many to Many Fields)
productsFiltered = Product.objects.filter(tags__name="Sports")

'''
(11)Bonus
Q: If the customer has more than 1 ball, how would you reflect it in the database?
A: Because there are many different products and this value changes constantly you would most 
likly not want to store the value in the database but rather just make this a function we can run
each time we load the customers profile
'''

#Returns the total count for number of time a "Ball" was ordered by the first customer
ballOrders = firstCustomer.order_set.filter(product__name="Ball").count()

#Returns total count for each product orderd
allOrders = {}

for order in firstCustomer.order_set.all():
	if order.product.name in allOrders:
		allOrders[order.product.name] += 1
	else:
		allOrders[order.product.name] = 1

#Returns: allOrders: {'Ball': 2, 'BBQ Grill': 1}


#RELATED SET EXAMPLE
class ParentModel(models.Model):
	name = models.CharField(max_length=200, null=True)

class ChildModel(models.Model):
	parent = models.ForeignKey(Customer)
	name = models.CharField(max_length=200, null=True)

parent = ParentModel.objects.first()
#Returns all child models related to parent
parent.childmodel_set.all()

original source : https://youtu.be/bc6wW-g6uss

image
image

=========================================================

.

.

image

=========================================================

.

.

image

=========================================================

.

.

image

content provider에서 data set이란 database의 table이라고 생각할수 있다.

=========================================================

.

.

content provider에서 data를 가져올때 cursor에 넣어서 되돌린다. 즉 cursor를 통해 data에 접근하게 된다. 

image
image

=========================================================

.

.

image

Contract class는 아래와 같이 만들수 있다. authority, data set이름, uri, 칼럼이름, type 이름등을 지정해서 여러곳에 햇갈리지 않고 일관되게 사용할수 있게 한다. 

image
image

content provider의 data에 접근하기위해 <uses-permission> 를 이용해서 permission을 얻을수 있다. content provider는 다른 app이 어떤 permission을 얻어야 하는지 지정해주어야 한다. 경우에 따라 다른 app에게 임시 permission은 허용하기 위해서는 content provider에 android:grantUriPermissions를 설정하거나 하위에 <grant-uri-permission>설정한다. 임시 permission 이 필요한 app의 경우 호출 intent에 위와 같이 Flag를 지정해 준다. 

image
image
image

=========================================================

.

.

image
image
image
image
image

=========================================================

.

.

image
image
image

=========================================================

.

.

참고자료) UriMatcher 실제 사용 예시 https://youtu.be/6ZbAsvifQq8

image

=========================================================

.

.

image
image

=========================================================

.

.

image

=========================================================

.

.

authority, data set이름, uri, 칼럼이름, type 이름등을 지정해서 여러곳에 햇갈리지 않고 일관되게 사용할수 있게 한다. 

image
image

=========================================================

.

.

image
image
image
image
image
image
image
image

=========================================================

.

.

image

original source : https://youtu.be/ycK7uN_fJJM

이강의에서 만들고자 하는 예시는 위와 같다. 오른쪽은 체팅리스트 이고 왼쪽은 상세 채팅내용이다.

===========================================================

SimpleCursorAdapter는 ResourceCursorAdapter를 extends하고 ResourceCursorAdapter는 CursorAdapter 를 extends한다.

===========================================================

===========================================================

===========================================================

===========================================================

===========================================================

===========================================================

===========================================================

===========================================================

original source : https://youtu.be/qqB2OJTg1RU

===========================================================

===========================================================

===========================================================

===========================================================

adapter안에 이미 다른 cursor의 내용이 있을수 있으므로 changeCursor()를 이용 내용을 새롭게 고쳐준다.

===========================================================

===========================================================

===========================================================

===========================================================

original source : https://youtu.be/8Xiqg86GWl8

rawQuery()는 Cursor obj를 리턴한다.

===========================================================

위의 그림에 빨간색으로 age는 내용에 오타가 있어서 수정한 것이다.

===========================================================

Cursor는 기본적으로 -1부터 시작된다. 즉 아무런 데이터가 없는 경우에 cursor는 -1위치에 있게 된다. 실제 data는 0부터 시작된다.

===========================================================

===========================================================

===========================================================

nullColumnHack에 기입된 칼럼의 경우 데이터가 주어지지 않는 경우 NULL값이 배정되서 exception 발생을 막는다.

===========================================================

===========================================================

===========================================================

===========================================================

===========================================================

COLLATE LOCALIZED ASC는 지역언어를 정렬의 우선으로 하는 기능이다. 

===========================================================