*
The moose likes Developer Certification (SCJD/OCMJD) and the fly likes How to write back an updated record to db file? Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of Murach's Java Servlets and JSP this week in the Servlets forum!
JavaRanch » Java Forums » Certification » Developer Certification (SCJD/OCMJD)
Bookmark "How to write back an updated record to db file?" Watch "How to write back an updated record to db file?" New topic
Author

How to write back an updated record to db file?

Dave Gear
Greenhorn

Joined: Feb 16, 2004
Posts: 13
Actually, this is a normal IO problem. My question is: In the updateRecord(long recNo, String[] data, long lockCoolie) method, when I write back the record to the db file, I did like this:

But when I tested it, it failed at dbFile.write(b) and the IOException says: Access Denied. I'm really stuck there without anyway to sovle it. So anybody who can point out what�s wrong here? Or what�s your way to write record to db file? Thanks a lot!

[Enclosed the code excerpt in CODE tags]
[ June 08, 2004: Message edited by: Philippe Maquet ]
Javini Javono
Ranch Hand

Joined: Dec 03, 2003
Posts: 286
Hi,

If you are using the RandomAccessFile, perhaps you need
to open it using "rw" for read and write?

Thanks,
Javini Javono
Philippe Maquet
Bartender

Joined: Jun 02, 2003
Posts: 1872
Hi Dave,

Welcome to JavaRanch and this forum!

Javini's suggestion probably will solve the issue.

Just a comment about the code posted above: why do you write each field value separately? You could prepare a buffer (array of bytes) for the whole record, and then write it in one operation.

Regards,

Phil.
Dave Gear
Greenhorn

Joined: Feb 16, 2004
Posts: 13
Thank you for your reply reply. I will try it later then to see if it works. Thanks again.
Jason Hocker
Ranch Hand

Joined: Jul 23, 2003
Posts: 132
Originally posted by Philippe Maquet:

Just a comment about the code posted above: why do you write each field value separately? You could prepare a buffer (array of bytes) for the whole record, and then write it in one operation.


Will the grader care?
Philippe Maquet
Bartender

Joined: Jun 02, 2003
Posts: 1872
Hi Jay,

Will the grader care?


I really don't know for the *actual* grader. But I know that if I was, I would...

Now seriously, I think that read *once* or even a few times in the project, such a lack of optimization (BTW, that code is even not simpler than a much better one, it's "just" slower) shouldn't lead to a lower score. But in case it'd be repeated regularly in the whole project, I fear that the grader would think: "OK, it works, but he's not a good coder". And would *you* give a high score to an average coder for a *developer* exam?

Today, I had to grade 17 small EJB development assignments. I didn't care for pure coding practices because it was not in my objectives for that exam (pure EJB knowledge). But I can tell you that:

  • after having read the 17 implementations, I know who is a coder and who is not (even if I didn't care in this context)
  • in another context, I'd have made a huge difference in the scores.



  • Regards,

    Phil.
    Dave Gear
    Greenhorn

    Joined: Feb 16, 2004
    Posts: 13
    The RW works, thanks a lot.

    But now I have another question. Simply put, if String [] rec represents the whole record, and each element rec[0], rec[1]�� corresponds to each field, which has a fixed length, namely 32, 64, 6, 4, 6, 8, 8. So most likely these elements will have some space at the end. Now, what I want to do is: update one element without changing its original length, say, I want to change the rec[1] to a string �Have a try� followed by a string of space which will make its total length keep to 64. I have tried several ways but never worked. No matte which way I used, the rec[1]�s length will be cut off to the length of the string assigned to it. In other words, the space at the end will be cut off. Is it possible to implement what I wanted to do? Or maybe my idea is not right, anybody can give me some helps?
    Philippe Maquet
    Bartender

    Joined: Jun 02, 2003
    Posts: 1872
    Hi Dave,

    Does the following help?

    1� allocate a bytes array for the record: new byte[recordLength]; // recordLength == 128 in your example
    // notice that your bytes array is automatically filled with 0s
    2� for each field:
    2.a - get its bytes representation by a call to String.getBytes(String charsetName)
    2.b - check that the field length is not overflowed or throw some IllegalArgumentException
    2.c - use System.arraycopy() to copy the field's bytes array in the record's bytes
    array at the right place (field's offset)
    3� write the record's bytes array in the file.

    Regards,

    Phil.
    Dave Gear
    Greenhorn

    Joined: Feb 16, 2004
    Posts: 13
    Philippe, thank you very much. I've not tried it yet, but it does make sense to me. I'll try it later. Thanks again.
    Dave Gear
    Greenhorn

    Joined: Feb 16, 2004
    Posts: 13
    Hi, Philippe or everybody:

    I'm still having trouble in this read-write problem. I tried your way and everything works well except the one byte delete flag. I'm stuck there getting nowhere. I described the problem briefly as following:
    In the readData() which I wrote myself (BTW, the purpose I wrote this method is to read data from db file and save them to a HashMap which uses the recNo as key as String[] as value), the code related to the delete flag is:



    Comment: this recordData will be saved as a value into a HashMap.

    In the writeRead() which I wrote myselft, the code related to the delete flag is:

    The problem is: Before I did any writeRead operation, the two system.out will get right answer:
    The length of flag is:1
    The content of flag is: 0.
    But once I called writeRead to update any record, the system.out of that record will be:
    The length of flag is:2
    The content of flag is: 48 (or something other numbers).

    What's the problem here? I've been working on it whole day. I would really appreciate your help or advice.

    [Andrew: put the source code between [code] and [/code] UBB tags]
    [ June 10, 2004: Message edited by: Andrew Monkhouse ]
    Andrew Monkhouse
    author and jackaroo
    Marshal Commander

    Joined: Mar 28, 2003
    Posts: 11404
        
      81

    Hi Dave,

    I have edited your post to put the code between [code] and [/code] UBB tags. Doing this ensures that indenting is preserved, which makes the code easier to read.

    When you are writing your post, there are a number of buttons just below the edit pane, which will insert the tags for you, so you don't have to remember what they are.

    If you would like to edit your original post so that you can see what I have done, you can click on the button that is just above your post.

    There are a few problems with the code you posted. But I suspect the problem that you are experiencing is with the code you haven't posted - the bit marked "???" in the writeRead() method. I suspect you are not changing your pointer to the position in the "recordBuffer" as you try and fill it.

    So if (as I suspect) you did something like:



    then the subContractor data will overwrite the deleted flag data, and then the city data will overwrite the subContractor data, and the ....

    What you need to be doing is updating the position in the record buffer where you are copying the new data:



    (Note: as Phil mentions earlier, you should also be checking for sizes of the data you are copying, and throwing an exception if the size is incorrect.)

    Another problem is that your "recordData" object is an array of Strings. This means that you must coerce the deleted flag (which is a byte) into a String. If you made recordData an array of Objects then you could store the Byte directly into the recordData without converting to and from a String.

    You have made a mistake with converting back from the String representation of the byte back into the byte[]. If it hadn't been for the other problems, this particular problem may have gone unnoticed, as it only causes problems when you are converting a byte which has more than one digit. If you have a byte which contains the number 45, then convert that into a String, it is stored as the String "45". If you call the getBytes() method on any String, it returns the bytes needed to represent the String. In the case of the String "45", it will return two bytes - the byte representing the "4", and the byte representing the "5" (and this is just ASCII - you could end up with far more complex conversions). What you need to do is create a Byte() from the String, then get the byteValue of it. (But, as I mentioned earlier, if you use an Object[] you will not need this conversion to/from Strings anyway ).

    Regards, Andrew


    The Sun Certified Java Developer Exam with J2SE 5: paper version from Amazon, PDF from Apress, Online reference: Books 24x7 Personal blog
    Philippe Maquet
    Bartender

    Joined: Jun 02, 2003
    Posts: 1872
    Hi Dave,

    The fact that you store the deleted flag value as a String in the field values array of Strings looks weird to me. Check your specific instructions, but it's highly probable that the deleted flag is not to be included in the array returned by readRecord().

    Now to fill your recordBuffer (which BTW and on the contrary of what I wrote in a previous post should be allocated with a length of recordLength + 1 (or + 2 if your deleted flag is a short)):

  • For the "deleted" flag, if it's a byte you may just assign recordBuffer[0] with its value. If it's a short (no luck ), you'll have to figure out which 2 bytes to assign to recordBuffer[0] and recordBuffer[1] for deleted and valid records.
  • For field values, use System.arraycopy() as shown by Andrew above, but with the bytes array's length as the value for its last argument, instead of the field's length. If you don't, you risk an IndexOutOfBoundsException (the getBytes() you use to get the bytes representation of the String field value may return less bytes than the field's length). See below:





  • Regards,

    Phil.
    [ June 12, 2004: Message edited by: Philippe Maquet ]
    Andrew Monkhouse
    author and jackaroo
    Marshal Commander

    Joined: Mar 28, 2003
    Posts: 11404
        
      81

    Hi Dave,

    Just to clarify your issue with using an Object[] instead of a String[] - all it means is that you would change the following code:



    To:



    When you try to get the data out again, you will need to cast the object stored back into it's real data type:



    But you also need to note Phil's comment above about what you are sending back to the client. You mentioned that you are storing the records in a HashMap, in which case you can store the deleted flag with the record, and you can use an Object[] for this.

    Regards, Andrew
    Dave Gear
    Greenhorn

    Joined: Feb 16, 2004
    Posts: 13
    Many thanks to Philippe and Andrew. But I'm still stuck there. I feel exausted because I've been working on this problem 24 hours each day almost the whole week but getting nowhere.

    But still, thanks to you guys.
    Dave Gear
    Greenhorn

    Joined: Feb 16, 2004
    Posts: 13
    I'm getting more and more confused about this one byte delete flag? It should be saved in the String[] with those other record fields? For example, there is a method ublic String[] readRecord(long recNo). The return value of this method is a String[], which represents the whole record. What I'm thinking is I save the one byte delete flag as the first element of this String[]. I can't find out other ways to bundle this flag to the paticular record?
    Dave Gear
    Greenhorn

    Joined: Feb 16, 2004
    Posts: 13
    So I should find a way to convert this one byte flag to String back and forth, which is the puzzle I've been working on for one week. Any body can give me a hand?
    Dave Gear
    Greenhorn

    Joined: Feb 16, 2004
    Posts: 13
    My question is about read and write the record data between the db file, especially about the one byte delete flag at the beginning of the record.
    1)
    In the readData method, to read each record data, I used the following code:



    2)
    In the writeData method, to write the updated record back to db file, the
    related code is here:




    My question here is: in the readData(), I System.out.println the total length of currentRecordData, the length of currentRecordData[0], and content of currentRecordData[0]. Before I used the writing operation, the total length of currentRecordData is 183, which is the length of all the data fields(32,64,64,6,8, 8) plus the one byte delete flag length. And the length of currentRecordData[0] and content of currentRecordData[0] are 1 and 0 respectively. However, after I used the writing operation, these three will be changed to: 184, 2, 48(or some other numbers), respectively. The db file will look like (please see the row 5, which I updated):
    Before writing:

    after writing:


    You will see the delete flag is different between the two, also the second field which I updated intentionally has some unreadable signs instead of space, this is another problem I want to ask.

    I hope this email will make it clear to you. And I really appreciate your time and help.

    Best regards,

    Dave

    [Phil: corrected use of CODE tags]
    [ June 13, 2004: Message edited by: Philippe Maquet ]
    Andrew Monkhouse
    author and jackaroo
    Marshal Commander

    Joined: Mar 28, 2003
    Posts: 11404
        
      81

    Hi Dave,

    Originally posted by Dave Gear:
    I'm getting more and more confused about this one byte delete flag? It should be saved in the String[] with those other record fields? For example, there is a method: public String[] readRecord(long recNo). The return value of this method is a String[], which represents the whole record. What I'm thinking is I save the one byte delete flag as the first element of this String[]. I can't find out other ways to bundle this flag to the paticular record?


    Ahh: but if the record is deleted, you cannot read it

    Does your readRecord method throw an exception? or have have some other way of indicating that the record does not exist?

    If so, then you do not need to worry about storing or returning the delete flag. If the flag indicates that the record has been deleted, you throw the appropriate exception, or do whatever else is appropriate. If the flag indicates that the record has not been deleted then you return all the fields of the record (which do not include the deleted flag).

    Regards, Andrew
    Andrew Monkhouse
    author and jackaroo
    Marshal Commander

    Joined: Mar 28, 2003
    Posts: 11404
        
      81

    Hi Dave,

    (See my post just above this one first - you probably do not need the information in this post now).

    Originally posted by Dave Gear:
    In the writeData method, to write the updated record back to db file, the
    related code is here:




    As I mentioned earlier, this code is wrong.

    Originally posted by Andrew Monkhouse:
    You have made a mistake with converting back from the String representation of the byte back into the byte[]. If it hadn't been for the other problems, this particular problem may have gone unnoticed, as it only causes problems when you are converting a byte which has more than one digit. If you have a byte which contains the number 45, then convert that into a String, it is stored as the String "45". If you call the getBytes() method on any String, it returns the bytes needed to represent the String. In the case of the String "45", it will return two bytes - the byte representing the "4", and the byte representing the "5" (and this is just ASCII - you could end up with far more complex conversions). What you need to do is create a Byte() from the String, then get the byteValue of it.


    There are other problems which need to be fixed, but start with the basics:
  • Construct a Byte from your String
  • Then get the byteValue from your newly created Byte object.


  • Do not attempt to getBytes from the String - this is wrong: it will return the bytes that can be used to recreate the String. That is:



    Regards, Andrew
    Andrew Monkhouse
    author and jackaroo
    Marshal Commander

    Joined: Mar 28, 2003
    Posts: 11404
        
      81

    Hi Dave,

    Perhaps you should post your entire update() method, and we can see if there is another problem elsewhere.

    Regards, Andrew
    Jesse Jesse
    Greenhorn

    Joined: Jun 04, 2004
    Posts: 22
    Hi

    I use stringVar.getBytes("US-ASCII") to get an array of bytes for the string. I think this is ok for my assignment as it states

    All numeric values are stored in the header information use the formats of the DataInputStream and DataOutputStream classes. All text values, and all fields (which are text only), contain only 8 bit characters, null terminated if less than the maximum length for the field. The character encoding is 8 bit US ASCII.


    SCJP, SCJD, SCWCD, SCBCD
    Andrew Monkhouse
    author and jackaroo
    Marshal Commander

    Joined: Mar 28, 2003
    Posts: 11404
        
      81

    Hi Mark,

    Your usage is fine, but it is different from what Dave is trying to achieve.

    You are trying to get the byte[] corresponding of an 8 bit US ASCII string to put in a file. In other words, the exact bytes that make up the String.

    Dave is trying to get the byte that is represented in the string.

    Regards, Andrew
    Dave Gear
    Greenhorn

    Joined: Feb 16, 2004
    Posts: 13
    Hey, Andrew: Good news. Your suggestion has helped me out! In other words, your way of Constructing a Byte from the String then getting the byteValue from the newly created Byte object instead of using String�s getBytes solved the problem. God, I really didn�t know the difference between the two before. I really appreciate your help!

    But now I have One more question:
    Just for the reason of test, I read data from the db file to another text file(I call it daveTest) in which the layout of the data makes it easier to read, row by row, field by field. For most of the fields, there are some spaces at the end because it�s not possible they have exactly the same length they should have.

    My question is as following: when I used my writeData which we discussed before to update a field to db file and read it again to daveTest file. When I opened the daveTest file using a kind of text editor, which is called editplus, for that updated field, the spaces at the end have been taken place by a string of unreadable small scares. But, if I used Microsoft�s Notepad or Wordpad to open this file, they are still spaces.
    Maybe this is not a big deal, but I�m just afraid I did something wrong. Anyone can give some comments on this?
    Andrew Monkhouse
    author and jackaroo
    Marshal Commander

    Joined: Mar 28, 2003
    Posts: 11404
        
      81

    Hi Dave,

    That is good news.

    The squares shown at the end of the string are the editor's way of showing you that there is something there that it cannot display using the normal US ASCII character set. Different editors handle this in different ways - some show the squares (as you saw), some show spaces (as you also saw), and some use some special indicator to display the character (e.g. depending on the editor, '\n' might be shown as '\n' or '^n' or something else).

    In your case, I suspect that the invisible characters are the NULL character: "0x00" - giving you null terminated strings. Which your instructions probably tell you are used in the database file, but obviously aren't used, or you wouldn't have noticed them :roll: .

    Now you will have to make a design decision (and document it ) - do you go back and fill the String with spaces to match the existing file, or do you write your code to handle either spaces or nulls (which are allowed in the instructions). Your choice.

    It may seem that at present handling nulls may be easier, but if you don't understand why they are there / how to get rid of them, then you may want to look into changing them to spaces, just for your own eductational purposes.

    Regards, Andrew
    Dave Gear
    Greenhorn

    Joined: Feb 16, 2004
    Posts: 13
    OK, I got it. In the original db file, when it's less than the maximum length for the field, spaces are used at the end of the field instead of null terminatd. But we are required to use null terminated at that situation which is what I happened to do.

    Well, Andrew, I can't express how many thanks I own to you. Same thanks to Phillippe and anyone who concerned this thread.
     
    I agree. Here's the link: http://aspose.com/file-tools
     
    subject: How to write back an updated record to db file?
     
    Similar Threads
    Reading the record from the database
    NX: Please Help - Writting and Reading 2 byte flag
    Bodgitt & Scarper - Database Record Numbering
    DB file reading problem
    Random access reads and writes