마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

DB pk 갱신에 대해서..[코드추가]

0 추천

폰 내부 db 를 사용해서, select로 저장된 값들을 커스텀 리스트뷰에 연동하여 보여주고 있습니다.

특정 값을 삭제할 때, 롱클릭으로 해당 position값을 받은 후 id값으로 넣어 삭제하는 방식을 구현하고있습니다.

일단 삭제 자체는 잘되는데... pk인 id값(int형)을 재 정렬 할 수 있을까요?

가령,

num  name

0        박철수

1        김민희

2       나윤지

이런식으로 회원이 있다면 ,1번 김민희를 삭제 시킨 후 다시 DB를 조회하면

0 박철수 / 2 나윤지가 남습니다... 가운데 공백이 생겨버리는데, DB데이터를 변경 후

pk값을 다시 정렬해서 부여할 수있나요? (0박철수/1나윤지 이런식으로)

 

맨 처음 테이블을 생성할 때는 다음과 같이 하였습니다.

"CREATE TABLE 테이블명 (num INTEGER PRIMARY KEY AUTOINCREMENT,"

+"name TEXT);

 

 

+ 혹시몰라서 소스도 추가했습니다. 지적/조언 부탁드립니다..일단 대화를 insert하는 부분은 제외했습니다.

 


		// ExamData 객체를 관리하는 ArrayList 객체를 생성한다.
		ArrayList<ExamData> data_list = new ArrayList<ExamData>();
		// 사용자 정의 어댑터 객체를 생성한다.
		m_adapter = new ExamAdapter(data_list);

		// 리스트를 얻어서 어댑터를 설정한다.
		m_list = (ListView) findViewById(R.id.var_list);
		m_list.setAdapter(m_adapter);

		//현재시간
		m_date_format = new SimpleDateFormat("yyyy/MM/dd", Locale.KOREA);
		m_time_format = new SimpleDateFormat("HH:mm:ss", Locale.KOREA);

		Log.d("onCreate", "내용출력하기");
		// 스크롤 맨 아래로 내려준다.
		m_list.post(new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				Log.d("listview post", "post");
				m_list.setSelection(m_adapter.getCount() - 1);
			}
		});
		ExamData data = null; // 이름,메세지,시간
		ExamData data2 = null; // 날짜

		dbHelper = new DBHelper(this, dbName, null, dbVersion);
		db = dbHelper.getReadableDatabase();
		sql = "SELECT * FROM student;";
		Cursor cursor = db.rawQuery(sql, null);
		String dbDate1 = null;
		String dbDate2 = null;
		if (cursor.getCount() > 0) // 하나라도 있는 경우
		{
			Log.d("메세지 총 갯수: ", "" + cursor.getCount() + "개");
			while (cursor.moveToNext()) {
				Log.d("조회결과:  ", String.format(
						"\n넘버: = %s ,이름: = %s, 메시지: = %s , 날짜: = %s , 시간: = %s",
						cursor.getString(0), cursor.getString(1), cursor.getString(2),
						cursor.getString(3), cursor.getString(4)));

				dbDate1 = cursor.getString(3);
				if (!dbDate1.equals(dbDate2)) {
					Log.d("바뀐 날짜 출력하기", dbDate1);
					data2 = new ExamData((int) 1, cursor.getString(3), null,
							null);
					m_adapter.add(data2);

				}
				dbDate2 = dbDate1;
				// Log.d("dbDate1",dbDate1);
				// Log.d("dbDate2",dbDate2);
				if (dbDate1.equals(dbDate2)) {
					Log.d("date", "date1 = date2");
				}

				//
				data = new ExamData((int) 0, cursor.getString(1),
						cursor.getString(2), cursor.getString(4));
				// 어댑터에 데이터를 추가한다.

				m_adapter.add(data);
			}
		} else 
		{
			null_text.setVisibility(View.VISIBLE);
			Log.d("onCreate조회결과가없습니다", "onCreate조회결과가없습니다.");
			
		}
		cursor.close();
		// 원하는 포지션으로 리스트뷰 스크롤하기.smoothScrollToPosition
		m_list.smoothScrollToPosition(m_adapter.getCount() - 1);
		mNotificationManager = (NotificationManager)
                this.getSystemService(Context.NOTIFICATION_SERVICE);
		mNotificationManager.cancelAll();
		
		
		
		//삭제
		m_list.setOnItemLongClickListener(new OnItemLongClickListener() {

			@Override
			public boolean onItemLongClick(AdapterView<?> parent, View view,
					int position, long id) {
				delete_dialog(position);
				return true;
				
			}
		});
		
		
	}// onCreate
	
	//aaa
	public void delete_dialog(final int position)
	{
		AlertDialog.Builder alert = new AlertDialog.Builder(MainActivity.this);
		alert.setPositiveButton("예", new DialogInterface.OnClickListener() {
		    @Override
		    public void onClick(DialogInterface dialog, int which) 
		    {
		    	Toast.makeText(getApplicationContext(), "예", 1000).show();
		    	db = dbHelper.getWritableDatabase();
		    	Log.d("position값",position+"");
				sql = "DELETE FROM student WHERE num ='"+ position +"';";
				db.execSQL(sql);
				;
				
		    }
		});
		alert.setNegativeButton("아니오", new DialogInterface.OnClickListener() {
			
			@Override
			public void onClick(DialogInterface dialog, int which) {
				// TODO Auto-generated method stub
				Toast.makeText(getApplicationContext(), "아니오", 1000).show();
				dialog.dismiss();     //닫기
			}
		});
		alert.setMessage("이 메세지를 삭제하시겠습니까?");
		alert.setTitle(position+"번째 메세지 삭제");
		alert.show();
		
	}
	
	// http://blog.daum.net/_blog/BlogTypeView.do?blogid=0NhTQ&articleno=251
	public static class ExamData {
		public int type = 0; // 레이어종류를 구분할 변수.
		public String data0 = null;
		public String data1 = null;
		public String data2 = null;

		// byte parm_type,
		public ExamData(int parm_type, String parm_data0, String parm_data1,
				String parm_data2) {
			type = parm_type;
			data0 = parm_data0; // 이름
			data1 = parm_data1; // 대화명
			data2 = parm_data2; // 시간
		}
	}

	

	// BaseAdapter 를 상속하여 어댑터 클래스를 재정의한다.
	private class ExamAdapter extends BaseAdapter {
		private LayoutInflater m_inflater = null;

		// ExamData 객체를 관리하는 ArrayList
		// private ArrayList<ExamData> m_data_list; //전역변수로 끌올
		public ExamAdapter(ArrayList<ExamData> items) {
			m_data_list = items;
			// 인플레이터를 얻는다.
			m_inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		}

		// ArrayList 에 ExamData 객체를 추가하는 메서드
		public void add(ExamData parm_data) {
			m_data_list.add(parm_data);
			// 데이터가 변화됨을 알려준다.
			notifyDataSetChanged();
		}

		// 어댑터에서 참조하는 ArrayList 가 가진 데이터의 개수를 반환하는 함수
		@Override
		public int getCount() {
			return m_data_list.size();
		}

		// 인자로 넘어온 값에 해당하는 데이터를 반환하는 함수
		@Override
		public ExamData getItem(int position) {
			return m_data_list.get(position);
		}

		// 인자로 넘어온 값에 해당하는 행 ID 를 반환하는 메서드
		@Override
		public long getItemId(int position) {
			return position;
		}

		// 인자로 넘어온 값에 해당하는 뷰의 타입을 반환하는 메서드
		@Override
		public int getItemViewType(int position) {
			return m_data_list.get(position).type;
		}

		// getView 메서드로 생성될 수 있는 뷰의 수를 반환하는 메서드
		@Override
		public int getViewTypeCount() {
			return 2;
		}

		// 각 항목에 출력될 뷰를 구성하여 반환하는 메서드
		public View getView(int position, View convertView, ViewGroup parent) {
			View view = null;
			// 해당 항목의 뷰 타입을 얻는다.
			int type = getItemViewType(position);
			// convertView 뷰는 어댑터가 현재 가지고 있는 해당 항목의 뷰객체이다.
			// null 이 넘어오는 경우에만 새로 생성하고, 그렇지않은 경우에는 그대로 사용한다.
			if (convertView == null) {
				view = m_inflater.inflate(R.layout.list_item1, null);
				switch (type) 
				{
				case 0://대화 형식 레이아웃
					view = m_inflater.inflate(R.layout.list_item1, null);
					break;
				case 1://시간타이틀 레이아웃
					view = m_inflater.inflate(R.layout.list_item3, null);
					break;
				}
			} 
			else 
			{
				view = convertView;
			}
			//요청하는 항목에 해당하는 데이터 객체를 얻는다.
			ExamData data = m_data_list.get(position);
		
			// 데이터가 존재하는 경우
			if (data != null) 
			{
				if (type == 0) 
				{
					TextView user_tv = null, msg_tv = null, date_tv = null;
					user_tv = (TextView) view.findViewById(R.id.user_view1);
					msg_tv = (TextView) view.findViewById(R.id.message_view1);
					date_tv = (TextView) view.findViewById(R.id.date_view1);

					// 이름, 대화내용, 시간정보를 텍스트뷰
					user_tv.setText(data.data0);
					msg_tv.setText(data.data1);
					date_tv.setText(data.data2);
				} 
				else if (type == 1) 
				{
					((TextView) view).setText(data.data0);
				}
				
				Log.d("getview","getview!");
			}
			return view;
		}
	}

	

 

anci (19,950 포인트) 님이 2015년 4월 15일 질문
anci님이 2015년 4월 15일 수정

3개의 답변

+2 추천
무슨 이유인지는 모르겠지만, 그런 방식은 하지마세요.

순번이 필요하면 뷰에서 추가시키는 방식으로 하는게 낫습니다.
쎄미 (162,410 포인트) 님이 2015년 4월 15일 답변
답변 감사합니다..현재 pk를 0부터 순차적으로 자동증가 시켜서 생성시키고 있고..
2가지 뷰를 섞어서 커스텀 리스트뷰에 뿌려주고 있습니다; 예제를 보고 수정하는지라..리스트뷰를 사용한 것이구요. 텍스트박스들을 출력 한 후에, longclick을 하여 해당 객체를 삭제하고자 합니다..처음엔 pk을 이용해서 삭제하려고했는데 그렇다 보면 본문의 문제처럼 0,1,5,8..이런식으로 pk값 간에 공백이 생겨버립니다. 그래서 컬럼값을 하나 더 만들어서 pk값 대신 그 컬럼값으로 삭제해야하나 생각하고 있습니다. 리스트뷰의 포지션값과 동일하게 맞추기 위해 delete등 삭제가 된다면 다시 일괄적으로 그 컬럼값들을 0~데이터전체갯수만큼 +1해야 하나 싶은데 ..다른 분들의 의견이 어떨지 궁금합니다 ㅠㅠ
pk 간에 공백이 생기는게 어째서 문제가 되는지 이해가 되질 않네요..
mamondebaltob님//댓글 감사합니다. 입력된 순서대로 db값을 리스트뷰에 순차적으로 뿌려주는데, 제가 원하는 것은 이 값을 삭제하고, 다시 커스텀 리스트뷰에 반영하는 것이었습니다. 삭제하기 위해선 현재 리스트뷰에서 롱클릭으로 선택한 아이템의 포지션 값을 DB의 pk혹은 다른 컬럼값과 대조해서 삭제하려고 했습니다. 일단 DB에 0/1/2/3 데이터가 있으니, 리스트뷰에도 순차적으로 0/1/2/3으로 어뎁터에 add 해준것이구요. 2번 녀석을 삭제 하면 DB내에 2번도 삭제 됩니다. 그 뒤로 아무런 작업없이 DB와 리스트를 대조하면 DB는 0/1/3 , 리스트뷰도 0/1/3이 남지만. 3번 을 삭제하려고 클릭하면 2라는 포지션을 갖게 되는거죠. 이 경우 삭제할 때 문제가 발생하기 때문에 질문드려봤습니다..
애초에 삭제할때 ListView 의 position 으로 삭제하는 것이 잘못된 접근입니다.
ListView Adapter 의 아이템이 각각 pk 를 갖고 있게 설계를 바꾸세요
ex) adapter 가 List<Something> datas; 를 갖고 있을 거잖아요?
이 Something 인스턴스가 db pk 값을 갖고 있어야 한다는 것이죠
mamondebaltob님//헉..답변 감사합니다 ㅠㅠ
현재 제가 두개의 infalter 로 [날짜타이틀]or[대화내용]으로 listview에 출력하고 있습니다.DB와 매칭되는 [대화내용]을 getivew등에서 mamondebaltob님이 말씀하신대로 pk값을 부여할 수있는것인가요?(추가&삭제 동적으로 갯수가 변화)
일단 소스가 거의 보기가 힘드네요.. ㅋㅋ
ExamData 가 POJO 인거 같구요
애초에 Cursor 로 데이터 땡겨서 ExamData 만들때
ExamData 에다가 pk 정보를 넣어주라는 말이죠
그리고 getView 에서 setonLongClick 같은 것을 할때
position 이 아니라 data.getPrimaryKey() 와 같은 식으로 구현하시라는 말이었습니다.
답변 감사합니다..
저도 구글링으로 본 샘플에서 리스트에 추가 되는 레이아웃과 그 안에 들어갈 내용만 변경한지라.. 전체적인 이해도가 부족했던것 같습니다.
즉, cursor로 DB전체의 데이터를 조회해서 하나하나 추가할 때마다
new ExamData((int) 1, m_date_format.format(new Date()),
                    null, null); 이 부분에 각각해당하는 pk값을 넣고, getview에서 setOnItemLongClickListener 부분에 포지션이 아닌 해당 데이터의 pk값을 받아와서 처리하라 이 말씀이신거죠?
네 맞습니당 ㅇㅇㅇㅇㅇ
답변감사합니다. getview내에서 data != null 일 때, setOnItemLongClickListener 를 사용해서 data.data1 로, 일단은 선택된 아이템의 [대화내용]을 toast로 띄우고자 하는데요, data.data1을 사용하려고하니 Exam data 요녀석을 final로 처리 해야한다고 에러가 뜨네요; final로 처리하면 변수내용이 바뀌지 않고 같은것만 나오게 되지 않나요..? 또한, final로 처리해서 대화내용을 출력해보니 엉뚱한 값이 나옵니다. [스크롤 중간쯤에 출력된 메세지]
+1 추천
pk 를 drop 하고 다시 create 하면 될 수도 있으나 추천하지 않습니다.

공백을 없애야만 하는 크리티컬한 이유가 있지 않은 이상에 (대부분의 경우에는 이런 경우는 없습니다.)

db 내부적으로 pk 를 관리하므로 손대지 않는 것이 바람직합니다.
mamondebaltob (32,750 포인트) 님이 2015년 4월 15일 답변
+1 추천
db 의 데이터와 리스트뷰의 포지션 을 매칭시키기 위한것 같으시군요.

DB를 변경해서 재정렬하지마시고 adapter에서 관리하는 데이터셋을 이용하시는게 좋습니다.

리스트에서 데이터 삭제 -> db raw 를 제거함과 동시에 adapter에서 관리하는 데이터셋(리스트나 맵 등)을 삭제 해주시면 sync 가 맞겠지요.
포도맛카라 (700 포인트) 님이 2015년 4월 15일 답변
...