Ruby on Rails
für Java Entwickler
Ende der Schmerzen
-first, last = :mariano, :kamp
Ruby on Rails
für Java Entwickler
Ende der Schmerzen
-first, last = :mariano, :kamp
Ein Ritter in
Yukihiro
Yukihiro
Matsumoto
“Matz”
Suche nach dem
Edelstein
Smalltalk
Perl
+
~=
Ruby
Smalltalk
Perl
+
~=
Ruby
objektorientierung
dynamik
http://flickr.com/photos/letioux/110269051/
Smalltalk
Perl
+
~=
Ruby
objektorientierung
reguläre ausdrücke
string
manipulationen
dynamik
“then computers”
objektorientierung
•
kapselung
•
vererbung
“everything is an object”
ja, ja ...
nein, diesmal wirklich!
>> "ruby".class
=> String
IRB-In
>> "ruby".class
=> String
IRB-Out
>> "ruby".class
=> String
>> 10.class
=> Fixnum
>> 10.class
=> Fixnum
objektorientierung
Primitives sind keine
Objekte!
>> nil.class
=> NilClass
>> nil.class
=> NilClass
objektorientierung
Schlüsselwort “null”
>> true.class
=> TrueClass
>> true.class
=> TrueClass
objektorientierung
Schlüsselwort “true”
objekte senden und
empfangen nachrichten
>> 10.div(2)
=> 5
>> 10.div(2)
=> 5
objektorientierung
Nicht für Primitives
>> 10.send(:div, 2)
=> 5
>> 10./2
=> 5
>> 10/2
=> 5
>> 10/2
=> 5
objektorientierung
Nur für Primitives
>> 10.send(:/, 2)
=> 5
>> String.new "ruby"
=> "ruby"
>> String.new "ruby"
=> "ruby"
objektorientierung
Schlüsselwort “new”
>> class Dog
>> def name=(new_name)
>> @name=new_name
>> end
>> def name
>> @name
>> end
>> end
=> nil
objektorientierung
>> class Dog
>> def name=(new_name)
>> @name=new_name
>> end
>> def name
>> @name
>> end
>> end
=> nil
objektorientierung
>> class Dog
>> def name=(new_name)
>> @name=new_name
>> end
>> def name
>> @name
>> end
>> end
=> nil
objektorientierung
Define a “setter”
>> class Dog
>> def name=(new_name)
>> @name=new_name
>> end
>> def name
>> @name
>> end
>> end
=> nil
objektorientierung
Define a “getter”
>> class Dog
>> def name=(new_name)
>> @name=new_name
>> end
>> def name
>> @name
>> end
>> end
=> nil
objektorientierung
>> class Dog
>> def name=(new_name)
>> @name=new_name
>> end
>> def name
>> @name
>> end
>> end
=> nil
objektorientierung
>> d = Dog.new
=> #<Dog:0x1d4bac>
>> d.name="Fluffy"
=> "Fluffy"
>> d.name
=> "Fluffy"
“Dog d = new Dog()”
>> class Dog
>> def name=(new_name)
>> @name=new_name
>> end
>> def name
>> @name
>> end
>> end
=> nil
objektorientierung
>> d = Dog.new
=> #<Dog:0x1d4bac>
>> d.name="Fluffy"
=> "Fluffy"
>> d.name
=> "Fluffy"
“d.setName(“Fluffy”)”
>> class Dog
>> def name=(new_name)
>> @name=new_name
>> end
>> def name
>> @name
>> end
>> end
=> nil
objektorientierung
>> d = Dog.new
=> #<Dog:0x1d4bac>
>> d.name="Fluffy"
=> "Fluffy"
>> d.name
=> "Fluffy"
“d.getName()”
>> class Dog
>> def name=(new_name)
>> @name=new_name
>> end
>> def name
>> @name
>> end
>> end
=> nil
objektorientierung
>> d = Dog.new
=> #<Dog:0x1d4bac>
>> d.name="Fluffy"
=> "Fluffy"
>> d.name
=> "Fluffy"
“everything is open”
sogar die ruby libraries
>> class Fixnum
>> def even?
>> self % 2 == 0
>> end
>> end
=> nil
>> 10.even?
=> true
>> 9.even?
=> false
dynamik
>> class Fixnum
>> def even?
>> self % 2 == 0
>> end
>> end
=> nil
>> 10.even?
=> true
>> 9.even?
=> false
dynamik
Subclassing, wenn erlaubt
Helper Klassen -> StringUtil
und bereits lebende
objekte
class << ActiveRecord::Base.connection
alias :__old_execute__ :execute
def execute(statement, name)
puts "Executing: #{statement}"
__old_execute__(statement, name)
end
end
class << ActiveRecord::Base.connection
alias :__old_execute__ :execute
def execute(statement, name)
puts "Executing: #{statement}"
__old_execute__(statement, name)
end
end
class << ActiveRecord::Base.connection
alias :__old_execute__ :execute
def execute(statement, name)
puts "Executing: #{statement}"
__old_execute__(statement, name)
end
end
class << ActiveRecord::Base.connection
alias :__old_execute__ :execute
def execute(statement, name)
puts "Executing: #{statement}"
__old_execute__(statement, name)
end
end
class << ActiveRecord::Base.connection
alias :__old_execute__ :execute
def execute(statement, name)
puts "Executing: #{statement}"
__old_execute__(statement, name)
end
end
class << ActiveRecord::Base.connection
alias :__old_execute__ :execute
def execute(statement, name)
puts "Executing: #{statement}"
__old_execute__(statement, name)
end
end
class << ActiveRecord::Base.connection
alias :__old_execute__ :execute
def execute(statement, name)
puts "Executing: #{statement}"
__old_execute__(statement, name)
end
end
class << ActiveRecord::Base.connection
alias :__old_execute__ :execute
def execute(statement, name)
puts "Executing: #{statement}"
__old_execute__(statement, name)
end
end
dynamik
>> Player.find(:all).size
=> 4
class << ActiveRecord::Base.connection
alias :__old_execute__ :execute
def execute(statement, name)
puts "Executing: #{statement}"
__old_execute__(statement, name)
end
end
dynamik
>> Player.find(:all).size
Executing: SELECT * FROM players
=> 4
dynamik
strong/static typing
dynamik
strong/static typing
dynamik
weak typing
dynamik
>> def sum(a, b)
>> a+b
>> end
=> nil
dynamic typing
dynamik
>> def sum(a, b)
>> a+b
>> end
=> nil
>> sum(1,2)
=> 3
dynamic typing
dynamik
>> def sum(a, b)
>> a+b
>> end
=> nil
>> sum("A","Z")
=> "AZ"
dynamic typing
dynamik
>> def sum(a, b)
>> a+b
>> end
=> nil
>> sum("A",2)
dynamic typing
dynamik
>> def sum(a, b)
>> a+b
>> end
=> nil
>> sum("A",2)
dynamic typing
TypeError: can't convert Fixnum into
String
:method_missing
dynamik
>> class Receiver
>> def method_missing(action, *args)
>> puts "My method #{action} was called,"
>> puts "with parameters: #{args.inspect}"
>> end
dynamik
>> class Receiver
>> def method_missing(action, *args)
>> puts "My method #{action} was called,"
>> puts "with parameters: #{args.inspect}"
>> end
>> end
>> r = Receiver.new
dynamik
>> class Receiver
>> def method_missing(action, *args)
>> puts "My method #{action} was called,"
>> puts "with parameters: #{args.inspect}"
>> end
>> end
>> r = Receiver.new
=> #<Receiver:0x33d43c>
>> r.test
My method test was called,
with parameters: []
dynamik
>> class Receiver
>> def method_missing(action, *args)
>> puts "My method #{action} was called,"
>> puts "with parameters: #{args.inspect}"
>> end
>> end
>> r.add 1,"b", 2
My method add was called,
with parameters: [1, "b", 2]
=> nil
>> r = Receiver.new
=> #<Receiver:0x33d43c>
>> r.test
My method test was called,
with parameters: []
dynamik
>> class Receiver
>> def method_missing(action, *args)
>> puts "My method #{action} was called,"
>> puts "with parameters: #{args.inspect}"
>> end
>> end
•
Proxy?
•
Mock?
•
State Machine?
•
CSV Reader?
•
AOP?
>> r.add 1,"b", 2
My method add was called,
with parameters: [1, "b", 2]
=> nil
>> r = Receiver.new
=> #<Receiver:0x33d43c>
>> r.test
My method test was called,
with parameters: []
meta programmierung
dynamik
>> class Dog
>> attr_accessor :name, :age
>> def age=(new_age)
>> @age=new_age*7
>> end
>> end
=> nil
dynamik
>> class Dog
>> attr_accessor :name, :age
>> def age=(new_age)
>> @age=new_age*7
>> end
>> end
=> nil
Definiert “on the fly”:
dynamik
>> class Dog
>> attr_accessor :name, :age
>> def age=(new_age)
>> @age=new_age*7
>> end
>> end
=> nil
dynamik
>> class Dog
>> attr_accessor :name, :age
>> def age=(new_age)
>> @age=new_age*7
>> end
>> end
=> nil
>> d = Dog.new
=> #<Dog:0x318498>
dynamik
>> class Dog
>> attr_accessor :name, :age
>> def age=(new_age)
>> @age=new_age*7
>> end
>> end
=> nil
>> d.name = "Fluffy"
=> "Fluffy"
>> d.age = 2
=> 2
dynamik
>> class Dog
>> attr_accessor :name, :age
>> def age=(new_age)
>> @age=new_age*7
>> end
>> end
=> nil
>> d.age
=> 14
und vieles mehr ...
blocks/closures
closures
>> def log(name)
>> puts "Now executing #{name}."
>> result = yield
>> puts "Done with #{name}."
>> result
closures
>> def log(name)
>> puts "Now executing #{name}."
>> result = yield
>> puts "Done with #{name}."
>> result
closures
>> def log(name)
>> puts "Now executing #{name}."
>> result = yield
>> puts "Done with #{name}."
>> result
>> end
>> log("Addition"){1+1}
Now executing Addition.
Done with Addition.
closures
>> def log(name)
>> puts "Now executing #{name}."
>> result = yield
>> puts "Done with #{name}."
>> result
>> end
>> log("Addition"){1+1}
Now executing Addition.
Done with Addition.
closures
>> def log(name)
>> puts "Now executing #{name}."
>> result = yield
>> puts "Done with #{name}."
>> result
>> end
>> log("Addition"){1+1}
Now executing Addition.
Done with Addition.
closures
>> def log(name)
>> puts "Now executing #{name}."
>> result = yield
>> puts "Done with #{name}."
>> result
>> end
>> log("Addition"){1+1}
Now executing Addition.
Done with Addition.
closures
>> def log(name)
>> puts "Now executing #{name}."
>> result = yield
>> puts "Done with #{name}."
>> result
>> end
>> log("Addition"){1+1}
Now executing Addition.
Done with Addition.
closures
>> def log(name)
>> puts "Now executing #{name}."
>> result = yield
>> puts "Done with #{name}."
>> result
>> end
>> log("Addition"){1+1}
Now executing Addition.
Done with Addition.
=> 2
>> log("Output"){puts "Chunky Bacon"}
Now executing Output.
Chunky Bacon
Done with Output.
=> nil
closures
>> def log(name)
>> puts "Now executing #{name}."
>> result = yield
>> puts "Done with #{name}."
>> result
>> end
>> ["chunky","bacon"].each {|element| puts element}
chunky
bacon
>> log("Addition"){1+1}
Now executing Addition.
Done with Addition.
=> 2
>> log("Output"){puts "Chunky Bacon"}
Now executing Output.
Chunky Bacon
Done with Output.
=> nil
Pragmatic Programmers
"Learn at least one new
[programming] language every year.”
“Different languages solve the same problems
in different ways. By learning several different
approaches, you can help broaden your
Google Trends
Programming Language Trends
I always knew one day Smalltalk would replace Java.
I just didn't know it would be called Ruby.
I like the ruby language for its rich yet uncluttered syntax
and the well designed frameworks that come with it.
David Heinemeier Hanson
extrahiert aus
conventions
configuration
over
conventions
configuration
zuckerbrot
&
DRY
(don’t repeat yourself)
conventions
conventions
Klassen- und Tabellen
Klassennamen sind Singular.
Tabellennamen sind Plural.
conventions
Klassen- und Tabellen
Klassennamen sind Singular.
Tabellennamen sind Plural.
Klassennamen sind CamelCase.
Tabellennamen sind Kleinbuchstaben, verbunden
durch “_”.
conventions
Klassen- und Tabellen
Klassennamen sind Singular.
Tabellennamen sind Plural.
Klassennamen sind CamelCase.
Tabellennamen sind Kleinbuchstaben, verbunden
durch “_”.
Bsp.:
TurnState => turn_states
OrderReceipt => order_receipts
Person => people
conventions
Spalten- und Attribute
Spaltenname == Attributname
Jede Tabelle hat eine “id”-Spalte
conventions
Relationen
Spaltennamen des foreign keys entspricht
dem Namen der Klasse plus “_id”.
conventions
Relationen
Spaltennamen des foreign keys entspricht
dem Namen der Klasse plus “_id”.
*
id
name
author
Book
id
name
Customer
conventions
Relationen
Spaltennamen des foreign keys entspricht
dem Namen der Klasse plus “_id”.
*
id
name
author
Book
id
name
Customer
Der Tabelle books wird die Spalte
customer_id angefügt.
conventions
und?
JEE vs Rails
JEE vs Rails
*
id
name
author
Book
id
name
Customer
http://www.laliluna.de/ejb-3-struts-tutorial-jboss.html
Java Beispielcode von
Sebastian Hennebrueder
JEE vs Rails
/**
*
* @author Sebastian Hennebrueder
* created Mar 15, 2006
* copyright 2006 by http://www.laliluna.de
*/
package de.laliluna.library;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
/**
* @author hennebrueder
*
*/
@Entity
@Table(name="customer")
@SequenceGenerator(name="customer_sequence", sequenceName="customer_id_seq")
public class Customer {
private Integer id;
private String name;
private List books =new ArrayList();
public Customer(){
super();
}
public Customer(Integer id, String name){
super();
this.id=id;
this.name=name;
}
@OneToMany(cascade=CascadeType.ALL, fetch = FetchType.EAGER, mappedBy="customer",
targetEntity=Book.class)
public List getBooks() {
return books;
}
public void setBooks(List books) {
this.books = books;
}
@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="customer_sequence")
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Customer: " + getId() + " Name " + getName();
}
}
id
name
Model -> Table
@Entity
@Table(name="customer")
[..]
public class Customer {
[..]
id
name
Customer
Rails:
class Customer < ActiveRecord::Base
end
Convention:
Class Name = “Customer” => Table Name = “Customers”
sonst: set_table_name(“customer”)
[..]
@SequenceGenerator(name="customer_sequence",
sequenceName="customer_id_seq")
public class Customer {
[..]
@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE,
generator="customer_sequence")
public Integer getId() {
id
name
Customer
Rails: Entfällt.
Convention:
Primary Key => “id”, sequence
@OneToMany(cascade=CascadeType.ALL,
fetch = FetchType.EAGER,
mappedBy="customer",
targetEntity=Book.class)
public List getBooks() {
return books;
}
public void setBooks(List books) {
this.books = books;
}
id
name
Customer
Rails:
has_many :books
Convention:
targetEntity = Singular des Entitätennames, sonst “, :class_name = “Book”
Getter/Setter dynamisch
private Integer id;
private String name;
private List books =new ArrayList();
public Customer(){ super(); }
public Customer(Integer id, String name){
super(); this.id=id; this.name=name;
}
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id;}
public String getName() { return name;}
public void setName(String name) { this.name = name;}
id
name
Customer
Rails:
create_table :customers do |t|
# keine “id” spalte
t.column :name, :string
end
Attributes
Convention:
Name der Spalte =
Name des Attributs
@Override
public String toString() {
return "Customer: "+getId()+" Name "+getName();
}
id
name
Customer
Rails:
Entfällt, Default:
#<Customer:0x2417c14 @attributes={"name"=>"Donald Duck", "id"=>"1"}>
Alternativ:
def to_s
“Customer: #{id} Name #{name}”
end
=> "Customer: 1 Name Donald Duck"
•
save/persist
•
merge
•
findAll
•
findById
•
findByCustomer (BookDAO...)
id
name
Customer
Rails:
Alle Methoden bereits im Framework generisch enthalten
“findByX” dynamisch -> findByName, findByAuthor, findByCustomer,
findByTitleAndAuthor ...
CustomerDAO,
CustomerDAOImpl
id
name
author
Book
class Initial < ActiveRecord::Migration
def self.up
create_table :books do |t|
t.column :name, :string
t.column :author, :string
t.column :customer_id, :integer
end
create_table :customers do |t|
t.column :name, :string
end
end
def self.down
drop_table :books
drop_table :customers
end
end
Migration
*
id
name
author
Book
id
name
Customer
class Initial < ActiveRecord::Migration
def self.up
create_table :books do |t|
t.column :name, :string
t.column :author, :string
t.column :customer_id, :integer
end
create_table :customers do |t|
t.column :name, :string
end
end
def self.down
drop_table :books
drop_table :customers
end
end
Migration
*
id
name
author
Book
id
name
Customer
class Initial < ActiveRecord::Migration
def self.up
create_table :books do |t|
t.column :name, :string
t.column :author, :string
t.column :customer_id, :integer
end
create_table :customers do |t|
t.column :name, :string
end
end
def self.down
drop_table :books
drop_table :customers
end
end
Migration
*
id
name
author
Book
id
name
Customer
class Initial < ActiveRecord::Migration
def self.up
create_table :books do |t|
t.column :name, :string
t.column :author, :string
t.column :customer_id, :integer
end
create_table :customers do |t|
t.column :name, :string
end
end
def self.down
drop_table :books
drop_table :customers
end
end
Migration
*
id
name
author
Book
id
name
Customer
class Initial < ActiveRecord::Migration
def self.up
create_table :books do |t|
t.column :name, :string
t.column :author, :string
t.column :customer_id, :integer
end
create_table :customers do |t|
t.column :name, :string
end
end
def self.down
drop_table :books
drop_table :customers
end
end
Migration
*
id
name
author
Book
id
name
Customer
class Initial < ActiveRecord::Migration
def self.up
create_table :books do |t|
t.column :name, :string
t.column :author, :string
t.column :customer_id, :integer
end
create_table :customers do |t|
t.column :name, :string
end
end
def self.down
drop_table :books
drop_table :customers
end
end
Migration
*
id
name
author
Book
id
name
Customer
class Initial < ActiveRecord::Migration
def self.up
create_table :books do |t|
t.column :name, :string
t.column :author, :string
t.column :customer_id, :integer
end
create_table :customers do |t|
t.column :name, :string
end
end
def self.down
drop_table :books
drop_table :customers
end
end
Migration
*
id
name
author
Book
id
name
Customer
class Customer < ActiveRecord::Base
has_many :books
end
class Book < ActiveRecord::Base
belongs_to :customer
end
Der Code
*
id
name
author
Book
id
name
Customer
Perfection in Design
This Wikipedia and Wikimedia Commons image is from the user Chris 73 and is freely available at http://commons.wikimedia.org/
wiki/Image:Antoine_de_Saint-Exup%C3%A9ry_Lyon.JPG under the creative commons cc-by-sa 2.5 license.
Perfection in Design
Antoine de Saint-Exupéry
This Wikipedia and Wikimedia Commons image is from the user Chris 73 and is freely available at http://commons.wikimedia.org/
wiki/Image:Antoine_de_Saint-Exup%C3%A9ry_Lyon.JPG under the creative commons cc-by-sa 2.5 license.
Perfection in Design
Antoine de Saint-Exupéry
This Wikipedia and Wikimedia Commons image is from the user Chris 73 and is freely available at http://commons.wikimedia.org/
wiki/Image:Antoine_de_Saint-Exup%C3%A9ry_Lyon.JPG under the creative commons cc-by-sa 2.5 license.
“You know you’ve achieved perfection
in design, not when you have nothing
more to add, but when you have
JEE vs Rails
Migration
Customer
Book
JEE vs Rails
/** * * @author Sebastian Hennebrueder
* created Mar 15, 2006 * copyright 2006 by http://www.laliluna.de */ package de.laliluna.library; import java.util.ArrayList; import java.util.List; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.SequenceGenerator;import javax.persistence.Table; /** * @author hennebrueder * */ @Entity @Table(name="customer") @SequenceGenerator(name="customer_sequence", sequenceName="customer_id_seq")
public class Customer { private Integer id; private String name; private List books =new ArrayList();
public Customer(){ super(); } public Customer(Integer id, String name){ super();
this.id=id; this.name=name;
}
@OneToMany(cascade=CascadeType.ALL, fetch = FetchType.EAGER, mappedBy="customer", targetEntity=Book.class) public List getBooks() { return books; } public void setBooks(List books) {
this.books = books; } @Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="customer_sequence") public Integer getId() {
return id; } public void setId(Integer id) {
this.id = id; } public String getName() {
return name; } public void setName(String name) {
this.name = name; } @Override public String toString() { return "Customer: " + getId() + " Name " + getName();
} }/** * * @author Sebastian Hennebrueder
* created Mar 15, 2006 * copyright 2006 by http://www.laliluna.de */ package de.laliluna.library; import java.util.List; import javax.ejb.Stateless; import javax.persistence.Entity; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; /** * @author hennebrueder * */ @Stateless public class CustomerDaoImp implements CustomerDao {
@PersistenceContext private EntityManager em; public static final String RemoteJNDIName = CustomerDaoImp.class.getSimpleName() + "/remote"; public static final String LocalJNDIName = CustomerDaoImp.class.getSimpleName() + "/local";
/* (non-Javadoc) * @see de.laliluna.example.ejb.CustomerDao#save(de.laliluna.example.ejb.Book) */
public void save(Customer customer) { em.persist(customer); } /* (non-Javadoc) * @see de.laliluna.example.ejb.CustomerDao#reattach(de.laliluna.example.ejb.Book) */
public void merge(Customer customer) { em.merge(customer); } /* (non-Javadoc) * @see de.laliluna.example.ejb.CustomerDao#findAll() */
public List findAll() { return em.createQuery("from Customer").getResultList(); }
/* (non-Javadoc) * @see de.laliluna.example.ejb.CustomerDao#findById(java.lang.Integer) */
public Customer findById(Integer id) { Customer customer = em.find(Customer.class, id);
return customer; }
}
/** * * @author Sebastian Hennebrueder * created Feb 27, 2006 * copyright 2006 by http://www.laliluna.de */ package de.laliluna.library; import java.util.ArrayList;import java.util.List; import javax.ejb.Stateful; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import org.apache.log4j.Logger; import sun.security.krb5.internal.crypto.bo; /** * @author hennebrueder * */ @Stateless public class BookDaoImp implements BookDao {
@PersistenceContext private EntityManager em; private Logger log = Logger.getLogger(this.getClass());
/* * (non-Javadoc)
* * @see de.laliluna.example.ejb.BookDao#save(de.laliluna.example.ejb.Book)
*/ public void save(Book book) { log.debug("Persist book: " + book); em.persist(book); } public void merge(Book book) {
em.merge(book); } public List findAll() { log.debug("find All books"); return em.createQuery("from Book").getResultList(); } public static final String RemoteJNDIName = BookDaoImp.class
.getSimpleName() + "/remote"; public static final String LocalJNDIName = BookDaoImp.class.getSimpleName()
+ "/local"; public Book findById(Integer id) { return em.find(Book.class, id);
} public List findByCustomer(Customer customer) { log.debug("find by customer"); return em.createQuery("from Book b where b.customer = :customer") .setParameter("customer", customer).getResultList();
}} package de.laliluna.library; import java.util.List; import javax.ejb.Local; import org.jboss.annotation.ejb.LocalBinding; @Local public interface BookDao { public void save(Book book); public void merge(Book book); public List findAll(); public List findByCustomer(Customer customer);
public Book findById(Integer id); }/** * * @author Sebastian Hennebrueder
* created Mar 15, 2006 * copyright 2006 by http://www.laliluna.de */ package de.laliluna.library; import java.util.List; import javax.ejb.Local; /** * @author hennebrueder * */ @Local public interface CustomerDao { public void save(Customer customer); public void merge(Customer customer); public List findAll(); public Customer findById(Integer id);} /** * * @author Sebastian Hennebrueder * created Feb 27, 2006 * copyright 2006 by http://www.laliluna.de */ package de.laliluna.library; import java.io.Serializable; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType;import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.SequenceGenerator;import javax.persistence.Table; import org.hibernate.annotations.GenericGenerator; /** * @author hennebrueder * */ @Entity @Table(name = "book") @SequenceGenerator(name = "book_sequence", sequenceName = "book_id_seq")
public class Book implements Serializable { private Integer id; private String title; private String author; private Customer customer; public Book() {
super(); } public Book(Integer id, String title, String author) {
super(); this.id = id; this.title = title; this.author = author; } @ManyToOne @JoinColumn(name = "customer_id")
public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer;
} public String getAuthor() { return author;
} public void setAuthor(String author) { this.author = author;
} @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "book_sequence")
public Integer getId() { return id; } public void setId(Integer id) { this.id = id;
} public String getTitle() { return title;
} public void setTitle(String title) { this.title = title;
} @Override public String toString() { return "Book: " + getId() + " Title " + getTitle() + " Author "
+ getAuthor(); }
}
Migration
Customer
Book
JEE vs Rails
/** * * @author Sebastian Hennebrueder
* created Mar 15, 2006 * copyright 2006 by http://www.laliluna.de */ package de.laliluna.library; import java.util.ArrayList; import java.util.List; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.SequenceGenerator;import javax.persistence.Table; /** * @author hennebrueder * */ @Entity @Table(name="customer") @SequenceGenerator(name="customer_sequence", sequenceName="customer_id_seq")
public class Customer { private Integer id; private String name; private List books =new ArrayList();
public Customer(){ super(); } public Customer(Integer id, String name){ super();
this.id=id; this.name=name;
}
@OneToMany(cascade=CascadeType.ALL, fetch = FetchType.EAGER, mappedBy="customer", targetEntity=Book.class) public List getBooks() { return books; } public void setBooks(List books) {
this.books = books; } @Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="customer_sequence") public Integer getId() {
return id; } public void setId(Integer id) {
this.id = id; } public String getName() {
return name; } public void setName(String name) {
this.name = name; } @Override public String toString() { return "Customer: " + getId() + " Name " + getName();
} }/** * * @author Sebastian Hennebrueder
* created Mar 15, 2006 * copyright 2006 by http://www.laliluna.de */ package de.laliluna.library; import java.util.List; import javax.ejb.Stateless; import javax.persistence.Entity; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; /** * @author hennebrueder * */ @Stateless public class CustomerDaoImp implements CustomerDao {
@PersistenceContext private EntityManager em; public static final String RemoteJNDIName = CustomerDaoImp.class.getSimpleName() + "/remote"; public static final String LocalJNDIName = CustomerDaoImp.class.getSimpleName() + "/local";
/* (non-Javadoc) * @see de.laliluna.example.ejb.CustomerDao#save(de.laliluna.example.ejb.Book) */
public void save(Customer customer) { em.persist(customer); } /* (non-Javadoc) * @see de.laliluna.example.ejb.CustomerDao#reattach(de.laliluna.example.ejb.Book) */
public void merge(Customer customer) { em.merge(customer); } /* (non-Javadoc) * @see de.laliluna.example.ejb.CustomerDao#findAll() */
public List findAll() { return em.createQuery("from Customer").getResultList(); }
/* (non-Javadoc) * @see de.laliluna.example.ejb.CustomerDao#findById(java.lang.Integer) */
public Customer findById(Integer id) { Customer customer = em.find(Customer.class, id);
return customer; }
}
/** * * @author Sebastian Hennebrueder * created Feb 27, 2006 * copyright 2006 by http://www.laliluna.de */ package de.laliluna.library; import java.util.ArrayList;import java.util.List; import javax.ejb.Stateful; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import org.apache.log4j.Logger; import sun.security.krb5.internal.crypto.bo; /** * @author hennebrueder * */ @Stateless public class BookDaoImp implements BookDao {
@PersistenceContext private EntityManager em; private Logger log = Logger.getLogger(this.getClass());
/* * (non-Javadoc)
* * @see de.laliluna.example.ejb.BookDao#save(de.laliluna.example.ejb.Book)
*/ public void save(Book book) { log.debug("Persist book: " + book); em.persist(book); } public void merge(Book book) {
em.merge(book); } public List findAll() { log.debug("find All books"); return em.createQuery("from Book").getResultList(); } public static final String RemoteJNDIName = BookDaoImp.class
.getSimpleName() + "/remote"; public static final String LocalJNDIName = BookDaoImp.class.getSimpleName()
+ "/local"; public Book findById(Integer id) { return em.find(Book.class, id);
} public List findByCustomer(Customer customer) { log.debug("find by customer"); return em.createQuery("from Book b where b.customer = :customer") .setParameter("customer", customer).getResultList();
}} package de.laliluna.library; import java.util.List; import javax.ejb.Local; import org.jboss.annotation.ejb.LocalBinding; @Local public interface BookDao { public void save(Book book); public void merge(Book book); public List findAll(); public List findByCustomer(Customer customer);
public Book findById(Integer id); }/** * * @author Sebastian Hennebrueder
* created Mar 15, 2006 * copyright 2006 by http://www.laliluna.de */ package de.laliluna.library; import java.util.List; import javax.ejb.Local; /** * @author hennebrueder * */ @Local public interface CustomerDao { public void save(Customer customer); public void merge(Customer customer); public List findAll(); public Customer findById(Integer id);} /** * * @author Sebastian Hennebrueder * created Feb 27, 2006 * copyright 2006 by http://www.laliluna.de */ package de.laliluna.library; import java.io.Serializable; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType;import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.SequenceGenerator;import javax.persistence.Table; import org.hibernate.annotations.GenericGenerator; /** * @author hennebrueder * */ @Entity @Table(name = "book") @SequenceGenerator(name = "book_sequence", sequenceName = "book_id_seq")
public class Book implements Serializable { private Integer id; private String title; private String author; private Customer customer; public Book() {
super(); } public Book(Integer id, String title, String author) {
super(); this.id = id; this.title = title; this.author = author; } @ManyToOne @JoinColumn(name = "customer_id")
public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer;
} public String getAuthor() { return author;
} public void setAuthor(String author) { this.author = author;
} @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "book_sequence")
public Integer getId() { return id; } public void setId(Integer id) { this.id = id;
} public String getTitle() { return title;
} public void setTitle(String title) { this.title = title;
} @Override public String toString() { return "Book: " + getId() + " Title " + getTitle() + " Author "
+ getAuthor(); }
}
class Customer < ActiveRecord::Base has_many :books end class Book < ActiveRecord::Base
belongs_to :customerend class Initial < ActiveRecord::Migration def self.up
create_table :books do |t| t.column :name, :string t.column :author, :string t.column :customer_id, :integer end create_table :customers do |t|
t.column :name, :string end end def self.down drop_table :books drop_table :customers end end
Migration
Customer
Book
JEE vs Rails
/** * * @author Sebastian Hennebrueder
* created Mar 15, 2006 * copyright 2006 by http://www.laliluna.de */ package de.laliluna.library; import java.util.ArrayList; import java.util.List; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.SequenceGenerator;import javax.persistence.Table; /** * @author hennebrueder * */ @Entity @Table(name="customer") @SequenceGenerator(name="customer_sequence", sequenceName="customer_id_seq")
public class Customer { private Integer id; private String name; private List books =new ArrayList();
public Customer(){ super(); } public Customer(Integer id, String name){ super();
this.id=id; this.name=name;
}
@OneToMany(cascade=CascadeType.ALL, fetch = FetchType.EAGER, mappedBy="customer", targetEntity=Book.class) public List getBooks() { return books; } public void setBooks(List books) {
this.books = books; } @Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="customer_sequence") public Integer getId() {
return id; } public void setId(Integer id) {
this.id = id; } public String getName() {
return name; } public void setName(String name) {
this.name = name; } @Override public String toString() { return "Customer: " + getId() + " Name " + getName();
} }/** * * @author Sebastian Hennebrueder
* created Mar 15, 2006 * copyright 2006 by http://www.laliluna.de */ package de.laliluna.library; import java.util.List; import javax.ejb.Stateless; import javax.persistence.Entity; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; /** * @author hennebrueder * */ @Stateless public class CustomerDaoImp implements CustomerDao {
@PersistenceContext private EntityManager em; public static final String RemoteJNDIName = CustomerDaoImp.class.getSimpleName() + "/remote"; public static final String LocalJNDIName = CustomerDaoImp.class.getSimpleName() + "/local";
/* (non-Javadoc) * @see de.laliluna.example.ejb.CustomerDao#save(de.laliluna.example.ejb.Book) */
public void save(Customer customer) { em.persist(customer); } /* (non-Javadoc) * @see de.laliluna.example.ejb.CustomerDao#reattach(de.laliluna.example.ejb.Book) */
public void merge(Customer customer) { em.merge(customer); } /* (non-Javadoc) * @see de.laliluna.example.ejb.CustomerDao#findAll() */
public List findAll() { return em.createQuery("from Customer").getResultList(); }
/* (non-Javadoc) * @see de.laliluna.example.ejb.CustomerDao#findById(java.lang.Integer) */
public Customer findById(Integer id) { Customer customer = em.find(Customer.class, id);
return customer; }
}
/** * * @author Sebastian Hennebrueder * created Feb 27, 2006 * copyright 2006 by http://www.laliluna.de */ package de.laliluna.library; import java.util.ArrayList;import java.util.List; import javax.ejb.Stateful; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import org.apache.log4j.Logger; import sun.security.krb5.internal.crypto.bo; /** * @author hennebrueder * */ @Stateless public class BookDaoImp implements BookDao {
@PersistenceContext private EntityManager em; private Logger log = Logger.getLogger(this.getClass());
/* * (non-Javadoc)
* * @see de.laliluna.example.ejb.BookDao#save(de.laliluna.example.ejb.Book)
*/ public void save(Book book) { log.debug("Persist book: " + book); em.persist(book); } public void merge(Book book) {
em.merge(book); } public List findAll() { log.debug("find All books"); return em.createQuery("from Book").getResultList(); } public static final String RemoteJNDIName = BookDaoImp.class
.getSimpleName() + "/remote"; public static final String LocalJNDIName = BookDaoImp.class.getSimpleName()
+ "/local"; public Book findById(Integer id) { return em.find(Book.class, id);
} public List findByCustomer(Customer customer) { log.debug("find by customer"); return em.createQuery("from Book b where b.customer = :customer") .setParameter("customer", customer).getResultList();
}} package de.laliluna.library; import java.util.List; import javax.ejb.Local; import org.jboss.annotation.ejb.LocalBinding; @Local public interface BookDao { public void save(Book book); public void merge(Book book); public List findAll(); public List findByCustomer(Customer customer);
public Book findById(Integer id); }/** * * @author Sebastian Hennebrueder
* created Mar 15, 2006 * copyright 2006 by http://www.laliluna.de */ package de.laliluna.library; import java.util.List; import javax.ejb.Local; /** * @author hennebrueder * */ @Local public interface CustomerDao { public void save(Customer customer); public void merge(Customer customer); public List findAll(); public Customer findById(Integer id);} /** * * @author Sebastian Hennebrueder * created Feb 27, 2006 * copyright 2006 by http://www.laliluna.de */ package de.laliluna.library; import java.io.Serializable; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType;import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.SequenceGenerator;import javax.persistence.Table; import org.hibernate.annotations.GenericGenerator; /** * @author hennebrueder * */ @Entity @Table(name = "book") @SequenceGenerator(name = "book_sequence", sequenceName = "book_id_seq")
public class Book implements Serializable { private Integer id; private String title; private String author; private Customer customer; public Book() {
super(); } public Book(Integer id, String title, String author) {
super(); this.id = id; this.title = title; this.author = author; } @ManyToOne @JoinColumn(name = "customer_id")
public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer;
} public String getAuthor() { return author;
} public void setAuthor(String author) { this.author = author;
} @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "book_sequence")
public Integer getId() { return id; } public void setId(Integer id) { this.id = id;
} public String getTitle() { return title;
} public void setTitle(String title) { this.title = title;
} @Override public String toString() { return "Book: " + getId() + " Title " + getTitle() + " Author "
+ getAuthor(); }
}
class Customer < ActiveRecord::Base has_many :books end class Book < ActiveRecord::Base
belongs_to :customerend class Initial < ActiveRecord::Migration def self.up
create_table :books do |t| t.column :name, :string t.column :author, :string t.column :customer_id, :integer end create_table :customers do |t|
t.column :name, :string end end def self.down drop_table :books drop_table :customers end end
Migration
Customer
Book
/ Noise
Signal
public void testBook() {
Book book = new Book(null,"My first bean book", "Sebastian");
em.persist(book);
Book book2 = new Book(null,"another book", "Paul");
em.persist(book2);
Book book3 = new Book(null,"EJB 3 developer guide","Sebastian");
em.persist(book3);
[..]
/ Noise
def test_book
book = Book.create(:name => "My first bean book",
:author => "Sebastian")
book2 = Book.create(:name => "another book",
:author => "Paul")
book3 = Book.create(:name => "EJB 3 dev guide",
:author => "Sebastian")
[..]
public void testBook() {
Book book = new Book(null,"My first bean book", "Sebastian");
em.persist(book);
Book book2 = new Book(null,"another book", "Paul");
em.persist(book2);
Book book3 = new Book(null,"EJB 3 developer guide","Sebastian");
em.persist(book3);
[..]
/ Noise
def test_book
book = Book.create(:name => "My first bean book",
:author => "Sebastian")
book2 = Book.create(:name => "another book",
:author => "Paul")
book3 = Book.create(:name => "EJB 3 dev guide",
:author => "Sebastian")
[..]
[..]
System.out.println("list some books");
List someBooks =
em.createQuery("from Book b where b.author=:name")
.setParameter("name", "Sebastian").getResultList();
for (Iterator iter = someBooks.iterator(); iter.hasNext();){
Book element = (Book) iter.next();
System.out.println(element);
}
System.out.println("list all books");
List allBooks = em.createQuery("from Book").getResultList();
for (Iterator iter = allBooks.iterator(); iter.hasNext();){
Book element = (Book) iter.next();
System.out.println(element);
}
[..]
puts "list some books"
Book.find_all_by_author("Sebastian").each {|book| puts book.inspect}
puts "list all books"
Book.find(:all).each {|book| puts book.inspect}
/ Noise
Signal
[..]
System.out.println("list some books");
List someBooks =
em.createQuery("from Book b where b.author=:name")
.setParameter("name", "Sebastian").getResultList();
for (Iterator iter = someBooks.iterator(); iter.hasNext();){
Book element = (Book) iter.next();
System.out.println(element);
}
System.out.println("list all books");
List allBooks = em.createQuery("from Book").getResultList();
for (Iterator iter = allBooks.iterator(); iter.hasNext();){
Book element = (Book) iter.next();
System.out.println(element);
}
[..]
puts "list some books"
Book.find_all_by_author("Sebastian").each {|book| puts book.inspect}
puts "list all books"
Book.find(:all).each {|book| puts book.inspect}
/ Noise
Signal
[..]
System.out.println("list some books");
List someBooks =
em.createQuery("from Book b where b.author=:name")
.setParameter("name", "Sebastian").getResultList();
for (Iterator iter = someBooks.iterator(); iter.hasNext();){
Book element = (Book) iter.next();
System.out.println(element);
}
System.out.println("list all books");
List allBooks = em.createQuery("from Book").getResultList();
for (Iterator iter = allBooks.iterator(); iter.hasNext();){
Book element = (Book) iter.next();
System.out.println(element);
}
[..]
puts "list some books"
Book.find_all_by_author("Sebastian").each {|book| puts book.inspect}
puts "list all books"
Book.find(:all).each {|book| puts book.inspect}
/ Noise
Signal
JDK 1.5 to the rescue
(Generics, foreach)
public void testRelation() {
[..] Drei Books und zwei Customers angelegt
customer1.getBooks().add(book);
book.setCustomer(customer1);
customer2.getBooks().add(book2);
customer2.getBooks().add(book3);
[..] Drei Books und zwei Customers angelegt
customer1.books << book
customer2.books << [book2, book3]
/ Noise
Signal
public void testRelation() {
[..] Drei Books und zwei Customers angelegt
customer1.getBooks().add(book);
book.setCustomer(customer1);
customer2.getBooks().add(book2);
customer2.getBooks().add(book3);
[..] Drei Books und zwei Customers angelegt
customer1.books << book
customer2.books << [book2, book3]
/ Noise
Signal
class Book < ActiveRecord::Base
belongs_to :customer
has_many :chapters
has_many :sections, :through => :chapters
validates_presence_of :author
validates_uniqueness_of :name
validates_length_of :name, :within => 6..20,
:too_long => "pick shorter name",
:too_short => "pick longer name"
end
Batteries included
Rails bringt einiges mit:
•
Struktur
•
Build
•
Testunterstützung
•
Entwicklungs-, Test- und Produktionsumgebung
•
Kein eigenes Denken notwendig
•
Skripte und Automatismen verwenden gleiche