Template::Plugin::JavaSQL - Help generate Java from database schemas.


Template-Plugin-Java documentation Contained in the Template-Plugin-Java distribution.

Index


Code Index:

NAME

Top

Template::Plugin::JavaSQL - Help generate Java from database schemas.

SYNOPSES

Top

Within an XML file processed by the Java plugin:

	<sql:
		query="select foo as f, bar as b from some_table"
	>

	or

	<sql:
		table="some_table"
	>

Via a template such as:

	[% USE Java %]
	[% Use JavaSQL %]
	...
	String query =
	"select [% JavaSQL.columnNames.join(", ") %] from [% JavaSQL.tables.join(", ") %]";
	[% IF JavaSQL.where %]
	query += " where [% JavaSQL.where %] ";
	[% ELSE %]
	query += " where 0=0 ";
	[% END %]
	[% FOREACH JavaSQL.columns %]
	if (${varName}Set) { query += " and $name = ?"; }
	[% END %]
	[% IF JavaSQL.order %]
	query += "order by [% JavaSQL.order %]";
	[% END %]
	stmt = myConnection.prepareStatement(query);

Just use the DBClass.template from the distribution for fully functional database classes.

DESCRIPTION

Top

In addition to methods that refer to parts of a SQL query, any columns resultant from the query or table will be added as java variables to the variables hash, with close-as-possible mapped types.

METHODS

Top

new

Constructor. If given one parameter of type Template::Context, will use that as a context. Then process the contents of "sql:" within the stash. This is what typically happens inside a template when a [% USE JavaSQL %] directive is encountered.

context

Returns Template::Context this instance has, if any.

query

The complete query, either supplied at instantiation or inferred from other arguments.

where

The "where" portion of the SQL query, excluding the word "where" itself.

tables

A list of tables used by the query.

tableCount

Number of tables used by query.

updatable

Whether the query used can be used to update the table.

order

An ORDER BY clause, if one was used.

columnNames

A list of column names used in the query.

isColumn

Check whether a variable name is a column.

varToColumn

Gives the column name mapping of the Java variable name.

columnToVar

Gives the Java variable name mapping of the column name.

columnCount

Number of columns returned from query.

columns

Intended to be used as [% FOREACH JavaSQL.columns %] ... See SYNOPSYS.

parseQuery

Used internally to parse a SQL query and set the appropriate state variables.

ENVIRONMENT

Top

DBI_DSN

DBI Data Source Name, for example, the data source for MySQL and database name "test" it would be: dbi:mysql:database=test

DBI_USER

User name to connect to the database as.

DBI_PASS

Password for database, can be blank for no password.

AUTHOR

Top

Rafael Kitover (caelum@debian.org) The concept and related templates are based on Andrew Lanthier's dbgen framework (an unreleased development utility).

COPYRIGHT

Top

BUGS

Top

Probably many.

TODO

Top

A very great deal.

SEE ALSO

Top

perl(1), Template(3), Template::Plugin::Java::Utils(3), Template::Plugin::Java::Constants(3),


Template-Plugin-Java documentation Contained in the Template-Plugin-Java distribution.
package Template::Plugin::JavaSQL;

use strict;
use base qw(Template::Plugin);
use Carp;
use DBI;
use Template::Plugin::Java::Constants qw/:all/;
use Template::Plugin::Java::Utils qw/parseOptions sqlType2JavaType/;

use constant QUERY => qr/
		from
		\s*(.*?)\s*	# $1 = tables
		(?:where
	 		 	\s*(.*?)\s*	# $2 = condition
				(?:order\s*by
				\s*(.*)\s*$	# $3 = order by clause
				)?
		)?
		\s*$
/xi;

my $dbh = DBI->connect_cached (
	$ENV{DBI_DSN},
	$ENV{DBI_USER},
	$ENV{DBI_PASS},
	{RaiseError => 1}
);

sub new {
	my $class = shift;
	my $self  = bless {}, ref $class || $class;

	if (UNIVERSAL::isa($_[0], 'Template::Context')) {
		$self->{context} = shift;
	} elsif (UNIVERSAL::isa($_[0], 'HASH')) {
		my $args = shift;
		@$self{ keys %$args } = values %$args;
	}

	if (@_ % 2 == 0) {
		my %more = @_;
		@$self{ keys %more } = values %more;
	}

# Now process the sql context if any.
	my $vars = $self->context->stash->get('variables');
	my $spec = delete $vars->{'sql:'};

	return unless $spec;

	my $query;
	if (exists $spec->{query}) {
		$query = $spec->{query};
	} elsif (exists $spec->{table}) {
		$query = "select * from ".$spec->{table};
	}

	my $sth = $dbh->prepare_cached($query);
	$sth->execute;

	my $columns = $sth->{NAME_lc};

	my $result;
	@$result{ @$columns } =
		map { { 'java:type' => sqlType2JavaType $_ } }
			map { $dbh->type_info($_)->{TYPE_NAME} }
				@{ $sth->{TYPE} };
	
	$sth->finish;

	$self->{columns} = [sort @$columns];
	$self->parseQuery($query);

	$result = parseOptions($result);

	@{$self->{column2var}}{ sort @$columns } = sort keys %$result;
	@{$self->{var2column}}{ sort keys %$result } = sort @$columns;

# Place the variable info from the table back into main variables hash.
	@$vars{ keys %$result } = values %$result;

# If only one table used in query, check whether all columns are used (this
# makes it updatable in the underlying templates). Ideally, we should be
# checking if all the primary keys are being used.
#
# Also, this can be overridden in the context or constructor, which is not a
# good idea.
	if (!exists $self->{updatable} && $self->tableCount == 1) {
		my $test_sth = $dbh->prepare_cached (
			"select * from ".$self->tables->[0]
		);

		if ($test_sth->{NUM_OF_FIELDS} == $sth->{NUM_OF_FIELDS}) {
			$self->{updatable} = TRUE;
		}
	} elsif (exists $spec->{updatable}) {
		$self->{updatable} = $spec->{updatable};
	}

	return $self;
}

sub context { $_[0]->{context} }

sub query   { $_[0]->{query}   }

sub where   { $_[0]->{where}   }

sub tables  { $_[0]->{tables}  }

sub tableCount { scalar @{$_[0]->{tables}} }

sub updatable { $_[0]->{updatable} }

sub order   { $_[0]->{order}   }

sub columnNames { $_[0]->{columns} }

sub isColumn {
	my ($self, $name) = @_;
	$name ||= $self->{context}->stash->get('name');
	  
	scalar grep { $name eq $_ } @{$_[0]->{columns}}
		and return TRUE;
	scalar grep { $self->varToColumn($name) eq $_ }
					@{$_[0]->{columns}}
		and return TRUE;

	return FALSE;
}

sub varToColumn { return $_[0]->{var2column}{$_[1]} }

sub columnToVar { return $_[0]->{column2var}{$_[1]} }

sub columnCount { scalar @{$_[0]->{columns}} }

sub columns {
	my $self    = shift;
	my $vars    = $self->context->stash->get('variables');

	my $position= 1;

	return [ map {
		my $key  = $_;
		my $var  = $self->{column2var}{$key};
		my $type = $vars->{$var}{'java:type'};

		{
			name	=> $key,
			varName => $var,
			capName	=> ucfirst $var,
			type	=> $type,
			capType	=> ucfirst $type,
			value   => $vars->{$var},
			initializer =>
				Template::Plugin::Java->initializer($type),
			position=> $position++
		}
	} @{ $self->{columns} } ];
}

sub parseQuery {
	my ($self, $query) = @_;

	$query =~ /@{[QUERY]}/;

	$self->{query}  = $query;
	$self->{tables} = [ split /\s*,\s*/, $1 ];
	$self->{where}  = $2;
	$self->{order}  = $3;
}

1;

__END__