One of the truly awesome things about PL/SQL is that a routine becomes a single logical unit of work. So as long as you allow the exceptions to propagate up, the transactional integrity is taken care of for you.
eg
SQL> create table T ( id int, x varchar2(40));
Table created.
SQL>
SQL> create or replace
2 procedure P is
3 x int;
4 begin
5 insert into T values (1,'first row');
6
7 insert into T values (2,'second row');
8
9 --
10 -- this will make the procedure crash
11 --
12 x := 1/0;
13
14 insert into T values (3,'third row');
15
16 insert into T values (4,'fourth row');
17 end;
18 /
Procedure created.
SQL>
SQL> insert into T values ( 0, 'an uncommitted row before we ran P');
1 row created.
SQL>
SQL> select * from T;
ID X
---------- ----------------------------------------
0 an uncommitted row before we ran P
SQL>
SQL>
SQL> exec P
BEGIN P; END;
*
ERROR at line 1:
ORA-01476: divisor is equal to zero
ORA-06512: at "MCDONAC.P", line 11
ORA-06512: at line 1
SQL>
SQL> select * from T;
ID X
---------- ----------------------------------------
0 an uncommitted row before we ran P