Friday, November 12, 2010

JSP tag files and dynamic attributes

In a JSP tag file sometimes you need to test for the presence of an attribute rather than whether it's empty or not. I needed to know if the id attribute was specified even if the value was empty (""). This is because with Spring's form tags, if you pass any id at all, it uses what you passed even if it's empty, otherwise it generates its own id.

My entire tag file was more complicated than what's below, but this gives you the parts you need to know.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="frm" uri="http://www.springframework.org/tags/form"%>

<%@ tag dynamic-attributes="columnMap"%>

<c:set var="hasId" value="false" />
<c:forEach var="attr" items="${columnMap}">
    <c:if test="${attr.key eq 'id'}"><c:set var="hasId" value="true" /></c:if>
</c:forEach>

<c:if test="${hasId eq true}">
    <frm:input id="${columnMap.id}" />
</c:if>

Remove from wrapped set

Late breaking news. Ugh! Turns out that Set<String> ts = new TreeSet<String>(s) doesn't wrap (thanks rbohn). Not sure why I had it in my head that it did. It creates another Set that contains the elements from the first. That would explain it. It's kind of hard to get anywhere when your presuppositions are wrong!

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

If you wrap a set with something else TreeSet or ArrayList, etc. Then you iterate over the wrapped collection and call remove on the iterator your item will not be removed. How did I not know this? Have I forgotten? Did I never know?

public static void main(String[] args) {
    Set<String> s = new HashSet<String>();
    s.add("this");
    s.add("that");
    s.add("momma");
    Set<String> ts = new TreeSet<String>(s);
    for (Iterator<String> it = ts.iterator(); it.hasNext();) {
        String ss = it.next();
        if ("momma".equals(ss)) {
            it.remove();
        }
    }
    System.out.println(s);
}

The output of the above will be as shown below (in no specific order). Yo momma did not get removed.

[that, momma, this]

Monday, November 8, 2010

Prevent double click

This is the only bulletproof way I know to prevent double clicks. The onclick event has to disable itself. You can't do "onclick='void 0;' in a function that you call from onclick because you may have already clicked three times before that function gets called.

For buttons, just do the onclick.
<input type="button" onclick="this.onclick='void 0;';someFunction();" />

For links you also have to explicitly do nothing in the href. Don't do href="#". That will not work.
<a href="javascript:void 0;" onclick="this.onclick='void 0;';someFunction();">

OK, rbohn says the following also works. If you want to separate all the logic into a single function, you can as long as the function returns false and your onclick returns false. For example: onclick="return myFunc(this);" Also, you must use el.setAttribute and not el.onclick=. el.onclick= will work in some browsers, but not others.

Another nice thing about rbohn's way is that your href can be set to # which is a bit neater. OK, well in a nicely controlled environment, with rbohn's way you can set your href to #, but in the real messy world, you need to have it set to "javascript:void 0;" or "javascript:return 0;". I have seen it break when set to #.

function myFunc(el) {
    el.setAttribute('onclick','return false;');
    document.forms['aviForm'].submit();  
    return false;
}

<a href="javascript:void 0;" onclick="return myFunc(this);">Confirm</a>

<input type="button" onclick="return myFunc(this);" />

Tuesday, November 2, 2010

Hibernate mapping cheat sheet

Check list

1. Make sure Collections are initialized.
2. Make sure Collections can't be null.
3. Most names and some other fields should not be null.

@Entity
@Table(name = "ar_campus")
public class Campus . . .

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    Long id;

    @Column(name = "xxx")
    @Column(name = "xxx", length = 11, unique = true, nullable = false)
    String someColumn;

Join column name has this rule: "Single same name as property, set name of parent"

Child NO reference back to parent.

class Hand {
    @OneToMany
    @JoinColumn(name = "handid")
    Set<Finger> fingers;

Child HAS reference back to parent.

class Hand {
    @OneToMany(mappedBy = "hand")
    Set<Finger> fingers;
  
class Finger {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "handid")
    Hand hand;

One to one with join column:

class Organization {
    @OneToOne(optional = false, mappedBy = "organization")
    OrgBoardInfo boardInfo;

class BoardInfo {
    @OneToOne(optional = false)
    @JoinColumn(name = "orgid", unique = true, nullable = false, updatable = false)
    Organization organization;

One to one with shared key:

class User {
    @OneToOne()
    @PrimaryKeyJoinColumn
    public AuxInfo auxInfo;

class AuxInfo {
    //(note must NOT put @GeneratedValue annotation on id)
    @Id
    Long    id;

    @OneToOne()
    @PrimaryKeyJoinColumn
    User    user;

Built in Java classes (String, Date, Long) or custom element.

class Board {
    @Enumerated(EnumType.STRING)
    @Column(name = "type")
    public BoardType type;

class Campus {
    @Column(name = "holiday", nullable = false)
    @CollectionOfElements(targetElement = PersistentLocalDate.class)
    @JoinTable(name = "ar_campus_holiday", joinColumns = { @JoinColumn(name = "cid") })
    Set<LocalDate> holidays;

class User {
    @Column(name = "system", nullable = false)
    @CollectionOfElements
    @Enumerated(EnumType.STRING)
    @JoinTable(name = "user_desired_system", joinColumns = @JoinColumn(name = "user"))
    Set<ExternalSystem> desiredSystems;

class Reservation {
    @ManyToMany
    @JoinTable(name = "rez_sr", joinColumns = { @JoinColumn(name = "rezId") }, inverseJoinColumns = { @JoinColumn(name = "srId") })
    Set<SubReceipt> subReceipts;

Column specs (for postgresql)

    @Column(name = "yearsOfExperience", nullable = false, columnDefinition = "integer default 0")
    int            yearsOfExperience;

    @Column(name = "experience", nullable = false, columnDefinition = "character varying(5000) default ''")
    String        experience;

    @Column(name = "deleted", nullable = false, columnDefinition = "bool default false")
    boolean deleted;

    @Column(name = "reservationId", nullable = false, columnDefinition = "bigint default 0")
    long reservationId;